1 /* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2014 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 */ 21 package proguard.gui; 22 23 import proguard.MemberSpecification; 24 import proguard.classfile.*; 25 import proguard.classfile.util.ClassUtil; 26 import proguard.util.ListUtil; 27 28 import javax.swing.*; 29 import javax.swing.border.*; 30 import java.awt.*; 31 import java.awt.event.*; 32 33 /** 34 * This <code>JDialog</code> allows the user to enter a String. 35 * 36 * @author Eric Lafortune 37 */ 38 final class MemberSpecificationDialog 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 boolean isField; 53 54 private final JRadioButton[] publicRadioButtons; 55 private final JRadioButton[] privateRadioButtons; 56 private final JRadioButton[] protectedRadioButtons; 57 private final JRadioButton[] staticRadioButtons; 58 private final JRadioButton[] finalRadioButtons; 59 private final JRadioButton[] syntheticRadioButtons; 60 61 private JRadioButton[] volatileRadioButtons; 62 private JRadioButton[] transientRadioButtons; 63 64 private JRadioButton[] synchronizedRadioButtons; 65 private JRadioButton[] nativeRadioButtons; 66 private JRadioButton[] abstractRadioButtons; 67 private JRadioButton[] strictRadioButtons; 68 private JRadioButton[] bridgeRadioButtons; 69 private JRadioButton[] varargsRadioButtons; 70 71 private final JTextField annotationTypeTextField = new JTextField(20); 72 private final JTextField nameTextField = new JTextField(20); 73 private final JTextField typeTextField = new JTextField(20); 74 private final JTextField argumentTypesTextField = new JTextField(20); 75 76 private int returnValue; 77 78 MemberSpecificationDialog(JDialog owner, boolean isField)79 public MemberSpecificationDialog(JDialog owner, boolean isField) 80 { 81 super(owner, msg(isField ? "specifyFields" : "specifyMethods"), true); 82 setResizable(true); 83 84 // Create some constraints that can be reused. 85 GridBagConstraints constraints = new GridBagConstraints(); 86 constraints.anchor = GridBagConstraints.WEST; 87 constraints.insets = new Insets(1, 2, 1, 2); 88 89 GridBagConstraints constraintsStretch = new GridBagConstraints(); 90 constraintsStretch.fill = GridBagConstraints.HORIZONTAL; 91 constraintsStretch.weightx = 1.0; 92 constraintsStretch.anchor = GridBagConstraints.WEST; 93 constraintsStretch.insets = constraints.insets; 94 95 GridBagConstraints constraintsLast = new GridBagConstraints(); 96 constraintsLast.gridwidth = GridBagConstraints.REMAINDER; 97 constraintsLast.anchor = GridBagConstraints.WEST; 98 constraintsLast.insets = constraints.insets; 99 100 GridBagConstraints constraintsLastStretch = new GridBagConstraints(); 101 constraintsLastStretch.gridwidth = GridBagConstraints.REMAINDER; 102 constraintsLastStretch.fill = GridBagConstraints.HORIZONTAL; 103 constraintsLastStretch.weightx = 1.0; 104 constraintsLastStretch.anchor = GridBagConstraints.WEST; 105 constraintsLastStretch.insets = constraints.insets; 106 107 GridBagConstraints panelConstraints = new GridBagConstraints(); 108 panelConstraints.gridwidth = GridBagConstraints.REMAINDER; 109 panelConstraints.fill = GridBagConstraints.HORIZONTAL; 110 panelConstraints.weightx = 1.0; 111 panelConstraints.weighty = 0.0; 112 panelConstraints.anchor = GridBagConstraints.NORTHWEST; 113 panelConstraints.insets = constraints.insets; 114 115 GridBagConstraints stretchPanelConstraints = new GridBagConstraints(); 116 stretchPanelConstraints.gridwidth = GridBagConstraints.REMAINDER; 117 stretchPanelConstraints.fill = GridBagConstraints.BOTH; 118 stretchPanelConstraints.weightx = 1.0; 119 stretchPanelConstraints.weighty = 1.0; 120 stretchPanelConstraints.anchor = GridBagConstraints.NORTHWEST; 121 stretchPanelConstraints.insets = constraints.insets; 122 123 GridBagConstraints labelConstraints = new GridBagConstraints(); 124 labelConstraints.anchor = GridBagConstraints.CENTER; 125 labelConstraints.insets = new Insets(2, 10, 2, 10); 126 127 GridBagConstraints lastLabelConstraints = new GridBagConstraints(); 128 lastLabelConstraints.gridwidth = GridBagConstraints.REMAINDER; 129 lastLabelConstraints.anchor = GridBagConstraints.CENTER; 130 lastLabelConstraints.insets = labelConstraints.insets; 131 132 GridBagConstraints advancedButtonConstraints = new GridBagConstraints(); 133 advancedButtonConstraints.weightx = 1.0; 134 advancedButtonConstraints.weighty = 1.0; 135 advancedButtonConstraints.anchor = GridBagConstraints.SOUTHWEST; 136 advancedButtonConstraints.insets = new Insets(4, 4, 8, 4); 137 138 GridBagConstraints okButtonConstraints = new GridBagConstraints(); 139 okButtonConstraints.weightx = 1.0; 140 okButtonConstraints.weighty = 1.0; 141 okButtonConstraints.anchor = GridBagConstraints.SOUTHEAST; 142 okButtonConstraints.insets = advancedButtonConstraints.insets; 143 144 GridBagConstraints cancelButtonConstraints = new GridBagConstraints(); 145 cancelButtonConstraints.gridwidth = GridBagConstraints.REMAINDER; 146 cancelButtonConstraints.weighty = 1.0; 147 cancelButtonConstraints.anchor = GridBagConstraints.SOUTHEAST; 148 cancelButtonConstraints.insets = okButtonConstraints.insets; 149 150 GridBagLayout layout = new GridBagLayout(); 151 152 Border etchedBorder = BorderFactory.createEtchedBorder(EtchedBorder.RAISED); 153 154 this.isField = isField; 155 156 // Create the access panel. 157 JPanel accessPanel = new JPanel(layout); 158 accessPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder, 159 msg("access"))); 160 161 accessPanel.add(Box.createGlue(), labelConstraints); 162 accessPanel.add(tip(new JLabel(msg("required")), "requiredTip"), labelConstraints); 163 accessPanel.add(tip(new JLabel(msg("not")), "notTip"), labelConstraints); 164 accessPanel.add(tip(new JLabel(msg("dontCare")), "dontCareTip"), labelConstraints); 165 accessPanel.add(Box.createGlue(), constraintsLastStretch); 166 167 publicRadioButtons = addRadioButtonTriplet("Public", accessPanel); 168 privateRadioButtons = addRadioButtonTriplet("Private", accessPanel); 169 protectedRadioButtons = addRadioButtonTriplet("Protected", accessPanel); 170 staticRadioButtons = addRadioButtonTriplet("Static", accessPanel); 171 finalRadioButtons = addRadioButtonTriplet("Final", accessPanel); 172 syntheticRadioButtons = addRadioButtonTriplet("Synthetic", accessPanel); 173 174 if (isField) 175 { 176 volatileRadioButtons = addRadioButtonTriplet("Volatile", accessPanel); 177 transientRadioButtons = addRadioButtonTriplet("Transient", accessPanel); 178 } 179 else 180 { 181 synchronizedRadioButtons = addRadioButtonTriplet("Synchronized", accessPanel); 182 nativeRadioButtons = addRadioButtonTriplet("Native", accessPanel); 183 abstractRadioButtons = addRadioButtonTriplet("Abstract", accessPanel); 184 strictRadioButtons = addRadioButtonTriplet("Strict", accessPanel); 185 bridgeRadioButtons = addRadioButtonTriplet("Bridge", accessPanel); 186 varargsRadioButtons = addRadioButtonTriplet("Varargs", accessPanel); 187 } 188 189 // Create the type panel. 190 JPanel typePanel = new JPanel(layout); 191 typePanel.setBorder(BorderFactory.createTitledBorder(etchedBorder, 192 msg(isField ? "fieldType" : 193 "returnType"))); 194 195 typePanel.add(tip(typeTextField, "typeTip"), constraintsLastStretch); 196 197 // Create the annotation type panel. 198 final JPanel annotationTypePanel = new JPanel(layout); 199 annotationTypePanel.setBorder(BorderFactory.createTitledBorder(etchedBorder, 200 msg("annotation"))); 201 202 annotationTypePanel.add(tip(annotationTypeTextField, "classNameTip"), constraintsLastStretch); 203 204 // Create the name panel. 205 JPanel namePanel = new JPanel(layout); 206 namePanel.setBorder(BorderFactory.createTitledBorder(etchedBorder, 207 msg("name"))); 208 209 namePanel.add(tip(nameTextField, isField ? "fieldNameTip" : 210 "methodNameTip"), constraintsLastStretch); 211 212 // Create the arguments panel. 213 JPanel argumentsPanel = new JPanel(layout); 214 argumentsPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder, 215 msg("argumentTypes"))); 216 217 argumentsPanel.add(tip(argumentTypesTextField, "argumentTypes2Tip"), constraintsLastStretch); 218 219 // Create the Advanced button. 220 final JButton advancedButton = new JButton(msg("basic")); 221 advancedButton.addActionListener(new ActionListener() 222 { 223 public void actionPerformed(ActionEvent e) 224 { 225 boolean visible = !annotationTypePanel.isVisible(); 226 227 annotationTypePanel.setVisible(visible); 228 229 advancedButton.setText(msg(visible ? "basic" : "advanced")); 230 231 pack(); 232 } 233 }); 234 advancedButton.doClick(); 235 236 // Create the Ok button. 237 JButton okButton = new JButton(msg("ok")); 238 okButton.addActionListener(new ActionListener() 239 { 240 public void actionPerformed(ActionEvent e) 241 { 242 returnValue = APPROVE_OPTION; 243 hide(); 244 } 245 }); 246 247 // Create the Cancel button. 248 JButton cancelButton = new JButton(msg("cancel")); 249 cancelButton.addActionListener(new ActionListener() 250 { 251 public void actionPerformed(ActionEvent e) 252 { 253 hide(); 254 } 255 }); 256 257 // Add all panels to the main panel. 258 JPanel mainPanel = new JPanel(layout); 259 mainPanel.add(tip(accessPanel, "accessTip"), panelConstraints); 260 mainPanel.add(tip(annotationTypePanel, "annotationTip"), panelConstraints); 261 mainPanel.add(tip(typePanel, isField ? "fieldTypeTip" : 262 "returnTypeTip"), panelConstraints); 263 mainPanel.add(tip(namePanel, "nameTip"), panelConstraints); 264 265 if (!isField) 266 { 267 mainPanel.add(tip(argumentsPanel, "argumentTypesTip"), panelConstraints); 268 } 269 270 mainPanel.add(tip(advancedButton, "advancedTip"), advancedButtonConstraints); 271 mainPanel.add(okButton, okButtonConstraints); 272 mainPanel.add(cancelButton, cancelButtonConstraints); 273 274 getContentPane().add(new JScrollPane(mainPanel)); 275 } 276 277 278 /** 279 * Adds a JLabel and three JRadioButton instances in a ButtonGroup to the 280 * given panel with a GridBagLayout, and returns the buttons in an array. 281 */ addRadioButtonTriplet(String labelText, JPanel panel)282 private JRadioButton[] addRadioButtonTriplet(String labelText, 283 JPanel panel) 284 { 285 GridBagConstraints labelConstraints = new GridBagConstraints(); 286 labelConstraints.anchor = GridBagConstraints.WEST; 287 labelConstraints.insets = new Insets(2, 10, 2, 10); 288 289 GridBagConstraints buttonConstraints = new GridBagConstraints(); 290 buttonConstraints.insets = labelConstraints.insets; 291 292 GridBagConstraints lastGlueConstraints = new GridBagConstraints(); 293 lastGlueConstraints.gridwidth = GridBagConstraints.REMAINDER; 294 lastGlueConstraints.weightx = 1.0; 295 296 // Create the radio buttons. 297 JRadioButton radioButton0 = new JRadioButton(); 298 JRadioButton radioButton1 = new JRadioButton(); 299 JRadioButton radioButton2 = new JRadioButton(); 300 301 // Put them in a button group. 302 ButtonGroup buttonGroup = new ButtonGroup(); 303 buttonGroup.add(radioButton0); 304 buttonGroup.add(radioButton1); 305 buttonGroup.add(radioButton2); 306 307 // Add the label and the buttons to the panel. 308 panel.add(new JLabel(labelText), labelConstraints); 309 panel.add(radioButton0, buttonConstraints); 310 panel.add(radioButton1, buttonConstraints); 311 panel.add(radioButton2, buttonConstraints); 312 panel.add(Box.createGlue(), lastGlueConstraints); 313 314 return new JRadioButton[] 315 { 316 radioButton0, 317 radioButton1, 318 radioButton2 319 }; 320 } 321 322 323 /** 324 * Sets the MemberSpecification to be represented in this dialog. 325 */ setMemberSpecification(MemberSpecification memberSpecification)326 public void setMemberSpecification(MemberSpecification memberSpecification) 327 { 328 String annotationType = memberSpecification.annotationType; 329 String name = memberSpecification.name; 330 String descriptor = memberSpecification.descriptor; 331 332 // Set the class name text fields. 333 annotationTypeTextField.setText(annotationType == null ? "" : ClassUtil.externalType(annotationType)); 334 335 // Set the access radio buttons. 336 setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_PUBLIC, publicRadioButtons); 337 setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_PRIVATE, privateRadioButtons); 338 setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_PROTECTED, protectedRadioButtons); 339 setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_STATIC, staticRadioButtons); 340 setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_FINAL, finalRadioButtons); 341 setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_SYNTHETIC, syntheticRadioButtons); 342 setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_VOLATILE, volatileRadioButtons); 343 setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_TRANSIENT, transientRadioButtons); 344 setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_SYNCHRONIZED, synchronizedRadioButtons); 345 setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_NATIVE, nativeRadioButtons); 346 setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_ABSTRACT, abstractRadioButtons); 347 setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_STRICT, strictRadioButtons); 348 setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_BRIDGE, bridgeRadioButtons); 349 setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_VARARGS, varargsRadioButtons); 350 351 // Set the class name text fields. 352 nameTextField.setText(name == null ? "*" : name); 353 354 if (isField) 355 { 356 typeTextField .setText(descriptor == null ? "***" : ClassUtil.externalType(descriptor)); 357 } 358 else 359 { 360 typeTextField .setText(descriptor == null ? "***" : ClassUtil.externalMethodReturnType(descriptor)); 361 argumentTypesTextField.setText(descriptor == null ? "..." : ClassUtil.externalMethodArguments(descriptor)); 362 } 363 } 364 365 366 /** 367 * Returns the MemberSpecification currently represented in this dialog. 368 */ getMemberSpecification()369 public MemberSpecification getMemberSpecification() 370 { 371 String annotationType = annotationTypeTextField.getText(); 372 String name = nameTextField.getText(); 373 String type = typeTextField.getText(); 374 String arguments = argumentTypesTextField.getText(); 375 376 // Convert all class member specifications into the internal format. 377 annotationType = 378 annotationType.equals("") || 379 annotationType.equals("***") ? null : ClassUtil.internalType(annotationType); 380 381 if (name.equals("") || 382 name.equals("*")) 383 { 384 name = null; 385 } 386 387 if (isField) 388 { 389 type = 390 type.equals("") || 391 type.equals("***") ? null : ClassUtil.internalType(type); 392 } 393 else 394 { 395 if (type.equals("")) 396 { 397 type = JavaConstants.TYPE_VOID; 398 } 399 400 type = 401 type .equals("***") && 402 arguments.equals("...") ? null : 403 ClassUtil.internalMethodDescriptor(type, ListUtil.commaSeparatedList(arguments)); 404 } 405 406 MemberSpecification memberSpecification = 407 new MemberSpecification(0, 0, annotationType, name, type); 408 409 // Also get the access radio button settings. 410 getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_PUBLIC, publicRadioButtons); 411 getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_PRIVATE, privateRadioButtons); 412 getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_PROTECTED, protectedRadioButtons); 413 getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_STATIC, staticRadioButtons); 414 getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_FINAL, finalRadioButtons); 415 getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_SYNTHETIC, syntheticRadioButtons); 416 getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_VOLATILE, volatileRadioButtons); 417 getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_TRANSIENT, transientRadioButtons); 418 getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_SYNCHRONIZED, synchronizedRadioButtons); 419 getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_NATIVE, nativeRadioButtons); 420 getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_ABSTRACT, abstractRadioButtons); 421 getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_STRICT, strictRadioButtons); 422 getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_BRIDGE, bridgeRadioButtons); 423 getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_VARARGS, varargsRadioButtons); 424 425 return memberSpecification; 426 } 427 428 429 /** 430 * Shows this dialog. This method only returns when the dialog is closed. 431 * 432 * @return <code>CANCEL_OPTION</code> or <code>APPROVE_OPTION</code>, 433 * depending on the choice of the user. 434 */ showDialog()435 public int showDialog() 436 { 437 returnValue = CANCEL_OPTION; 438 439 // Open the dialog in the right place, then wait for it to be closed, 440 // one way or another. 441 pack(); 442 setLocationRelativeTo(getOwner()); 443 show(); 444 445 return returnValue; 446 } 447 448 449 /** 450 * Sets the appropriate radio button of a given triplet, based on the access 451 * flags of the given keep option. 452 */ setMemberSpecificationRadioButtons(MemberSpecification memberSpecification, int flag, JRadioButton[] radioButtons)453 private void setMemberSpecificationRadioButtons(MemberSpecification memberSpecification, 454 int flag, 455 JRadioButton[] radioButtons) 456 { 457 if (radioButtons != null) 458 { 459 int index = (memberSpecification.requiredSetAccessFlags & flag) != 0 ? 0 : 460 (memberSpecification.requiredUnsetAccessFlags & flag) != 0 ? 1 : 461 2; 462 radioButtons[index].setSelected(true); 463 } 464 } 465 466 467 /** 468 * Updates the access flag of the given keep option, based on the given radio 469 * button triplet. 470 */ getMemberSpecificationRadioButtons(MemberSpecification memberSpecification, int flag, JRadioButton[] radioButtons)471 private void getMemberSpecificationRadioButtons(MemberSpecification memberSpecification, 472 int flag, 473 JRadioButton[] radioButtons) 474 { 475 if (radioButtons != null) 476 { 477 if (radioButtons[0].isSelected()) 478 { 479 memberSpecification.requiredSetAccessFlags |= flag; 480 } 481 else if (radioButtons[1].isSelected()) 482 { 483 memberSpecification.requiredUnsetAccessFlags |= flag; 484 } 485 } 486 } 487 488 489 /** 490 * Attaches the tool tip from the GUI resources that corresponds to the 491 * given key, to the given component. 492 */ tip(JComponent component, String messageKey)493 private static JComponent tip(JComponent component, String messageKey) 494 { 495 component.setToolTipText(msg(messageKey)); 496 497 return component; 498 } 499 500 501 /** 502 * Returns the message from the GUI resources that corresponds to the given 503 * key. 504 */ msg(String messageKey)505 private static String msg(String messageKey) 506 { 507 return GUIResources.getMessage(messageKey); 508 } 509 } 510