1/*
2 * ProGuard -- shrinking, optimization, obfuscation, and preverification
3 *             of Java bytecode.
4 *
5 * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu)
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21package proguard.gui;
22
23import proguard.*;
24import proguard.classfile.ClassConstants;
25import proguard.classfile.util.ClassUtil;
26
27import javax.swing.*;
28import javax.swing.border.*;
29import java.awt.*;
30import java.awt.event.*;
31import java.util.List;
32
33/**
34 * This <code>JDialog</code> allows the user to enter a String.
35 *
36 * @author Eric Lafortune
37 */
38final class ClassSpecificationDialog extends JDialog
39{
40    /**
41     * Return value if the dialog is canceled (with the Cancel button or by
42     * closing the dialog window).
43     */
44    public static final int CANCEL_OPTION = 1;
45
46    /**
47     * Return value if the dialog is approved (with the Ok button).
48     */
49    public static final int APPROVE_OPTION = 0;
50
51
52    private final JTextArea commentsTextArea = new JTextArea(4, 20);
53
54    private final JRadioButton keepClassesAndMembersRadioButton  = new JRadioButton(msg("keep"));
55    private final JRadioButton keepClassMembersRadioButton       = new JRadioButton(msg("keepClassMembers"));
56    private final JRadioButton keepClassesWithMembersRadioButton = new JRadioButton(msg("keepClassesWithMembers"));
57
58    private final JCheckBox allowShrinkingCheckBox    = new JCheckBox(msg("allowShrinking"));
59    private final JCheckBox allowOptimizationCheckBox = new JCheckBox(msg("allowOptimization"));
60    private final JCheckBox allowObfuscationCheckBox  = new JCheckBox(msg("allowObfuscation"));
61
62
63    private final JRadioButton[] publicRadioButtons;
64    private final JRadioButton[] finalRadioButtons;
65    private final JRadioButton[] abstractRadioButtons;
66    private final JRadioButton[] enumRadioButtons;
67    private final JRadioButton[] annotationRadioButtons;
68    private final JRadioButton[] interfaceRadioButtons;
69
70    private final JTextField annotationTypeTextField        = new JTextField(20);
71    private final JTextField classNameTextField             = new JTextField(20);
72    private final JTextField extendsAnnotationTypeTextField = new JTextField(20);
73    private final JTextField extendsClassNameTextField      = new JTextField(20);
74
75    private final MemberSpecificationsPanel memberSpecificationsPanel;
76
77    private int returnValue;
78
79
80    public ClassSpecificationDialog(JFrame owner, boolean fullKeepOptions)
81    {
82        super(owner, msg("specifyClasses"), true);
83        setResizable(true);
84
85        // Create some constraints that can be reused.
86        GridBagConstraints constraints = new GridBagConstraints();
87        constraints.anchor = GridBagConstraints.WEST;
88        constraints.insets = new Insets(1, 2, 1, 2);
89
90        GridBagConstraints constraintsStretch = new GridBagConstraints();
91        constraintsStretch.fill    = GridBagConstraints.HORIZONTAL;
92        constraintsStretch.weightx = 1.0;
93        constraintsStretch.anchor  = GridBagConstraints.WEST;
94        constraintsStretch.insets  = constraints.insets;
95
96        GridBagConstraints constraintsLast = new GridBagConstraints();
97        constraintsLast.gridwidth = GridBagConstraints.REMAINDER;
98        constraintsLast.anchor    = GridBagConstraints.WEST;
99        constraintsLast.insets    = constraints.insets;
100
101        GridBagConstraints constraintsLastStretch = new GridBagConstraints();
102        constraintsLastStretch.gridwidth = GridBagConstraints.REMAINDER;
103        constraintsLastStretch.fill      = GridBagConstraints.HORIZONTAL;
104        constraintsLastStretch.weightx   = 1.0;
105        constraintsLastStretch.anchor    = GridBagConstraints.WEST;
106        constraintsLastStretch.insets    = constraints.insets;
107
108        GridBagConstraints panelConstraints = new GridBagConstraints();
109        panelConstraints.gridwidth = GridBagConstraints.REMAINDER;
110        panelConstraints.fill      = GridBagConstraints.HORIZONTAL;
111        panelConstraints.weightx   = 1.0;
112        panelConstraints.weighty   = 0.0;
113        panelConstraints.anchor    = GridBagConstraints.NORTHWEST;
114        panelConstraints.insets    = constraints.insets;
115
116        GridBagConstraints stretchPanelConstraints = new GridBagConstraints();
117        stretchPanelConstraints.gridwidth = GridBagConstraints.REMAINDER;
118        stretchPanelConstraints.fill      = GridBagConstraints.BOTH;
119        stretchPanelConstraints.weightx   = 1.0;
120        stretchPanelConstraints.weighty   = 1.0;
121        stretchPanelConstraints.anchor    = GridBagConstraints.NORTHWEST;
122        stretchPanelConstraints.insets    = constraints.insets;
123
124        GridBagConstraints labelConstraints = new GridBagConstraints();
125        labelConstraints.anchor = GridBagConstraints.CENTER;
126        labelConstraints.insets = new Insets(2, 10, 2, 10);
127
128        GridBagConstraints lastLabelConstraints = new GridBagConstraints();
129        lastLabelConstraints.gridwidth = GridBagConstraints.REMAINDER;
130        lastLabelConstraints.anchor    = GridBagConstraints.CENTER;
131        lastLabelConstraints.insets    = labelConstraints.insets;
132
133        GridBagConstraints advancedButtonConstraints = new GridBagConstraints();
134        advancedButtonConstraints.weightx = 1.0;
135        advancedButtonConstraints.weighty = 1.0;
136        advancedButtonConstraints.anchor  = GridBagConstraints.SOUTHWEST;
137        advancedButtonConstraints.insets  = new Insets(4, 4, 8, 4);
138
139        GridBagConstraints okButtonConstraints = new GridBagConstraints();
140        okButtonConstraints.weightx = 1.0;
141        okButtonConstraints.weighty = 1.0;
142        okButtonConstraints.anchor  = GridBagConstraints.SOUTHEAST;
143        okButtonConstraints.insets  = advancedButtonConstraints.insets;
144
145        GridBagConstraints cancelButtonConstraints = new GridBagConstraints();
146        cancelButtonConstraints.gridwidth = GridBagConstraints.REMAINDER;
147        cancelButtonConstraints.weighty   = 1.0;
148        cancelButtonConstraints.anchor    = GridBagConstraints.SOUTHEAST;
149        cancelButtonConstraints.insets    = advancedButtonConstraints.insets;
150
151        GridBagLayout layout = new GridBagLayout();
152
153        Border etchedBorder = BorderFactory.createEtchedBorder(EtchedBorder.RAISED);
154
155        // Create the comments panel.
156        JPanel commentsPanel = new JPanel(layout);
157        commentsPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
158                                                                 msg("comments")));
159
160        JScrollPane commentsScrollPane = new JScrollPane(commentsTextArea);
161        commentsScrollPane.setBorder(classNameTextField.getBorder());
162
163        commentsPanel.add(tip(commentsScrollPane, "commentsTip"), constraintsLastStretch);
164
165        // Create the keep option panel.
166        ButtonGroup keepButtonGroup = new ButtonGroup();
167        keepButtonGroup.add(keepClassesAndMembersRadioButton);
168        keepButtonGroup.add(keepClassMembersRadioButton);
169        keepButtonGroup.add(keepClassesWithMembersRadioButton);
170
171        JPanel keepOptionPanel = new JPanel(layout);
172        keepOptionPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
173                                                                   msg("keepTitle")));
174
175        keepOptionPanel.add(tip(keepClassesAndMembersRadioButton,  "keepTip"),                   constraintsLastStretch);
176        keepOptionPanel.add(tip(keepClassMembersRadioButton,       "keepClassMembersTip"),       constraintsLastStretch);
177        keepOptionPanel.add(tip(keepClassesWithMembersRadioButton, "keepClassesWithMembersTip"), constraintsLastStretch);
178
179        // Create the allow option panel.
180        final JPanel allowOptionPanel = new JPanel(layout);
181        allowOptionPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
182                                                                    msg("allowTitle")));
183
184        allowOptionPanel.add(tip(allowShrinkingCheckBox,    "allowShrinkingTip"),    constraintsLastStretch);
185        allowOptionPanel.add(tip(allowOptimizationCheckBox, "allowOptimizationTip"), constraintsLastStretch);
186        allowOptionPanel.add(tip(allowObfuscationCheckBox,  "allowObfuscationTip"),  constraintsLastStretch);
187
188        // Create the access panel.
189        JPanel accessPanel = new JPanel(layout);
190        accessPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
191                                                               msg("access")));
192
193        accessPanel.add(Box.createGlue(),                                labelConstraints);
194        accessPanel.add(tip(new JLabel(msg("required")), "requiredTip"), labelConstraints);
195        accessPanel.add(tip(new JLabel(msg("not")),      "notTip"),      labelConstraints);
196        accessPanel.add(tip(new JLabel(msg("dontCare")), "dontCareTip"), labelConstraints);
197        accessPanel.add(Box.createGlue(),                                constraintsLastStretch);
198
199        publicRadioButtons     = addRadioButtonTriplet("Public",     accessPanel);
200        finalRadioButtons      = addRadioButtonTriplet("Final",      accessPanel);
201        abstractRadioButtons   = addRadioButtonTriplet("Abstract",   accessPanel);
202        enumRadioButtons       = addRadioButtonTriplet("Enum",       accessPanel);
203        annotationRadioButtons = addRadioButtonTriplet("Annotation", accessPanel);
204        interfaceRadioButtons  = addRadioButtonTriplet("Interface",  accessPanel);
205
206        // Create the annotation type panel.
207        final JPanel annotationTypePanel = new JPanel(layout);
208        annotationTypePanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
209                                                                       msg("annotation")));
210
211        annotationTypePanel.add(tip(annotationTypeTextField, "classNameTip"), constraintsLastStretch);
212
213        // Create the class name panel.
214        JPanel classNamePanel = new JPanel(layout);
215        classNamePanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
216                                                                  msg("class")));
217
218        classNamePanel.add(tip(classNameTextField, "classNameTip"), constraintsLastStretch);
219
220        // Create the extends annotation type panel.
221        final JPanel extendsAnnotationTypePanel = new JPanel(layout);
222        extendsAnnotationTypePanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
223                                                                              msg("extendsImplementsAnnotation")));
224
225        extendsAnnotationTypePanel.add(tip(extendsAnnotationTypeTextField, "classNameTip"), constraintsLastStretch);
226
227        // Create the extends class name panel.
228        JPanel extendsClassNamePanel = new JPanel(layout);
229        extendsClassNamePanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
230                                                                         msg("extendsImplementsClass")));
231
232        extendsClassNamePanel.add(tip(extendsClassNameTextField, "classNameTip"), constraintsLastStretch);
233
234
235        // Create the class member list panel.
236        memberSpecificationsPanel = new MemberSpecificationsPanel(this, fullKeepOptions);
237        memberSpecificationsPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
238                                                                             msg("classMembers")));
239
240        // Create the Advanced button.
241        final JButton advancedButton = new JButton(msg("basic"));
242        advancedButton.addActionListener(new ActionListener()
243        {
244            public void actionPerformed(ActionEvent e)
245            {
246                boolean visible = !allowOptionPanel.isVisible();
247
248                allowOptionPanel          .setVisible(visible);
249                annotationTypePanel       .setVisible(visible);
250                extendsAnnotationTypePanel.setVisible(visible);
251
252                advancedButton.setText(msg(visible ? "basic" : "advanced"));
253
254                pack();
255            }
256        });
257        advancedButton.doClick();
258
259        // Create the Ok button.
260        JButton okButton = new JButton(msg("ok"));
261        okButton.addActionListener(new ActionListener()
262        {
263            public void actionPerformed(ActionEvent e)
264            {
265                returnValue = APPROVE_OPTION;
266                hide();
267            }
268        });
269
270        // Create the Cancel button.
271        JButton cancelButton = new JButton(msg("cancel"));
272        cancelButton.addActionListener(new ActionListener()
273        {
274            public void actionPerformed(ActionEvent e)
275            {
276                hide();
277            }
278        });
279
280        // Add all panels to the main panel.
281        JPanel mainPanel = new JPanel(layout);
282        mainPanel.add(tip(commentsPanel,              "commentsTip"),                    panelConstraints);
283        if (fullKeepOptions)
284        {
285            mainPanel.add(tip(keepOptionPanel,        "keepTitleTip"),                   panelConstraints);
286            mainPanel.add(tip(allowOptionPanel,       "allowTitleTip"),                  panelConstraints);
287        }
288        mainPanel.add(tip(accessPanel,                "accessTip"),                      panelConstraints);
289        mainPanel.add(tip(annotationTypePanel,        "annotationTip"),                  panelConstraints);
290        mainPanel.add(tip(classNamePanel,             "classTip"),                       panelConstraints);
291        mainPanel.add(tip(extendsAnnotationTypePanel, "extendsImplementsAnnotationTip"), panelConstraints);
292        mainPanel.add(tip(extendsClassNamePanel,      "extendsImplementsClassTip"),      panelConstraints);
293        mainPanel.add(tip(memberSpecificationsPanel,  "classMembersTip"),                stretchPanelConstraints);
294
295        mainPanel.add(tip(advancedButton, "advancedTip"), advancedButtonConstraints);
296        mainPanel.add(okButton,                           okButtonConstraints);
297        mainPanel.add(cancelButton,                       cancelButtonConstraints);
298
299        getContentPane().add(new JScrollPane(mainPanel));
300    }
301
302
303    /**
304     * Adds a JLabel and three JRadioButton instances in a ButtonGroup to the
305     * given panel with a GridBagLayout, and returns the buttons in an array.
306     */
307    private JRadioButton[] addRadioButtonTriplet(String labelText,
308                                                 JPanel panel)
309    {
310        GridBagConstraints labelConstraints = new GridBagConstraints();
311        labelConstraints.anchor = GridBagConstraints.WEST;
312        labelConstraints.insets = new Insets(2, 10, 2, 10);
313
314        GridBagConstraints buttonConstraints = new GridBagConstraints();
315        buttonConstraints.insets = labelConstraints.insets;
316
317        GridBagConstraints lastGlueConstraints = new GridBagConstraints();
318        lastGlueConstraints.gridwidth = GridBagConstraints.REMAINDER;
319        lastGlueConstraints.weightx   = 1.0;
320
321        // Create the radio buttons.
322        JRadioButton radioButton0 = new JRadioButton();
323        JRadioButton radioButton1 = new JRadioButton();
324        JRadioButton radioButton2 = new JRadioButton();
325
326        // Put them in a button group.
327        ButtonGroup buttonGroup = new ButtonGroup();
328        buttonGroup.add(radioButton0);
329        buttonGroup.add(radioButton1);
330        buttonGroup.add(radioButton2);
331
332        // Add the label and the buttons to the panel.
333        panel.add(new JLabel(labelText), labelConstraints);
334        panel.add(radioButton0,          buttonConstraints);
335        panel.add(radioButton1,          buttonConstraints);
336        panel.add(radioButton2,          buttonConstraints);
337        panel.add(Box.createGlue(),      lastGlueConstraints);
338
339        return new JRadioButton[]
340        {
341             radioButton0,
342             radioButton1,
343             radioButton2
344        };
345    }
346
347
348    /**
349     * Sets the KeepClassSpecification to be represented in this dialog.
350     */
351    public void setKeepSpecification(KeepClassSpecification keepClassSpecification)
352    {
353        boolean markClasses       = keepClassSpecification.markClasses;
354        boolean markConditionally = keepClassSpecification.markConditionally;
355        boolean allowShrinking    = keepClassSpecification.allowShrinking;
356        boolean allowOptimization = keepClassSpecification.allowOptimization;
357        boolean allowObfuscation  = keepClassSpecification.allowObfuscation;
358
359        // Figure out the proper keep radio button and set it.
360        JRadioButton keepOptionRadioButton =
361            markConditionally ? keepClassesWithMembersRadioButton :
362            markClasses       ? keepClassesAndMembersRadioButton  :
363                                keepClassMembersRadioButton;
364
365        keepOptionRadioButton.setSelected(true);
366
367        // Set the allow radio buttons.
368        allowShrinkingCheckBox   .setSelected(allowShrinking);
369        allowOptimizationCheckBox.setSelected(allowOptimization);
370        allowObfuscationCheckBox .setSelected(allowObfuscation);
371
372        setClassSpecification(keepClassSpecification);
373    }
374
375
376    /**
377     * Sets the ClassSpecification to be represented in this dialog.
378     */
379    public void setClassSpecification(ClassSpecification classSpecification)
380    {
381        String comments              = classSpecification.comments;
382        String annotationType        = classSpecification.annotationType;
383        String className             = classSpecification.className;
384        String extendsAnnotationType = classSpecification.extendsAnnotationType;
385        String extendsClassName      = classSpecification.extendsClassName;
386        List   keepFieldOptions      = classSpecification.fieldSpecifications;
387        List   keepMethodOptions     = classSpecification.methodSpecifications;
388
389        // Set the comments text area.
390        commentsTextArea.setText(comments == null ? "" : comments);
391
392        // Set the access radio buttons.
393        setClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_PUBLIC,      publicRadioButtons);
394        setClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_FINAL,       finalRadioButtons);
395        setClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_ABSTRACT,    abstractRadioButtons);
396        setClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_ENUM,        enumRadioButtons);
397        setClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_ANNOTATTION, annotationRadioButtons);
398        setClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_INTERFACE,   interfaceRadioButtons);
399
400        // Set the class and annotation text fields.
401        annotationTypeTextField       .setText(annotationType        == null ? ""  : ClassUtil.externalType(annotationType));
402        classNameTextField            .setText(className             == null ? "*" : ClassUtil.externalClassName(className));
403        extendsAnnotationTypeTextField.setText(extendsAnnotationType == null ? ""  : ClassUtil.externalType(extendsAnnotationType));
404        extendsClassNameTextField     .setText(extendsClassName      == null ? ""  : ClassUtil.externalClassName(extendsClassName));
405
406        // Set the keep class member option list.
407        memberSpecificationsPanel.setMemberSpecifications(keepFieldOptions, keepMethodOptions);
408    }
409
410
411    /**
412     * Returns the KeepClassSpecification currently represented in this dialog.
413     */
414    public KeepClassSpecification getKeepSpecification()
415    {
416        boolean markClasses       = !keepClassMembersRadioButton     .isSelected();
417        boolean markConditionally = keepClassesWithMembersRadioButton.isSelected();
418        boolean allowShrinking    = allowShrinkingCheckBox           .isSelected();
419        boolean allowOptimization = allowOptimizationCheckBox        .isSelected();
420        boolean allowObfuscation  = allowObfuscationCheckBox         .isSelected();
421
422        return new KeepClassSpecification(markClasses,
423                                     markConditionally,
424                                     allowShrinking,
425                                     allowOptimization,
426                                     allowObfuscation,
427                                     getClassSpecification());
428    }
429
430
431    /**
432     * Returns the ClassSpecification currently represented in this dialog.
433     */
434    public ClassSpecification getClassSpecification()
435    {
436        String comments              = commentsTextArea.getText();
437        String annotationType        = annotationTypeTextField.getText();
438        String className             = classNameTextField.getText();
439        String extendsAnnotationType = extendsAnnotationTypeTextField.getText();
440        String extendsClassName      = extendsClassNameTextField.getText();
441
442        ClassSpecification classSpecification =
443            new ClassSpecification(comments.equals("")              ? null : comments,
444                                   0,
445                                   0,
446                                   annotationType.equals("")        ? null : ClassUtil.internalType(annotationType),
447                                   className.equals("") ||
448                                   className.equals("*")            ? null : ClassUtil.internalClassName(className),
449                                   extendsAnnotationType.equals("") ? null : ClassUtil.internalType(extendsAnnotationType),
450                                   extendsClassName.equals("")      ? null : ClassUtil.internalClassName(extendsClassName));
451
452        // Also get the access radio button settings.
453        getClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_PUBLIC,      publicRadioButtons);
454        getClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_FINAL,       finalRadioButtons);
455        getClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_ABSTRACT,    abstractRadioButtons);
456        getClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_ENUM,        enumRadioButtons);
457        getClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_ANNOTATTION, annotationRadioButtons);
458        getClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_INTERFACE,   interfaceRadioButtons);
459
460        // Get the keep class member option lists.
461        classSpecification.fieldSpecifications  = memberSpecificationsPanel.getMemberSpecifications(true);
462        classSpecification.methodSpecifications = memberSpecificationsPanel.getMemberSpecifications(false);
463
464        return classSpecification;
465    }
466
467
468    /**
469     * Shows this dialog. This method only returns when the dialog is closed.
470     *
471     * @return <code>CANCEL_OPTION</code> or <code>APPROVE_OPTION</code>,
472     *         depending on the choice of the user.
473     */
474    public int showDialog()
475    {
476        returnValue = CANCEL_OPTION;
477
478        // Open the dialog in the right place, then wait for it to be closed,
479        // one way or another.
480        pack();
481        setLocationRelativeTo(getOwner());
482        show();
483
484        return returnValue;
485    }
486
487
488    /**
489     * Sets the appropriate radio button of a given triplet, based on the access
490     * flags of the given keep option.
491     */
492    private void setClassSpecificationRadioButtons(ClassSpecification classSpecification,
493                                                   int                flag,
494                                                   JRadioButton[]     radioButtons)
495    {
496        int index = (classSpecification.requiredSetAccessFlags   & flag) != 0 ? 0 :
497                    (classSpecification.requiredUnsetAccessFlags & flag) != 0 ? 1 :
498                                                                                 2;
499        radioButtons[index].setSelected(true);
500    }
501
502
503    /**
504     * Updates the access flag of the given keep option, based on the given radio
505     * button triplet.
506     */
507    private void getClassSpecificationRadioButtons(ClassSpecification classSpecification,
508                                                   int                flag,
509                                                   JRadioButton[]     radioButtons)
510    {
511        if      (radioButtons[0].isSelected())
512        {
513            classSpecification.requiredSetAccessFlags   |= flag;
514        }
515        else if (radioButtons[1].isSelected())
516        {
517            classSpecification.requiredUnsetAccessFlags |= flag;
518        }
519    }
520
521
522    /**
523     * Attaches the tool tip from the GUI resources that corresponds to the
524     * given key, to the given component.
525     */
526    private static JComponent tip(JComponent component, String messageKey)
527    {
528        component.setToolTipText(msg(messageKey));
529
530        return component;
531    }
532
533
534    /**
535     * Returns the message from the GUI resources that corresponds to the given
536     * key.
537     */
538    private static String msg(String messageKey)
539    {
540         return GUIResources.getMessage(messageKey);
541    }
542}
543