/* * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * * Copyright (c) 2002-2010 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.*; import proguard.classfile.ClassConstants; import proguard.classfile.util.ClassUtil; import javax.swing.*; import javax.swing.border.*; import java.awt.*; import java.awt.event.*; import java.util.List; /** * This <code>JDialog</code> allows the user to enter a String. * * @author Eric Lafortune */ final class ClassSpecificationDialog 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 JTextArea commentsTextArea = new JTextArea(4, 20); private final JRadioButton keepClassesAndMembersRadioButton = new JRadioButton(msg("keep")); private final JRadioButton keepClassMembersRadioButton = new JRadioButton(msg("keepClassMembers")); private final JRadioButton keepClassesWithMembersRadioButton = new JRadioButton(msg("keepClassesWithMembers")); private final JCheckBox allowShrinkingCheckBox = new JCheckBox(msg("allowShrinking")); private final JCheckBox allowOptimizationCheckBox = new JCheckBox(msg("allowOptimization")); private final JCheckBox allowObfuscationCheckBox = new JCheckBox(msg("allowObfuscation")); private final JRadioButton[] publicRadioButtons; private final JRadioButton[] finalRadioButtons; private final JRadioButton[] abstractRadioButtons; private final JRadioButton[] enumRadioButtons; private final JRadioButton[] annotationRadioButtons; private final JRadioButton[] interfaceRadioButtons; private final JTextField annotationTypeTextField = new JTextField(20); private final JTextField classNameTextField = new JTextField(20); private final JTextField extendsAnnotationTypeTextField = new JTextField(20); private final JTextField extendsClassNameTextField = new JTextField(20); private final MemberSpecificationsPanel memberSpecificationsPanel; private int returnValue; public ClassSpecificationDialog(JFrame owner, boolean fullKeepOptions) { super(owner, msg("specifyClasses"), 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 = advancedButtonConstraints.insets; GridBagLayout layout = new GridBagLayout(); Border etchedBorder = BorderFactory.createEtchedBorder(EtchedBorder.RAISED); // Create the comments panel. JPanel commentsPanel = new JPanel(layout); commentsPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder, msg("comments"))); JScrollPane commentsScrollPane = new JScrollPane(commentsTextArea); commentsScrollPane.setBorder(classNameTextField.getBorder()); commentsPanel.add(tip(commentsScrollPane, "commentsTip"), constraintsLastStretch); // Create the keep option panel. ButtonGroup keepButtonGroup = new ButtonGroup(); keepButtonGroup.add(keepClassesAndMembersRadioButton); keepButtonGroup.add(keepClassMembersRadioButton); keepButtonGroup.add(keepClassesWithMembersRadioButton); JPanel keepOptionPanel = new JPanel(layout); keepOptionPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder, msg("keepTitle"))); keepOptionPanel.add(tip(keepClassesAndMembersRadioButton, "keepTip"), constraintsLastStretch); keepOptionPanel.add(tip(keepClassMembersRadioButton, "keepClassMembersTip"), constraintsLastStretch); keepOptionPanel.add(tip(keepClassesWithMembersRadioButton, "keepClassesWithMembersTip"), constraintsLastStretch); // Create the allow option panel. final JPanel allowOptionPanel = new JPanel(layout); allowOptionPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder, msg("allowTitle"))); allowOptionPanel.add(tip(allowShrinkingCheckBox, "allowShrinkingTip"), constraintsLastStretch); allowOptionPanel.add(tip(allowOptimizationCheckBox, "allowOptimizationTip"), constraintsLastStretch); allowOptionPanel.add(tip(allowObfuscationCheckBox, "allowObfuscationTip"), constraintsLastStretch); // 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); finalRadioButtons = addRadioButtonTriplet("Final", accessPanel); abstractRadioButtons = addRadioButtonTriplet("Abstract", accessPanel); enumRadioButtons = addRadioButtonTriplet("Enum", accessPanel); annotationRadioButtons = addRadioButtonTriplet("Annotation", accessPanel); interfaceRadioButtons = addRadioButtonTriplet("Interface", accessPanel); // 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 class name panel. JPanel classNamePanel = new JPanel(layout); classNamePanel.setBorder(BorderFactory.createTitledBorder(etchedBorder, msg("class"))); classNamePanel.add(tip(classNameTextField, "classNameTip"), constraintsLastStretch); // Create the extends annotation type panel. final JPanel extendsAnnotationTypePanel = new JPanel(layout); extendsAnnotationTypePanel.setBorder(BorderFactory.createTitledBorder(etchedBorder, msg("extendsImplementsAnnotation"))); extendsAnnotationTypePanel.add(tip(extendsAnnotationTypeTextField, "classNameTip"), constraintsLastStretch); // Create the extends class name panel. JPanel extendsClassNamePanel = new JPanel(layout); extendsClassNamePanel.setBorder(BorderFactory.createTitledBorder(etchedBorder, msg("extendsImplementsClass"))); extendsClassNamePanel.add(tip(extendsClassNameTextField, "classNameTip"), constraintsLastStretch); // Create the class member list panel. memberSpecificationsPanel = new MemberSpecificationsPanel(this, fullKeepOptions); memberSpecificationsPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder, msg("classMembers"))); // Create the Advanced button. final JButton advancedButton = new JButton(msg("basic")); advancedButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { boolean visible = !allowOptionPanel.isVisible(); allowOptionPanel .setVisible(visible); annotationTypePanel .setVisible(visible); extendsAnnotationTypePanel.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(commentsPanel, "commentsTip"), panelConstraints); if (fullKeepOptions) { mainPanel.add(tip(keepOptionPanel, "keepTitleTip"), panelConstraints); mainPanel.add(tip(allowOptionPanel, "allowTitleTip"), panelConstraints); } mainPanel.add(tip(accessPanel, "accessTip"), panelConstraints); mainPanel.add(tip(annotationTypePanel, "annotationTip"), panelConstraints); mainPanel.add(tip(classNamePanel, "classTip"), panelConstraints); mainPanel.add(tip(extendsAnnotationTypePanel, "extendsImplementsAnnotationTip"), panelConstraints); mainPanel.add(tip(extendsClassNamePanel, "extendsImplementsClassTip"), panelConstraints); mainPanel.add(tip(memberSpecificationsPanel, "classMembersTip"), stretchPanelConstraints); 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 KeepClassSpecification to be represented in this dialog. */ public void setKeepSpecification(KeepClassSpecification keepClassSpecification) { boolean markClasses = keepClassSpecification.markClasses; boolean markConditionally = keepClassSpecification.markConditionally; boolean allowShrinking = keepClassSpecification.allowShrinking; boolean allowOptimization = keepClassSpecification.allowOptimization; boolean allowObfuscation = keepClassSpecification.allowObfuscation; // Figure out the proper keep radio button and set it. JRadioButton keepOptionRadioButton = markConditionally ? keepClassesWithMembersRadioButton : markClasses ? keepClassesAndMembersRadioButton : keepClassMembersRadioButton; keepOptionRadioButton.setSelected(true); // Set the allow radio buttons. allowShrinkingCheckBox .setSelected(allowShrinking); allowOptimizationCheckBox.setSelected(allowOptimization); allowObfuscationCheckBox .setSelected(allowObfuscation); setClassSpecification(keepClassSpecification); } /** * Sets the ClassSpecification to be represented in this dialog. */ public void setClassSpecification(ClassSpecification classSpecification) { String comments = classSpecification.comments; String annotationType = classSpecification.annotationType; String className = classSpecification.className; String extendsAnnotationType = classSpecification.extendsAnnotationType; String extendsClassName = classSpecification.extendsClassName; List keepFieldOptions = classSpecification.fieldSpecifications; List keepMethodOptions = classSpecification.methodSpecifications; // Set the comments text area. commentsTextArea.setText(comments == null ? "" : comments); // Set the access radio buttons. setClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_PUBLIC, publicRadioButtons); setClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_FINAL, finalRadioButtons); setClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_ABSTRACT, abstractRadioButtons); setClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_ENUM, enumRadioButtons); setClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_ANNOTATTION, annotationRadioButtons); setClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_INTERFACE, interfaceRadioButtons); // Set the class and annotation text fields. annotationTypeTextField .setText(annotationType == null ? "" : ClassUtil.externalType(annotationType)); classNameTextField .setText(className == null ? "*" : ClassUtil.externalClassName(className)); extendsAnnotationTypeTextField.setText(extendsAnnotationType == null ? "" : ClassUtil.externalType(extendsAnnotationType)); extendsClassNameTextField .setText(extendsClassName == null ? "" : ClassUtil.externalClassName(extendsClassName)); // Set the keep class member option list. memberSpecificationsPanel.setMemberSpecifications(keepFieldOptions, keepMethodOptions); } /** * Returns the KeepClassSpecification currently represented in this dialog. */ public KeepClassSpecification getKeepSpecification() { boolean markClasses = !keepClassMembersRadioButton .isSelected(); boolean markConditionally = keepClassesWithMembersRadioButton.isSelected(); boolean allowShrinking = allowShrinkingCheckBox .isSelected(); boolean allowOptimization = allowOptimizationCheckBox .isSelected(); boolean allowObfuscation = allowObfuscationCheckBox .isSelected(); return new KeepClassSpecification(markClasses, markConditionally, allowShrinking, allowOptimization, allowObfuscation, getClassSpecification()); } /** * Returns the ClassSpecification currently represented in this dialog. */ public ClassSpecification getClassSpecification() { String comments = commentsTextArea.getText(); String annotationType = annotationTypeTextField.getText(); String className = classNameTextField.getText(); String extendsAnnotationType = extendsAnnotationTypeTextField.getText(); String extendsClassName = extendsClassNameTextField.getText(); ClassSpecification classSpecification = new ClassSpecification(comments.equals("") ? null : comments, 0, 0, annotationType.equals("") ? null : ClassUtil.internalType(annotationType), className.equals("") || className.equals("*") ? null : ClassUtil.internalClassName(className), extendsAnnotationType.equals("") ? null : ClassUtil.internalType(extendsAnnotationType), extendsClassName.equals("") ? null : ClassUtil.internalClassName(extendsClassName)); // Also get the access radio button settings. getClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_PUBLIC, publicRadioButtons); getClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_FINAL, finalRadioButtons); getClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_ABSTRACT, abstractRadioButtons); getClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_ENUM, enumRadioButtons); getClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_ANNOTATTION, annotationRadioButtons); getClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_INTERFACE, interfaceRadioButtons); // Get the keep class member option lists. classSpecification.fieldSpecifications = memberSpecificationsPanel.getMemberSpecifications(true); classSpecification.methodSpecifications = memberSpecificationsPanel.getMemberSpecifications(false); return classSpecification; } /** * 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 setClassSpecificationRadioButtons(ClassSpecification classSpecification, int flag, JRadioButton[] radioButtons) { int index = (classSpecification.requiredSetAccessFlags & flag) != 0 ? 0 : (classSpecification.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 getClassSpecificationRadioButtons(ClassSpecification classSpecification, int flag, JRadioButton[] radioButtons) { if (radioButtons[0].isSelected()) { classSpecification.requiredSetAccessFlags |= flag; } else if (radioButtons[1].isSelected()) { classSpecification.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); } }