| /* |
| * ProGuard -- shrinking, optimization, obfuscation, and preverification |
| * of Java bytecode. |
| * |
| * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License as published by the Free |
| * Software Foundation; either version 2 of the License, or (at your option) |
| * any later version. |
| * |
| * This program is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| * more details. |
| * |
| * You should have received a copy of the GNU General Public License along |
| * with this program; if not, write to the Free Software Foundation, Inc., |
| * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| package proguard.gui; |
| |
| import proguard.MemberSpecification; |
| import proguard.classfile.*; |
| import proguard.classfile.util.ClassUtil; |
| import proguard.util.ListUtil; |
| |
| import javax.swing.*; |
| import javax.swing.border.*; |
| import java.awt.*; |
| import java.awt.event.*; |
| |
| /** |
| * This <code>JDialog</code> allows the user to enter a String. |
| * |
| * @author Eric Lafortune |
| */ |
| final class MemberSpecificationDialog extends JDialog |
| { |
| /** |
| * Return value if the dialog is canceled (with the Cancel button or by |
| * closing the dialog window). |
| */ |
| public static final int CANCEL_OPTION = 1; |
| |
| /** |
| * Return value if the dialog is approved (with the Ok button). |
| */ |
| public static final int APPROVE_OPTION = 0; |
| |
| |
| private final boolean isField; |
| |
| private final JRadioButton[] publicRadioButtons; |
| private final JRadioButton[] privateRadioButtons; |
| private final JRadioButton[] protectedRadioButtons; |
| private final JRadioButton[] staticRadioButtons; |
| private final JRadioButton[] finalRadioButtons; |
| private final JRadioButton[] syntheticRadioButtons; |
| |
| private JRadioButton[] volatileRadioButtons; |
| private JRadioButton[] transientRadioButtons; |
| |
| private JRadioButton[] synchronizedRadioButtons; |
| private JRadioButton[] nativeRadioButtons; |
| private JRadioButton[] abstractRadioButtons; |
| private JRadioButton[] strictRadioButtons; |
| private JRadioButton[] bridgeRadioButtons; |
| private JRadioButton[] varargsRadioButtons; |
| |
| private final JTextField annotationTypeTextField = new JTextField(20); |
| private final JTextField nameTextField = new JTextField(20); |
| private final JTextField typeTextField = new JTextField(20); |
| private final JTextField argumentTypesTextField = new JTextField(20); |
| |
| private int returnValue; |
| |
| |
| public MemberSpecificationDialog(JDialog owner, boolean isField) |
| { |
| super(owner, msg(isField ? "specifyFields" : "specifyMethods"), true); |
| setResizable(true); |
| |
| // Create some constraints that can be reused. |
| GridBagConstraints constraints = new GridBagConstraints(); |
| constraints.anchor = GridBagConstraints.WEST; |
| constraints.insets = new Insets(1, 2, 1, 2); |
| |
| GridBagConstraints constraintsStretch = new GridBagConstraints(); |
| constraintsStretch.fill = GridBagConstraints.HORIZONTAL; |
| constraintsStretch.weightx = 1.0; |
| constraintsStretch.anchor = GridBagConstraints.WEST; |
| constraintsStretch.insets = constraints.insets; |
| |
| GridBagConstraints constraintsLast = new GridBagConstraints(); |
| constraintsLast.gridwidth = GridBagConstraints.REMAINDER; |
| constraintsLast.anchor = GridBagConstraints.WEST; |
| constraintsLast.insets = constraints.insets; |
| |
| GridBagConstraints constraintsLastStretch = new GridBagConstraints(); |
| constraintsLastStretch.gridwidth = GridBagConstraints.REMAINDER; |
| constraintsLastStretch.fill = GridBagConstraints.HORIZONTAL; |
| constraintsLastStretch.weightx = 1.0; |
| constraintsLastStretch.anchor = GridBagConstraints.WEST; |
| constraintsLastStretch.insets = constraints.insets; |
| |
| GridBagConstraints panelConstraints = new GridBagConstraints(); |
| panelConstraints.gridwidth = GridBagConstraints.REMAINDER; |
| panelConstraints.fill = GridBagConstraints.HORIZONTAL; |
| panelConstraints.weightx = 1.0; |
| panelConstraints.weighty = 0.0; |
| panelConstraints.anchor = GridBagConstraints.NORTHWEST; |
| panelConstraints.insets = constraints.insets; |
| |
| GridBagConstraints stretchPanelConstraints = new GridBagConstraints(); |
| stretchPanelConstraints.gridwidth = GridBagConstraints.REMAINDER; |
| stretchPanelConstraints.fill = GridBagConstraints.BOTH; |
| stretchPanelConstraints.weightx = 1.0; |
| stretchPanelConstraints.weighty = 1.0; |
| stretchPanelConstraints.anchor = GridBagConstraints.NORTHWEST; |
| stretchPanelConstraints.insets = constraints.insets; |
| |
| GridBagConstraints labelConstraints = new GridBagConstraints(); |
| labelConstraints.anchor = GridBagConstraints.CENTER; |
| labelConstraints.insets = new Insets(2, 10, 2, 10); |
| |
| GridBagConstraints lastLabelConstraints = new GridBagConstraints(); |
| lastLabelConstraints.gridwidth = GridBagConstraints.REMAINDER; |
| lastLabelConstraints.anchor = GridBagConstraints.CENTER; |
| lastLabelConstraints.insets = labelConstraints.insets; |
| |
| GridBagConstraints advancedButtonConstraints = new GridBagConstraints(); |
| advancedButtonConstraints.weightx = 1.0; |
| advancedButtonConstraints.weighty = 1.0; |
| advancedButtonConstraints.anchor = GridBagConstraints.SOUTHWEST; |
| advancedButtonConstraints.insets = new Insets(4, 4, 8, 4); |
| |
| GridBagConstraints okButtonConstraints = new GridBagConstraints(); |
| okButtonConstraints.weightx = 1.0; |
| okButtonConstraints.weighty = 1.0; |
| okButtonConstraints.anchor = GridBagConstraints.SOUTHEAST; |
| okButtonConstraints.insets = advancedButtonConstraints.insets; |
| |
| GridBagConstraints cancelButtonConstraints = new GridBagConstraints(); |
| cancelButtonConstraints.gridwidth = GridBagConstraints.REMAINDER; |
| cancelButtonConstraints.weighty = 1.0; |
| cancelButtonConstraints.anchor = GridBagConstraints.SOUTHEAST; |
| cancelButtonConstraints.insets = okButtonConstraints.insets; |
| |
| GridBagLayout layout = new GridBagLayout(); |
| |
| Border etchedBorder = BorderFactory.createEtchedBorder(EtchedBorder.RAISED); |
| |
| this.isField = isField; |
| |
| // Create the access panel. |
| JPanel accessPanel = new JPanel(layout); |
| accessPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder, |
| msg("access"))); |
| |
| accessPanel.add(Box.createGlue(), labelConstraints); |
| accessPanel.add(tip(new JLabel(msg("required")), "requiredTip"), labelConstraints); |
| accessPanel.add(tip(new JLabel(msg("not")), "notTip"), labelConstraints); |
| accessPanel.add(tip(new JLabel(msg("dontCare")), "dontCareTip"), labelConstraints); |
| accessPanel.add(Box.createGlue(), constraintsLastStretch); |
| |
| publicRadioButtons = addRadioButtonTriplet("Public", accessPanel); |
| privateRadioButtons = addRadioButtonTriplet("Private", accessPanel); |
| protectedRadioButtons = addRadioButtonTriplet("Protected", accessPanel); |
| staticRadioButtons = addRadioButtonTriplet("Static", accessPanel); |
| finalRadioButtons = addRadioButtonTriplet("Final", accessPanel); |
| syntheticRadioButtons = addRadioButtonTriplet("Synthetic", accessPanel); |
| |
| if (isField) |
| { |
| volatileRadioButtons = addRadioButtonTriplet("Volatile", accessPanel); |
| transientRadioButtons = addRadioButtonTriplet("Transient", accessPanel); |
| } |
| else |
| { |
| synchronizedRadioButtons = addRadioButtonTriplet("Synchronized", accessPanel); |
| nativeRadioButtons = addRadioButtonTriplet("Native", accessPanel); |
| abstractRadioButtons = addRadioButtonTriplet("Abstract", accessPanel); |
| strictRadioButtons = addRadioButtonTriplet("Strict", accessPanel); |
| bridgeRadioButtons = addRadioButtonTriplet("Bridge", accessPanel); |
| varargsRadioButtons = addRadioButtonTriplet("Varargs", accessPanel); |
| } |
| |
| // Create the type panel. |
| JPanel typePanel = new JPanel(layout); |
| typePanel.setBorder(BorderFactory.createTitledBorder(etchedBorder, |
| msg(isField ? "fieldType" : |
| "returnType"))); |
| |
| typePanel.add(tip(typeTextField, "typeTip"), constraintsLastStretch); |
| |
| // Create the annotation type panel. |
| final JPanel annotationTypePanel = new JPanel(layout); |
| annotationTypePanel.setBorder(BorderFactory.createTitledBorder(etchedBorder, |
| msg("annotation"))); |
| |
| annotationTypePanel.add(tip(annotationTypeTextField, "classNameTip"), constraintsLastStretch); |
| |
| // Create the name panel. |
| JPanel namePanel = new JPanel(layout); |
| namePanel.setBorder(BorderFactory.createTitledBorder(etchedBorder, |
| msg("name"))); |
| |
| namePanel.add(tip(nameTextField, isField ? "fieldNameTip" : |
| "methodNameTip"), constraintsLastStretch); |
| |
| // Create the arguments panel. |
| JPanel argumentsPanel = new JPanel(layout); |
| argumentsPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder, |
| msg("argumentTypes"))); |
| |
| argumentsPanel.add(tip(argumentTypesTextField, "argumentTypes2Tip"), constraintsLastStretch); |
| |
| // Create the Advanced button. |
| final JButton advancedButton = new JButton(msg("basic")); |
| advancedButton.addActionListener(new ActionListener() |
| { |
| public void actionPerformed(ActionEvent e) |
| { |
| boolean visible = !annotationTypePanel.isVisible(); |
| |
| annotationTypePanel.setVisible(visible); |
| |
| advancedButton.setText(msg(visible ? "basic" : "advanced")); |
| |
| pack(); |
| } |
| }); |
| advancedButton.doClick(); |
| |
| // Create the Ok button. |
| JButton okButton = new JButton(msg("ok")); |
| okButton.addActionListener(new ActionListener() |
| { |
| public void actionPerformed(ActionEvent e) |
| { |
| returnValue = APPROVE_OPTION; |
| hide(); |
| } |
| }); |
| |
| // Create the Cancel button. |
| JButton cancelButton = new JButton(msg("cancel")); |
| cancelButton.addActionListener(new ActionListener() |
| { |
| public void actionPerformed(ActionEvent e) |
| { |
| hide(); |
| } |
| }); |
| |
| // Add all panels to the main panel. |
| JPanel mainPanel = new JPanel(layout); |
| mainPanel.add(tip(accessPanel, "accessTip"), panelConstraints); |
| mainPanel.add(tip(annotationTypePanel, "annotationTip"), panelConstraints); |
| mainPanel.add(tip(typePanel, isField ? "fieldTypeTip" : |
| "returnTypeTip"), panelConstraints); |
| mainPanel.add(tip(namePanel, "nameTip"), panelConstraints); |
| |
| if (!isField) |
| { |
| mainPanel.add(tip(argumentsPanel, "argumentTypesTip"), panelConstraints); |
| } |
| |
| mainPanel.add(tip(advancedButton, "advancedTip"), advancedButtonConstraints); |
| mainPanel.add(okButton, okButtonConstraints); |
| mainPanel.add(cancelButton, cancelButtonConstraints); |
| |
| getContentPane().add(new JScrollPane(mainPanel)); |
| } |
| |
| |
| /** |
| * Adds a JLabel and three JRadioButton instances in a ButtonGroup to the |
| * given panel with a GridBagLayout, and returns the buttons in an array. |
| */ |
| private JRadioButton[] addRadioButtonTriplet(String labelText, |
| JPanel panel) |
| { |
| GridBagConstraints labelConstraints = new GridBagConstraints(); |
| labelConstraints.anchor = GridBagConstraints.WEST; |
| labelConstraints.insets = new Insets(2, 10, 2, 10); |
| |
| GridBagConstraints buttonConstraints = new GridBagConstraints(); |
| buttonConstraints.insets = labelConstraints.insets; |
| |
| GridBagConstraints lastGlueConstraints = new GridBagConstraints(); |
| lastGlueConstraints.gridwidth = GridBagConstraints.REMAINDER; |
| lastGlueConstraints.weightx = 1.0; |
| |
| // Create the radio buttons. |
| JRadioButton radioButton0 = new JRadioButton(); |
| JRadioButton radioButton1 = new JRadioButton(); |
| JRadioButton radioButton2 = new JRadioButton(); |
| |
| // Put them in a button group. |
| ButtonGroup buttonGroup = new ButtonGroup(); |
| buttonGroup.add(radioButton0); |
| buttonGroup.add(radioButton1); |
| buttonGroup.add(radioButton2); |
| |
| // Add the label and the buttons to the panel. |
| panel.add(new JLabel(labelText), labelConstraints); |
| panel.add(radioButton0, buttonConstraints); |
| panel.add(radioButton1, buttonConstraints); |
| panel.add(radioButton2, buttonConstraints); |
| panel.add(Box.createGlue(), lastGlueConstraints); |
| |
| return new JRadioButton[] |
| { |
| radioButton0, |
| radioButton1, |
| radioButton2 |
| }; |
| } |
| |
| |
| /** |
| * Sets the MemberSpecification to be represented in this dialog. |
| */ |
| public void setMemberSpecification(MemberSpecification memberSpecification) |
| { |
| String annotationType = memberSpecification.annotationType; |
| String name = memberSpecification.name; |
| String descriptor = memberSpecification.descriptor; |
| |
| // Set the class name text fields. |
| annotationTypeTextField.setText(annotationType == null ? "" : ClassUtil.externalType(annotationType)); |
| |
| // Set the access radio buttons. |
| setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_PUBLIC, publicRadioButtons); |
| setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_PRIVATE, privateRadioButtons); |
| setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_PROTECTED, protectedRadioButtons); |
| setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_STATIC, staticRadioButtons); |
| setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_FINAL, finalRadioButtons); |
| setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_SYNTHETIC, syntheticRadioButtons); |
| setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_VOLATILE, volatileRadioButtons); |
| setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_TRANSIENT, transientRadioButtons); |
| setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_SYNCHRONIZED, synchronizedRadioButtons); |
| setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_NATIVE, nativeRadioButtons); |
| setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_ABSTRACT, abstractRadioButtons); |
| setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_STRICT, strictRadioButtons); |
| setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_BRIDGE, bridgeRadioButtons); |
| setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_VARARGS, varargsRadioButtons); |
| |
| // Set the class name text fields. |
| nameTextField.setText(name == null ? "*" : name); |
| |
| if (isField) |
| { |
| typeTextField .setText(descriptor == null ? "***" : ClassUtil.externalType(descriptor)); |
| } |
| else |
| { |
| typeTextField .setText(descriptor == null ? "***" : ClassUtil.externalMethodReturnType(descriptor)); |
| argumentTypesTextField.setText(descriptor == null ? "..." : ClassUtil.externalMethodArguments(descriptor)); |
| } |
| } |
| |
| |
| /** |
| * Returns the MemberSpecification currently represented in this dialog. |
| */ |
| public MemberSpecification getMemberSpecification() |
| { |
| String annotationType = annotationTypeTextField.getText(); |
| String name = nameTextField.getText(); |
| String type = typeTextField.getText(); |
| String arguments = argumentTypesTextField.getText(); |
| |
| // Convert all class member specifications into the internal format. |
| annotationType = |
| annotationType.equals("") || |
| annotationType.equals("***") ? null : ClassUtil.internalType(annotationType); |
| |
| if (name.equals("") || |
| name.equals("*")) |
| { |
| name = null; |
| } |
| |
| if (isField) |
| { |
| type = |
| type.equals("") || |
| type.equals("***") ? null : ClassUtil.internalType(type); |
| } |
| else |
| { |
| if (type.equals("")) |
| { |
| type = JavaConstants.TYPE_VOID; |
| } |
| |
| type = |
| type .equals("***") && |
| arguments.equals("...") ? null : |
| ClassUtil.internalMethodDescriptor(type, ListUtil.commaSeparatedList(arguments)); |
| } |
| |
| MemberSpecification memberSpecification = |
| new MemberSpecification(0, 0, annotationType, name, type); |
| |
| // Also get the access radio button settings. |
| getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_PUBLIC, publicRadioButtons); |
| getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_PRIVATE, privateRadioButtons); |
| getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_PROTECTED, protectedRadioButtons); |
| getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_STATIC, staticRadioButtons); |
| getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_FINAL, finalRadioButtons); |
| getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_SYNTHETIC, syntheticRadioButtons); |
| getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_VOLATILE, volatileRadioButtons); |
| getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_TRANSIENT, transientRadioButtons); |
| getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_SYNCHRONIZED, synchronizedRadioButtons); |
| getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_NATIVE, nativeRadioButtons); |
| getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_ABSTRACT, abstractRadioButtons); |
| getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_STRICT, strictRadioButtons); |
| getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_BRIDGE, bridgeRadioButtons); |
| getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_VARARGS, varargsRadioButtons); |
| |
| return memberSpecification; |
| } |
| |
| |
| /** |
| * Shows this dialog. This method only returns when the dialog is closed. |
| * |
| * @return <code>CANCEL_OPTION</code> or <code>APPROVE_OPTION</code>, |
| * depending on the choice of the user. |
| */ |
| public int showDialog() |
| { |
| returnValue = CANCEL_OPTION; |
| |
| // Open the dialog in the right place, then wait for it to be closed, |
| // one way or another. |
| pack(); |
| setLocationRelativeTo(getOwner()); |
| show(); |
| |
| return returnValue; |
| } |
| |
| |
| /** |
| * Sets the appropriate radio button of a given triplet, based on the access |
| * flags of the given keep option. |
| */ |
| private void setMemberSpecificationRadioButtons(MemberSpecification memberSpecification, |
| int flag, |
| JRadioButton[] radioButtons) |
| { |
| if (radioButtons != null) |
| { |
| int index = (memberSpecification.requiredSetAccessFlags & flag) != 0 ? 0 : |
| (memberSpecification.requiredUnsetAccessFlags & flag) != 0 ? 1 : |
| 2; |
| radioButtons[index].setSelected(true); |
| } |
| } |
| |
| |
| /** |
| * Updates the access flag of the given keep option, based on the given radio |
| * button triplet. |
| */ |
| private void getMemberSpecificationRadioButtons(MemberSpecification memberSpecification, |
| int flag, |
| JRadioButton[] radioButtons) |
| { |
| if (radioButtons != null) |
| { |
| if (radioButtons[0].isSelected()) |
| { |
| memberSpecification.requiredSetAccessFlags |= flag; |
| } |
| else if (radioButtons[1].isSelected()) |
| { |
| memberSpecification.requiredUnsetAccessFlags |= flag; |
| } |
| } |
| } |
| |
| |
| /** |
| * Attaches the tool tip from the GUI resources that corresponds to the |
| * given key, to the given component. |
| */ |
| private static JComponent tip(JComponent component, String messageKey) |
| { |
| component.setToolTipText(msg(messageKey)); |
| |
| return component; |
| } |
| |
| |
| /** |
| * Returns the message from the GUI resources that corresponds to the given |
| * key. |
| */ |
| private static String msg(String messageKey) |
| { |
| return GUIResources.getMessage(messageKey); |
| } |
| } |