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