blob: 9744e9eb2c60c6f1acf6cfd0967fc491aac1f378 [file] [log] [blame]
/*
* 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);
}
}