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