/*
* ProGuard -- shrinking, optimization, obfuscation, and preverification
* of Java bytecode.
*
* Copyright (c) 2002-2009 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 JDialog
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 CANCEL_OPTION
or APPROVE_OPTION
,
* 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);
}
}