diff options
author | Joe Onorato <joeo@android.com> | 2009-08-31 10:12:00 -0700 |
---|---|---|
committer | Joe Onorato <joeo@android.com> | 2009-08-31 10:12:00 -0700 |
commit | b72c5c2e5482cf10117b2b25f642f7616b2326c3 (patch) | |
tree | f02ba1bc29f4fe6853d9b7008eed37cdcfb96e81 /src/proguard/gui | |
parent | a23344a828357fe4b6596f8af5fed467d72757ab (diff) | |
download | proguard-b72c5c2e5482cf10117b2b25f642f7616b2326c3.tar.gz |
ProGuard 4.4android-sdk-tools_r4android-sdk-tools_r3android-sdk-2.1_r1android-sdk-2.0_r1android-sdk-2.0.1_r1android-sdk-2.0.1-docs_r1android-2.1_r2.1sandroid-2.1_r2.1p2android-2.1_r2.1pandroid-2.1_r2android-2.1_r1android-2.0_r1android-2.0.1_r1eclair-sholes-release2eclair-sholes-releaseeclair-releaseeclair-passion-release
Diffstat (limited to 'src/proguard/gui')
62 files changed, 10123 insertions, 0 deletions
diff --git a/src/proguard/gui/ClassPathPanel.java b/src/proguard/gui/ClassPathPanel.java new file mode 100644 index 0000000..95f3d1b --- /dev/null +++ b/src/proguard/gui/ClassPathPanel.java @@ -0,0 +1,441 @@ +/* + * 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.util.ListUtil; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.io.File; +import java.util.List; + +/** + * This <code>ListPanel</code> allows the user to add, edit, filter, move, and + * remove ClassPathEntry objects in a ClassPath object. + * + * @author Eric Lafortune + */ +class ClassPathPanel extends ListPanel +{ + private final JFrame owner; + private final boolean inputAndOutput; + private final JFileChooser chooser; + private final FilterDialog filterDialog; + + + public ClassPathPanel(JFrame owner, boolean inputAndOutput) + { + super(); + + super.firstSelectionButton = inputAndOutput ? 3 : 2; + + this.owner = owner; + this.inputAndOutput = inputAndOutput; + + list.setCellRenderer(new MyListCellRenderer()); + + chooser = new JFileChooser(""); + chooser.setMultiSelectionEnabled(true); + chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); + chooser.addChoosableFileFilter( + new ExtensionFileFilter(msg("jarWarEarZipExtensions"), + new String[] { ".jar", ".war", ".ear", ".zip" })); + chooser.setApproveButtonText(msg("ok")); + + filterDialog = new FilterDialog(owner, msg("enterFilter")); + + addAddButton(inputAndOutput, false); + if (inputAndOutput) + { + addAddButton(inputAndOutput, true); + } + addEditButton(); + addFilterButton(); + addRemoveButton(); + addUpButton(); + addDownButton(); + + enableSelectionButtons(); + } + + + protected void addAddButton(boolean inputAndOutput, + final boolean isOutput) + { + JButton addButton = new JButton(msg(inputAndOutput ? + isOutput ? "addOutput" : + "addInput" : + "add")); + addButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + chooser.setDialogTitle(msg("addJars")); + chooser.setSelectedFile(null); + chooser.setSelectedFiles(null); + + int returnValue = chooser.showOpenDialog(owner); + if (returnValue == JFileChooser.APPROVE_OPTION) + { + File[] selectedFiles = chooser.getSelectedFiles(); + ClassPathEntry[] entries = classPathEntries(selectedFiles, isOutput); + + // Add the new elements. + addElements(entries); + } + } + }); + + addButton(tip(addButton, inputAndOutput ? + isOutput ? "addOutputTip" : + "addInputTip" : + "addTip")); + } + + + protected void addEditButton() + { + JButton editButton = new JButton(msg("edit")); + editButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + boolean isOutput = false; + + int[] selectedIndices = list.getSelectedIndices(); + + // Copy the Object array into a File array. + File[] selectedFiles = new File[selectedIndices.length]; + for (int index = 0; index < selectedFiles.length; index++) + { + ClassPathEntry entry = + (ClassPathEntry)listModel.getElementAt(selectedIndices[index]); + + isOutput = entry.isOutput(); + + selectedFiles[index] = entry.getFile(); + } + + chooser.setDialogTitle(msg("chooseJars")); + + // Up to JDK 1.3.1, setSelectedFiles doesn't show in the file + // chooser, so we just use setSelectedFile first. It also sets + // the current directory. + chooser.setSelectedFile(selectedFiles[0]); + chooser.setSelectedFiles(selectedFiles); + + int returnValue = chooser.showOpenDialog(owner); + if (returnValue == JFileChooser.APPROVE_OPTION) + { + selectedFiles = chooser.getSelectedFiles(); + ClassPathEntry[] entries = classPathEntries(selectedFiles, isOutput); + + // If there are the same number of files selected now as + // there were before, we can just replace the old ones. + if (selectedIndices.length == selectedFiles.length) + { + // Replace the old elements. + setElementsAt(entries, selectedIndices); + } + else + { + // Remove the old elements. + removeElementsAt(selectedIndices); + + // Add the new elements. + addElements(entries); + } + } + } + }); + + addButton(tip(editButton, "editTip")); + } + + + protected void addFilterButton() + { + JButton filterButton = new JButton(msg("filter")); + filterButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + if (!list.isSelectionEmpty()) + { + int[] selectedIndices = list.getSelectedIndices(); + + // Put the filters of the first selected entry in the dialog. + getFiltersFrom(selectedIndices[0]); + + int returnValue = filterDialog.showDialog(); + if (returnValue == FilterDialog.APPROVE_OPTION) + { + // Apply the entered filters to all selected entries. + setFiltersAt(selectedIndices); + } + } + } + }); + + addButton(tip(filterButton, "filterTip")); + } + + + /** + * Sets the ClassPath to be represented in this panel. + */ + public void setClassPath(ClassPath classPath) + { + listModel.clear(); + + if (classPath != null) + { + for (int index = 0; index < classPath.size(); index++) + { + listModel.addElement(classPath.get(index)); + } + } + + // Make sure the selection buttons are properly enabled, + // since the clear method doesn't seem to notify the listener. + enableSelectionButtons(); + } + + + /** + * Returns the ClassPath currently represented in this panel. + */ + public ClassPath getClassPath() + { + int size = listModel.size(); + if (size == 0) + { + return null; + } + + ClassPath classPath = new ClassPath(); + for (int index = 0; index < size; index++) + { + classPath.add((ClassPathEntry)listModel.get(index)); + } + + return classPath; + } + + + /** + * Converts the given array of File objects into a corresponding array of + * ClassPathEntry objects. + */ + private ClassPathEntry[] classPathEntries(File[] files, boolean isOutput) + { + ClassPathEntry[] entries = new ClassPathEntry[files.length]; + for (int index = 0; index < entries.length; index++) + { + entries[index] = new ClassPathEntry(files[index], isOutput); + } + return entries; + } + + + /** + * Sets up the filter dialog with the filters from the specified class path + * entry. + */ + private void getFiltersFrom(int index) + { + ClassPathEntry firstEntry = (ClassPathEntry)listModel.get(index); + + filterDialog.setFilter(firstEntry.getFilter()); + filterDialog.setJarFilter(firstEntry.getJarFilter()); + filterDialog.setWarFilter(firstEntry.getWarFilter()); + filterDialog.setEarFilter(firstEntry.getEarFilter()); + filterDialog.setZipFilter(firstEntry.getZipFilter()); + } + + + /** + * Applies the entered filter to the specified class path entries. + * Any previously set filters are discarded. + */ + private void setFiltersAt(int[] indices) + { + for (int index = indices.length - 1; index >= 0; index--) + { + ClassPathEntry entry = (ClassPathEntry)listModel.get(indices[index]); + entry.setFilter(filterDialog.getFilter()); + entry.setJarFilter(filterDialog.getJarFilter()); + entry.setWarFilter(filterDialog.getWarFilter()); + entry.setEarFilter(filterDialog.getEarFilter()); + entry.setZipFilter(filterDialog.getZipFilter()); + } + + // Make sure they are selected and thus repainted. + list.setSelectedIndices(indices); + } + + + /** + * 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); + } + + + /** + * This ListCellRenderer renders ClassPathEntry objects. + */ + private class MyListCellRenderer implements ListCellRenderer + { + private static final String ARROW_IMAGE_FILE = "arrow.gif"; + + private final JPanel cellPanel = new JPanel(new GridBagLayout()); + private final JLabel iconLabel = new JLabel("", JLabel.RIGHT); + private final JLabel jarNameLabel = new JLabel("", JLabel.RIGHT); + private final JLabel filterLabel = new JLabel("", JLabel.RIGHT); + + private final Icon arrowIcon; + + + public MyListCellRenderer() + { + GridBagConstraints jarNameLabelConstraints = new GridBagConstraints(); + jarNameLabelConstraints.anchor = GridBagConstraints.WEST; + jarNameLabelConstraints.insets = new Insets(1, 2, 1, 2); + + GridBagConstraints filterLabelConstraints = new GridBagConstraints(); + filterLabelConstraints.gridwidth = GridBagConstraints.REMAINDER; + filterLabelConstraints.fill = GridBagConstraints.HORIZONTAL; + filterLabelConstraints.weightx = 1.0; + filterLabelConstraints.anchor = GridBagConstraints.EAST; + filterLabelConstraints.insets = jarNameLabelConstraints.insets; + + arrowIcon = new ImageIcon(Toolkit.getDefaultToolkit().getImage(this.getClass().getResource(ARROW_IMAGE_FILE))); + + cellPanel.add(iconLabel, jarNameLabelConstraints); + cellPanel.add(jarNameLabel, jarNameLabelConstraints); + cellPanel.add(filterLabel, filterLabelConstraints); + } + + + // Implementations for ListCellRenderer. + + public Component getListCellRendererComponent(JList list, + Object value, + int index, + boolean isSelected, + boolean cellHasFocus) + { + ClassPathEntry entry = (ClassPathEntry)value; + + // Prepend an arrow to the output entries. + if (inputAndOutput && entry.isOutput()) + { + iconLabel.setIcon(arrowIcon); + } + else + { + iconLabel.setIcon(null); + } + + // Set the entry name text. + jarNameLabel.setText(entry.getName()); + + // Set the filter text. + StringBuffer filter = null; + filter = appendFilter(filter, entry.getZipFilter()); + filter = appendFilter(filter, entry.getEarFilter()); + filter = appendFilter(filter, entry.getWarFilter()); + filter = appendFilter(filter, entry.getJarFilter()); + filter = appendFilter(filter, entry.getFilter()); + + if (filter != null) + { + filter.append(')'); + } + + filterLabel.setText(filter != null ? filter.toString() : ""); + + // Set the colors. + if (isSelected) + { + cellPanel.setBackground(list.getSelectionBackground()); + jarNameLabel.setForeground(list.getSelectionForeground()); + filterLabel.setForeground(list.getSelectionForeground()); + } + else + { + cellPanel.setBackground(list.getBackground()); + jarNameLabel.setForeground(list.getForeground()); + filterLabel.setForeground(list.getForeground()); + } + + // Make the font color red if this is an input file that can't be read. + if (!(inputAndOutput && entry.isOutput()) && + !entry.getFile().canRead()) + { + jarNameLabel.setForeground(Color.red); + } + + cellPanel.setOpaque(true); + + return cellPanel; + } + + + private StringBuffer appendFilter(StringBuffer filter, List additionalFilter) + { + if (filter != null) + { + filter.append(';'); + } + + if (additionalFilter != null) + { + if (filter == null) + { + filter = new StringBuffer().append('('); + } + + filter.append(ListUtil.commaSeparatedString(additionalFilter)); + } + + return filter; + } + } +} diff --git a/src/proguard/gui/ClassSpecificationDialog.java b/src/proguard/gui/ClassSpecificationDialog.java new file mode 100644 index 0000000..36d80d4 --- /dev/null +++ b/src/proguard/gui/ClassSpecificationDialog.java @@ -0,0 +1,542 @@ +/* + * 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 <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); + } +} diff --git a/src/proguard/gui/ClassSpecificationsPanel.java b/src/proguard/gui/ClassSpecificationsPanel.java new file mode 100644 index 0000000..2cf0b1d --- /dev/null +++ b/src/proguard/gui/ClassSpecificationsPanel.java @@ -0,0 +1,231 @@ +/* + * 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.ClassSpecification; +import proguard.classfile.util.ClassUtil; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import java.util.List; + + +/** + * This <code>ListPanel</code> allows the user to add, edit, move, and remove + * ClassSpecification entries in a list. + * + * @author Eric Lafortune + */ +class ClassSpecificationsPanel extends ListPanel +{ + protected final ClassSpecificationDialog classSpecificationDialog; + + + public ClassSpecificationsPanel(JFrame owner, boolean fullKeepOptions) + { + super(); + + list.setCellRenderer(new MyListCellRenderer()); + + classSpecificationDialog = new ClassSpecificationDialog(owner, fullKeepOptions); + + addAddButton(); + addEditButton(); + addRemoveButton(); + addUpButton(); + addDownButton(); + + enableSelectionButtons(); + } + + + protected void addAddButton() + { + JButton addButton = new JButton(msg("add")); + addButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + setClassSpecification(createClassSpecification()); + int returnValue = classSpecificationDialog.showDialog(); + if (returnValue == ClassSpecificationDialog.APPROVE_OPTION) + { + // Add the new element. + addElement(getClassSpecification()); + } + } + }); + + addButton(tip(addButton, "addTip")); + } + + + protected void addEditButton() + { + JButton editButton = new JButton(msg("edit")); + editButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + ClassSpecification selectedClassSpecification = + (ClassSpecification)list.getSelectedValue(); + + setClassSpecification(selectedClassSpecification); + int returnValue = classSpecificationDialog.showDialog(); + if (returnValue == ClassSpecificationDialog.APPROVE_OPTION) + { + // Replace the old element. + setElementAt(getClassSpecification(), + list.getSelectedIndex()); + } + } + }); + + addButton(tip(editButton, "editTip")); + } + + + protected ClassSpecification createClassSpecification() + { + return new ClassSpecification(); + } + + + protected void setClassSpecification(ClassSpecification classSpecification) + { + classSpecificationDialog.setClassSpecification(classSpecification); + } + + + protected ClassSpecification getClassSpecification() + { + return classSpecificationDialog.getClassSpecification(); + } + + + /** + * Sets the ClassSpecification objects to be represented in this panel. + */ + public void setClassSpecifications(List classSpecifications) + { + listModel.clear(); + + if (classSpecifications != null) + { + for (int index = 0; index < classSpecifications.size(); index++) + { + listModel.addElement(classSpecifications.get(index)); + } + } + + // Make sure the selection buttons are properly enabled, + // since the clear method doesn't seem to notify the listener. + enableSelectionButtons(); + } + + + /** + * Returns the ClassSpecification objects currently represented in this panel. + */ + public List getClassSpecifications() + { + int size = listModel.size(); + if (size == 0) + { + return null; + } + + List classSpecifications = new ArrayList(size); + for (int index = 0; index < size; index++) + { + classSpecifications.add(listModel.get(index)); + } + + return classSpecifications; + } + + + /** + * 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); + } + + + /** + * This ListCellRenderer renders ClassSpecification objects. + */ + private static class MyListCellRenderer implements ListCellRenderer + { + private final JLabel label = new JLabel(); + + + // Implementations for ListCellRenderer. + + public Component getListCellRendererComponent(JList list, + Object value, + int index, + boolean isSelected, + boolean cellHasFocus) + { + ClassSpecification classSpecification = (ClassSpecification)value; + + String comments = classSpecification.comments; + + label.setText(comments != null ? comments.trim() : + classSpecification.className != null ? (msg("class") + ' ' + ClassUtil.externalClassName(classSpecification.className)) : + classSpecification.extendsClassName != null ? (msg("extensionsOf") + ' ' + ClassUtil.externalClassName(classSpecification.extendsClassName)) : + (msg("specificationNumber") + index)); + + if (isSelected) + { + label.setBackground(list.getSelectionBackground()); + label.setForeground(list.getSelectionForeground()); + } + else + { + label.setBackground(list.getBackground()); + label.setForeground(list.getForeground()); + } + + label.setOpaque(true); + + return label; + } + } +} diff --git a/src/proguard/gui/ExtensionFileFilter.java b/src/proguard/gui/ExtensionFileFilter.java new file mode 100644 index 0000000..d67be40 --- /dev/null +++ b/src/proguard/gui/ExtensionFileFilter.java @@ -0,0 +1,78 @@ +/* + * 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 javax.swing.filechooser.FileFilter; +import java.io.File; + + +/** + * This <code>FileFilter</code> accepts files that end in one of the given + * extensions. + * + * @author Eric Lafortune + */ +final class ExtensionFileFilter extends FileFilter +{ + private final String description; + private final String[] extensions; + + + /** + * Creates a new ExtensionFileFilter. + * @param description a description of the filter. + * @param extensions an array of acceptable extensions. + */ + public ExtensionFileFilter(String description, String[] extensions) + { + this.description = description; + this.extensions = extensions; + } + + + // Implemntations for FileFilter + + public String getDescription() + { + return description; + } + + + public boolean accept(File file) + { + if (file.isDirectory()) + { + return true; + } + + String fileName = file.getName().toLowerCase(); + + for (int index = 0; index < extensions.length; index++) + { + if (fileName.endsWith(extensions[index])) + { + return true; + } + } + + return false; + } +} diff --git a/src/proguard/gui/FilterBuilder.java b/src/proguard/gui/FilterBuilder.java new file mode 100644 index 0000000..e46193f --- /dev/null +++ b/src/proguard/gui/FilterBuilder.java @@ -0,0 +1,208 @@ +/* + * 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 javax.swing.*; + +/** + * This class builds filters corresponding to the selections and names of a + * given list of check boxes. + */ +public class FilterBuilder +{ + private JCheckBox[] checkBoxes; + private char separator; + + + /** + * Creates a new FilterBuilder. + * @param checkBoxes the check boxes with names and selections that should + * be reflected in the output filter. + * @param separator the separator for the names in the check boxes. + */ + public FilterBuilder(JCheckBox[] checkBoxes, char separator) + { + this.checkBoxes = checkBoxes; + this.separator = separator; + } + + + /** + * Builds a filter for the current names and selections of the check boxes. + */ + public String buildFilter() + { + StringBuffer positive = new StringBuffer(); + StringBuffer negative = new StringBuffer(); + + buildFilter("", positive, negative); + + return positive.length() <= negative.length() ? + positive.toString() : + negative.toString(); + } + + + /** + * Builds two versions of the filter for the given prefix. + * @param prefix the prefix. + * @param positive the filter to be extended, assuming the matching + * strings are accepted. + * @param negative the filter to be extended, assuming the matching + * strings are rejected. + */ + private void buildFilter(String prefix, + StringBuffer positive, + StringBuffer negative) + { + int positiveCount = 0; + int negativeCount = 0; + + // Count all selected and unselected check boxes with the prefix. + for (int index = 0; index < checkBoxes.length; index++) + { + JCheckBox checkBox = checkBoxes[index]; + String name = checkBox.getText(); + + if (name.startsWith(prefix)) + { + if (checkBox.isSelected()) + { + positiveCount++; + } + else + { + negativeCount++; + } + } + } + + // Are there only unselected check boxes? + if (positiveCount == 0) + { + // Extend the positive filter with exceptions and return. + if (positive.length() > 0) + { + positive.append(','); + } + positive.append('!').append(prefix); + if (prefix.length() == 0 || + prefix.charAt(prefix.length()-1) == separator) + { + positive.append('*'); + } + + return; + } + + // Are there only selected check boxes? + if (negativeCount == 0) + { + // Extend the negative filter with exceptions and return. + if (negative.length() > 0) + { + negative.append(','); + } + negative.append(prefix); + if (prefix.length() == 0 || + prefix.charAt(prefix.length()-1) == separator) + { + negative.append('*'); + } + + return; + } + + // Create new positive and negative filters for names starting with the + // prefix only. + StringBuffer positiveFilter = new StringBuffer(); + StringBuffer negativeFilter = new StringBuffer(); + + String newPrefix = null; + + for (int index = 0; index < checkBoxes.length; index++) + { + String name = checkBoxes[index].getText(); + + if (name.startsWith(prefix)) + { + if (newPrefix == null || + !name.startsWith(newPrefix)) + { + int prefixIndex = + name.indexOf(separator, prefix.length()+1); + + newPrefix = prefixIndex >= 0 ? + name.substring(0, prefixIndex+1) : + name; + + buildFilter(newPrefix, + positiveFilter, + negativeFilter); + } + } + } + + // Extend the positive filter. + if (positiveFilter.length() <= negativeFilter.length() + prefix.length() + 3) + { + if (positive.length() > 0 && + positiveFilter.length() > 0) + { + positive.append(','); + } + + positive.append(positiveFilter); + } + else + { + if (positive.length() > 0 && + negativeFilter.length() > 0) + { + positive.append(','); + } + + positive.append(negativeFilter).append(",!").append(prefix).append('*'); + } + + // Extend the negative filter. + if (negativeFilter.length() <= positiveFilter.length() + prefix.length() + 4) + { + if (negative.length() > 0 && + negativeFilter.length() > 0) + { + negative.append(','); + } + + negative.append(negativeFilter); + } + else + { + if (negative.length() > 0 && + positiveFilter.length() > 0) + { + negative.append(','); + } + + negative.append(positiveFilter).append(',').append(prefix).append('*'); + } + } +} diff --git a/src/proguard/gui/FilterDialog.java b/src/proguard/gui/FilterDialog.java new file mode 100644 index 0000000..1567a31 --- /dev/null +++ b/src/proguard/gui/FilterDialog.java @@ -0,0 +1,320 @@ +/* + * 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.util.ListUtil; + +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 + */ +public class FilterDialog 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 static final String DEFAULT_FILTER = "**"; + private static final String DEFAULT_JAR_FILTER = "**.jar"; + private static final String DEFAULT_WAR_FILTER = "**.war"; + private static final String DEFAULT_EAR_FILTER = "**.ear"; + private static final String DEFAULT_ZIP_FILTER = "**.zip"; + + + private final JTextField filterTextField = new JTextField(40); + private final JTextField jarFilterTextField = new JTextField(40); + private final JTextField warFilterTextField = new JTextField(40); + private final JTextField earFilterTextField = new JTextField(40); + private final JTextField zipFilterTextField = new JTextField(40); + private int returnValue; + + + public FilterDialog(JFrame owner, + String explanation) + { + super(owner, true); + setResizable(true); + + // Create some constraints that can be reused. + GridBagConstraints textConstraints = new GridBagConstraints(); + textConstraints.gridwidth = GridBagConstraints.REMAINDER; + textConstraints.fill = GridBagConstraints.HORIZONTAL; + textConstraints.weightx = 1.0; + textConstraints.weighty = 1.0; + textConstraints.anchor = GridBagConstraints.NORTHWEST; + textConstraints.insets = new Insets(10, 10, 10, 10); + + GridBagConstraints labelConstraints = new GridBagConstraints(); + labelConstraints.anchor = GridBagConstraints.WEST; + labelConstraints.insets = new Insets(1, 2, 1, 2); + + GridBagConstraints textFieldConstraints = new GridBagConstraints(); + textFieldConstraints.gridwidth = GridBagConstraints.REMAINDER; + textFieldConstraints.fill = GridBagConstraints.HORIZONTAL; + textFieldConstraints.weightx = 1.0; + textFieldConstraints.anchor = GridBagConstraints.WEST; + textFieldConstraints.insets = labelConstraints.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 = labelConstraints.insets; + + GridBagConstraints okButtonConstraints = new GridBagConstraints(); + okButtonConstraints.weightx = 1.0; + okButtonConstraints.weighty = 1.0; + okButtonConstraints.anchor = GridBagConstraints.SOUTHEAST; + okButtonConstraints.insets = new Insets(4, 4, 8, 4); + + 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); + + // Create the panel with the explanation. + JTextArea explanationTextArea = new JTextArea(explanation, 3, 0); + explanationTextArea.setOpaque(false); + explanationTextArea.setEditable(false); + explanationTextArea.setLineWrap(true); + explanationTextArea.setWrapStyleWord(true); + + // Create the filter labels. + JLabel filterLabel = new JLabel(msg("nameFilter")); + JLabel jarFilterLabel = new JLabel(msg("jarNameFilter")); + JLabel warFilterLabel = new JLabel(msg("warNameFilter")); + JLabel earFilterLabel = new JLabel(msg("earNameFilter")); + JLabel zipFilterLabel = new JLabel(msg("zipNameFilter")); + + // Create the filter panel. + JPanel filterPanel = new JPanel(layout); + filterPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder, + msg("filters"))); + + filterPanel.add(explanationTextArea, textConstraints); + + filterPanel.add(tip(filterLabel, "nameFilterTip"), labelConstraints); + filterPanel.add(tip(filterTextField, "fileNameFilterTip"), textFieldConstraints); + + filterPanel.add(tip(jarFilterLabel, "jarNameFilterTip"), labelConstraints); + filterPanel.add(tip(jarFilterTextField, "fileNameFilterTip"), textFieldConstraints); + + filterPanel.add(tip(warFilterLabel, "warNameFilterTip"), labelConstraints); + filterPanel.add(tip(warFilterTextField, "fileNameFilterTip"), textFieldConstraints); + + filterPanel.add(tip(earFilterLabel, "earNameFilterTip"), labelConstraints); + filterPanel.add(tip(earFilterTextField, "fileNameFilterTip"), textFieldConstraints); + + filterPanel.add(tip(zipFilterLabel, "zipNameFilterTip"), labelConstraints); + filterPanel.add(tip(zipFilterTextField, "fileNameFilterTip"), textFieldConstraints); + + + JButton okButton = new JButton(msg("ok")); + okButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + returnValue = APPROVE_OPTION; + hide(); + } + }); + + 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(filterPanel, panelConstraints); + mainPanel.add(okButton, okButtonConstraints); + mainPanel.add(cancelButton, cancelButtonConstraints); + + getContentPane().add(mainPanel); + } + + + /** + * Sets the filter to be represented in this dialog. + */ + public void setFilter(List filter) + { + filterTextField.setText(filter != null ? ListUtil.commaSeparatedString(filter) : DEFAULT_FILTER); + } + + + /** + * Returns the filter currently represented in this dialog. + */ + public List getFilter() + { + String filter = filterTextField.getText(); + + return filter.equals(DEFAULT_FILTER) ? null : ListUtil.commaSeparatedList(filter); + } + + + /** + * Sets the jar filter to be represented in this dialog. + */ + public void setJarFilter(List filter) + { + jarFilterTextField.setText(filter != null ? ListUtil.commaSeparatedString(filter) : DEFAULT_JAR_FILTER); + } + + + /** + * Returns the jar filter currently represented in this dialog. + */ + public List getJarFilter() + { + String filter = jarFilterTextField.getText(); + + return filter.equals(DEFAULT_JAR_FILTER) ? null : ListUtil.commaSeparatedList(filter); + } + + + /** + * Sets the war filter to be represented in this dialog. + */ + public void setWarFilter(List filter) + { + warFilterTextField.setText(filter != null ? ListUtil.commaSeparatedString(filter) : DEFAULT_WAR_FILTER); + } + + + /** + * Returns the war filter currently represented in this dialog. + */ + public List getWarFilter() + { + String filter = warFilterTextField.getText(); + + return filter.equals(DEFAULT_WAR_FILTER) ? null : ListUtil.commaSeparatedList(filter); + } + + + /** + * Sets the ear filter to be represented in this dialog. + */ + public void setEarFilter(List filter) + { + earFilterTextField.setText(filter != null ? ListUtil.commaSeparatedString(filter) : DEFAULT_EAR_FILTER); + } + + + /** + * Returns the ear filter currently represented in this dialog. + */ + public List getEarFilter() + { + String filter = earFilterTextField.getText(); + + return filter.equals(DEFAULT_EAR_FILTER) ? null : ListUtil.commaSeparatedList(filter); + } + + + /** + * Sets the zip filter to be represented in this dialog. + */ + public void setZipFilter(List filter) + { + zipFilterTextField.setText(filter != null ? ListUtil.commaSeparatedString(filter) : DEFAULT_ZIP_FILTER); + } + + + /** + * Returns the zip filter currently represented in this dialog. + */ + public List getZipFilter() + { + String filter = zipFilterTextField.getText(); + + return filter.equals(DEFAULT_ZIP_FILTER) ? null : ListUtil.commaSeparatedList(filter); + } + + + /** + * 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; + } + + + /** + * 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); + } +} diff --git a/src/proguard/gui/GUIResources.java b/src/proguard/gui/GUIResources.java new file mode 100644 index 0000000..85d582c --- /dev/null +++ b/src/proguard/gui/GUIResources.java @@ -0,0 +1,56 @@ +/* + * 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 java.text.MessageFormat; +import java.util.ResourceBundle; + + +/** + * This class provides some utility methods for working with resource bundles. + * + * @author Eric Lafortune + */ +class GUIResources +{ + private static final ResourceBundle messages = ResourceBundle.getBundle(GUIResources.class.getName()); + private static final MessageFormat formatter = new MessageFormat(""); + + + /** + * Returns an internationalized message, based on its key. + */ + public static String getMessage(String messageKey) + { + return messages.getString(messageKey); + } + + + /** + * Returns an internationalized, formatted message, based on its key, with + * the given arguments. + */ + public static String getMessage(String messageKey, Object[] messageArguments) + { + formatter.applyPattern(messages.getString(messageKey)); + return formatter.format(messageArguments); + } +} diff --git a/src/proguard/gui/GUIResources.properties b/src/proguard/gui/GUIResources.properties new file mode 100644 index 0000000..86ab7a1 --- /dev/null +++ b/src/proguard/gui/GUIResources.properties @@ -0,0 +1,631 @@ +# ProGuard -- shrinking, optimization, and obfuscation of Java class files. +# Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) + +# +# Tab names. +# +proGuardTab = ProGuard +inputOutputTab = Input/Output +shrinkingTab = Shrinking +obfuscationTab = Obfuscation +optimizationTab = Optimization +informationTab = Information +processTab = Process +reTraceTab = ReTrace + +# +# Splash text. +# +developed = Developed by Eric Lafortune +shrinking = Shrinking +optimization = Optimization +obfuscation = Obfuscation +preverification = Preverification + +# +# Panel titles. +# +welcome = Welcome to ProGuard, version 4.4 +options = Options +keepAdditional = Keep additional classes and class members +keepNamesAdditional = Keep additional class names and class member names +assumeNoSideEffectsAdditional = Assume no side effects for additional methods +whyAreYouKeeping = Why are you keeping +preverificationAndTargeting = Preverification and targeting +consistencyAndCorrectness = Consistency and correctness +processingConsole = Processing console +reTraceSettings = ReTrace settings +deobfuscatedStackTrace = De-obfuscated stack trace + +keepAdditionalTip = \ + If required, keep additional classes, fields, and methods as entry points. +keepNamesAdditionalTip = \ + If required, keep the names of additional classes, fields, and methods. +assumeNoSideEffectsAdditionalTip = \ + <html>Optionally specify additional methods that don't have any side effects.<br>\ + <i>Only add entries if you know what you're doing!</i></html> +whyAreYouKeepingTip = \ + Ask ProGuard why it is keeping certain classes, fields, or methods. + +# +# Info texts. +# +proGuardInfo = \ + ProGuard is a free class file shrinker, optimizer, obfuscator, and preverifier.\ + \n\n\ + With this GUI, you can create, load, modify, and save ProGuard configurations. \ + \n\ + You can then process your code right away, or you can run ProGuard from the \ + command line using your saved configuration. \ + \n\n\ + With the ReTrace part of this GUI you can de-obfuscate your stack traces.\ + \n\n\ + ProGuard and ReTrace are written and maintained by Eric Lafortune.\ + \n\n\ + Distributed under the GNU General Public License.\ + \n\ + Copyright (c) 2002-2009. + +processingInfo = \ + You can now start processing your code, \ + or you can run ProGuard from the command line using your saved configuration. + +reTraceInfo = \ + If you had ProGuard write out a mapping file, \ + you can de-obfuscate your obfuscated stack traces with ReTrace!\ + \n\n\ + You can load an obfuscated stack trace from a file, \ + or you can paste it straight into the text area above. + +# +# Titles and labels corresponding to common ProGuard options. +# +programJars = Program jars, wars, ears, zips, and directories +libraryJars = Library jars, wars, ears, zips, and directories + +shrink = Shrink +printUsage = Print usage + +optimize = Optimize +allowAccessModification = Allow access modification +mergeInterfacesAggressively = Merge interfaces aggressively +optimizations = Optimizations +optimizationPasses = Optimization passes + +obfuscate = Obfuscate +printMapping = Print mapping +applyMapping = Apply mapping +obfuscationDictionary = Obfuscation dictionary +classObfuscationDictionary = Class obfuscation dictionary +packageObfuscationDictionary = Package obfuscation dictionary +overloadAggressively = Overload aggressively +useUniqueClassMemberNames = Use unique class member names +keepPackageNames = Keep package names +flattenPackageHierarchy = Flatten package hierarchy +repackageClasses = Repackage classes +useMixedCaseClassNames = Use mixed-case class names +keepAttributes = Keep attributes +renameSourceFileAttribute = Rename SourceFile attribute +adaptClassStrings = Adapt class strings +adaptResourceFileNames = Adapt resource file names +adaptResourceFileContents = Adapt resource file contents + +preverify = Preverify +microEdition = Micro Edition + +verbose = Verbose +note = Note potential mistakes in the configuration +warn = Warn about possibly erronous input +ignoreWarnings = Ignore warnings about possibly erronous input +skipNonPublicLibraryClasses = Skip non-public library classes +skipNonPublicLibraryClassMembers = Skip non-public library class members +keepDirectories = Keep directories +forceProcessing = Force processing +target = Target +targets = 1.0,1.1,1.2,1.3,1.4,1.5,1.6 +printSeeds = Print seeds +printConfiguration = Print configuration +dump = Print class files + +mappingFile = Mapping file +obfuscatedStackTrace = Obfuscated stack trace + +programJarsTip = \ + <html>The input jars (wars, ears, zips, directories), followed by<br>\ + their corresponding output jars (wars, ears, zips, directories).</html> +libraryJarsTip = \ + <html>The library jars (wars, ears, zips, directories), on which the program jars depend.<br>\ + The library jars are required for processing, but they are not copied to the output.</html> + +shrinkTip = \ + Remove unused classes, fields, and methods from the output. +printUsageTip = \ + Print out the list of unused classes, fields, and methods. + +optimizeTip = \ + Optimize the bytecode of the processed classes. +allowAccessModificationTip = \ + Allow the optimization step to modify the access modifiers of classes, fields, and methods. +mergeInterfacesAggressivelyTip = \ + <html>Allow interfaces to be merged, even if their implementations don't implement all<br>\ + interface methods. This is not allowed in the Java language, but it is allowed in bytecode.</html> +optimizationsTip = \ + Specify the types of optimizations to be performed. +optimizationsFilterTip = \ + A filter for the names of the optimizations to be performed. +optimizationsSelectTip = \ + Select from the currently available optimizations... +optimizationPassesTip = \ + Specify the number of optimization passes to be performed. + +obfuscateTip = \ + Obfuscate the names of the processed classes, fields, and methods. +printMappingTip = \ + Print out the obfuscation mapping of original names to obfuscated names. +applyMappingTip = \ + Apply the given mapping of original names to obfuscated names. +obfuscationDictionaryTip = \ + Use the words in the given file for obfuscating field names and method names. +classObfuscationDictionaryTip = \ + Use the words in the given file for obfuscating class names. +packageObfuscationDictionaryTip = \ + Use the words in the given file for obfuscating package names. +overloadAggressivelyTip = \ + <html>Allow fields and methods to get the same obfuscated names, even if only their types or<br>\ + return types differ. This is not allowed in the Java language, but it is allowed in bytecode.</html> +useUniqueClassMemberNamesTip = \ + <html>Make sure fields and methods get the same obfuscation mapping across classes, even<br>\ + if they are unrelated. This is advisable if the output is to be obfuscated incrementally.</html> +keepPackageNamesTip = \ + Keep the specified package names from being obfuscated. +packageNamesTip = \ + <html>An optional comma-separated list of package names,<br>\ + e.g. <code>myapplication,mylibrary.**</code><br>\ + Possible wildcards:\ + <ul>\ + <li><code>?</code> for any single character, except the package separator.\ + <li><code>*</code> for any number of any characters, except the package separator.\ + <li><code>**</code> for any number of any characters.\ + </ul>\ + The negator <code>!</code> is also supported.</html> +flattenPackageHierarchyTip = \ + Move all packages that are renamed into the given parent package. +repackageClassesTip = \ + Move all classes that are renamed into the given package. +packageTip = \ + The optional package name. +useMixedCaseClassNamesTip = \ + <html>Generate mixed-case obfucated class names. This will complicate unpacking<br>\ + the resulting jars on case-insensitive file systems, should that be necessary.</html> +keepAttributesTip = \ + Keep the specified optional class file attributes. +attributesTip = \ + <html>An optional comma-separated list of class file attributes.\ + <ul>\ + <li>"Exceptions,Innerclasses, Signature" are necessary if the output is to be used as a library.\ + <li>"Deprecated" is optional if the output is to be used as a library.\ + <li>"LocalVariable*Table" can be useful for debugging.\ + <li>"Sourcefile,LineNumberTable" are necessary for generating stack traces.\ + <li>"*Annotations*" is necessary for preserving annotations.\ + </ul>\ + The wildcard <code>*</code> and the negator <code>!</code> are allowed.</html> +renameSourceFileAttributeTip = \ + <html>Put the given string in the "SourceFile" attribute of the processed class files.<br>\ + It will appear as the file name of the classes in stack traces.</html> +sourceFileAttributeTip = \ + The replacement "SourceFile" string. +adaptClassStringsTip = \ + <html>Adapt string constants in the specified classes, based<br>\ + on the obfuscated names of corresponding classes.</html> +adaptResourceFileNamesTip = \ + <html>Rename the specified resource files, based on the<br>\ + obfuscated names of the corresponding class files.</html> +adaptResourceFileContentsTip = \ + <html>Adapt the contents of the specified resource files, based<br>\ + on the obfuscated names of the processed classes.</html> +fileNameFilterTip = \ + <html>A filter on file names,<br>\ + e.g. <code>mydirectory1/**,mydirectory2/**</code><br>\ + Possible wildcards:\ + <ul>\ + <li><code>?</code> for any single character, except the directory separator.\ + <li><code>*</code> for any number of any characters, except the directory separator.\ + <li><code>**</code> for any number of any characters.\ + </ul>\ + The negator <code>!</code> is also supported.</html> + +preverifyTip = \ + Preverify the processed classes, for Java Micro Edition or for Java 6. +microEditionTip = \ + Target Java Micro Edition. + +verboseTip = \ + Print out verbose messages while processing. +noteTip = \ + Print out notes about special or unusual input. +noteFilterTip = \ + A filter matching classes for which no notes should be printed. +warnTip = \ + <html>Print out warnings about possibly erronous input.<br>\ + <i>Only unset this option if you know what you're doing!</i></html> +warnFilterTip = \ + A filter matching classes for which no warnings should be printed. +ignoreWarningsTip = \ + <html>Ignore any warnings about possibly erronous input.<br>\ + <i>Only set this option if you know what you're doing!</i></html> +skipNonPublicLibraryClassesTip = \ + <html>Skip reading non-public library classes, for efficiency.<br>\ + You may have to unset this option if ProGuard complains about missing classes.</html> +skipNonPublicLibraryClassMembersTip = \ + <html>Skip reading non-public library fields and methods, for efficiency.<br>\ + You may have to unset this option if ProGuard complains about missing class members.</html> +keepDirectoriesTip = \ + Keep the specified directories in the output jars, wars, ears, zips, or directories. +directoriesTip = \ + <html>A filter on directory names,<br>\ + e.g. <code>mydirectory1,mydirectory2/**</code><br>\ + Possible wildcards:\ + <ul>\ + <li><code>?</code> for any single character, except the directory separator.\ + <li><code>*</code> for any number of any characters, except the directory separator.\ + <li><code>**</code> for any number of any characters.\ + </ul>\ + The negator <code>!</code> is also supported.</html> +forceProcessingTip = \ + Always process the input, even if the output seems up to date. +targetTip = \ + Target the specified version of Java. +printSeedsTip = \ + Print out the list of kept classes, fields, and methods. +printConfigurationTip = \ + Print out the configuration. +dumpTip = \ + Print out the internal structure of the processed class files. + +mappingFileTip = \ + The file containing the mapping of original names to obfuscated names. +obfuscatedStackTraceTip = \ + A stack trace produced by previously obfuscated code. + +# +# Titles and labels corresponding to ProGuard keep options. +# +keepTitle = Keep + +keep = Keep classes and class members +keepClassMembers = Keep class members only +keepClassesWithMembers = Keep classes and class members, if members are present + +allowTitle = Allow + +allowShrinking = Allow shrinking +allowOptimization = Allow optimization +allowObfuscation = Allow obfuscation + +keepTitleTip = Keep the specified classes and/or their fields and methods. + +keepTip = \ + <html>Keep the specified classes, fields, and methods as entry points.<br>\ + This is the most common option.</html> +keepClassMembersTip = \ + Only keep the specified fields and methods as entry points. +keepClassesWithMembersTip = \ + <html>Keep the specified classes, fields, and methods,<br>\ + on the condition that the fields and methods are present.</html> + +allowTitleTip = \ + <html>Optionally relax keeping the specified classes, fields, and methods.<br>\ + <i>These are advanced options.</i></html> + +allowShrinkingTip = \ + Remove the specified classes, fields, and methods anyway, if they are not used. +allowOptimizationTip = \ + <html>Optimize the specified classes, fields, and methods as entry points anyway.<br>\ + <i>Only set this option if you know what you're doing!</i></html> +allowObfuscationTip = \ + <html>Obfuscate the names of the specified classes, fields, and methods anyway.<br>\ + <i>Only set this option if you know what you're doing!</i></html> + +# +# Further keep titles and labels. +# +specifyClasses = Specify classes and class members... +specifyFields = Specify fields... +specifyMethods = Specify methods... + +comments = Comments +access = Access +required = Required +not = Not +dontCare = Don't care +annotation = Annotation +class = Class +extendsImplementsAnnotation = Extends/implements class with annotation +extendsImplementsClass = Extends/implements class +classMembers = Class members + +extensionsOf = Extensions of +specificationNumber = Specification # + +fieldType = Field type +returnType = Return type +name = Name +argumentTypes = Argument types + +commentsTip = \ + Optionally add a comment for this option in the configuration file. +accessTip = \ + <html>Optionally place constraints on the access modifiers of this element.<br>\ + E.g. only match public elements.</html> +requiredTip = \ + The access modifier has to be set. +notTip = \ + The access modifier must not be set. +dontCareTip = \ + The access modifier is irrelevant. +annotationTip = \ + <html>Optionally require the given annotation to be present on this element.<br>\ + E.g. only match elements that have an annotation <code>myPackage.MyAnnotation</code>.<br>\ + <i>This is an advanced option.</i></html> +classTip = \ + The name of the class or interface. +extendsImplementsAnnotationTip = \ + <html>Optionally require the given annotation to be present on the<br>\ + extended or implemented class or interface.<br>\ + E.g. only match classes that extend a class that has an annotation<br>\ + <code>myPackage.MyAnnotation</code>.<br>\ + <i>This is an advanced option.</i></html> +extendsImplementsClassTip = \ + <html>Optionally require the class to implement or extend the given class or interface.<br>\ + E.g. only match classes that implement an interface <code>myPackage.MyInterface</code>.</html> +classMembersTip = \ + <html>Optionally keep fields and methods as entry points in the matching class or classes.<br>\ + E.g. keep all public '<code>get*</code>' methods as entry points.</html> + +fieldTypeTip = The field type. +returnTypeTip = The method return type, if any. +nameTip = The name. +argumentTypesTip = The method argument types, if any. + +classNameTip = \ + <html>The class name, e.g. <code>myPackage.MyClass</code><br>\ + Possible wildcards:\ + <ul>\ + <li><code>?</code> for any single character, except the package separator.\ + <li><code>*</code> for any number of any characters, except the package separator.\ + <li><code>**</code> for any number of any characters.\ + </ul></html> +classNamesTip = \ + <html>A regular expression to further constrain the class names,<br>\ + e.g. <code>myPackage1.MyClass,myPackage2.**</code><br>\ + Possible wildcards:\ + <ul>\ + <li><code>?</code> for any single character, except the package separator.\ + <li><code>*</code> for any number of any characters, except the package separator.\ + <li><code>**</code> for any number of any characters.\ + </ul>\ + The negator <code>!</code> is also supported.</html> +typeTip = \ + <html>The type, e.g. <code>int</code>, or <code>java.lang.String[]</code><br>\ + Possible wildcards:\ + <ul>\ + <li><code>%</code> for any primitive type.\ + <li><code>?</code> for any single character, except the package separator.\ + <li><code>*</code> for any number of any characters, except the package separator.\ + <li><code>**</code> for any number of any characters.\ + <li><code>***</code> (or empty) for any type.\ + </ul></html> +fieldNameTip = \ + <html>The field name, e.g. <code>myField</code><br>\ + Possible wildcards:\ + <ul>\ + <li><code>?</code> for any single character.\ + <li><code>*</code> for any number of any characters.\ + </ul></html> +methodNameTip = \ + <html>The method name, e.g. <code>myMethod</code><br>\ + Possible wildcards:\ + <ul>\ + <li><code>?</code> for any single character.\ + <li><code>*</code> for any number of any characters.\ + </ul></html> +argumentTypes2Tip = \ + <html>The comma-separated list of argument types,<br>\ + e.g. <code>java.lang.String[],int,boolean</code><br>\ + Possible wildcards:\ + <ul>\ + <li><code>%</code> for any primitive type.\ + <li><code>?</code> for any single character, except the package separator.\ + <li><code>*</code> for any number of any characters, except the package separator.\ + <li><code>**</code> for any number of any characters.\ + <li><code>***</code> for any type.\ + <li><code>...</code> for any number of any arguments.\ + </ul></html> + +# +# Titles and labels corresponding to optimization options. +# +selectOptimizations = Select optimizations... + +field = Field +method = Method +code = Code + +class_marking_finalTip = \ + Mark classes as final, whenever possible. +class_merging_verticalTip = \ + Merge classes vertically in the class hierarchy, whenever possible. +class_merging_horizontalTip = \ + Merge classes horizontally in the class hierarchy, whenever possible. +field_removal_writeonlyTip = \ + Remove write-only fields. +field_marking_privateTip = \ + Mark fields as private, whenever possible. +field_propagation_valueTip = \ + Propagate the values of fields across methods. +method_marking_privateTip = \ + Mark methods as private, whenever possible (devirtualization). +method_marking_staticTip = \ + Mark methods as static, whenever possible (devirtualization). +method_marking_finalTip = \ + Mark methods as final, whenever possible. +method_removal_parameterTip = \ + Remove unused method parameters. +method_propagation_parameterTip = \ + Propagate the values of method parameters from method invocations to \ + the invoked methods. +method_propagation_returnvalueTip = \ + Propagate the values of method return values from methods to their \ + invocations. +method_inlining_shortTip = \ + Inline short methods. +method_inlining_uniqueTip = \ + Inline methods that are only called once. +method_inlining_tailrecursionTip = \ + Simplify tail recursion calls, whenever possible. +code_mergingTip = \ + Merge identical blocks of code by modifying branch targets. +code_simplification_variableTip = \ + Perform peephole optimizations for variable loading and storing. +code_simplification_arithmeticTip = \ + Perform peephole optimizations for arithmetic instructions. +code_simplification_castTip = \ + Perform peephole optimizations for casting operations. +code_simplification_fieldTip = \ + Perform peephole optimizations for field loading and storing. +code_simplification_branchTip = \ + Perform peephole optimizations for branch instructions. +code_simplification_advancedTip = \ + Simplify code based on control flow analysis and data flow analysis. +code_removal_advancedTip = \ + Remove dead code based on control flow analysis and data flow analysis. +code_removal_simpleTip = \ + Remove dead code based on a simple control flow analysis. +code_removal_variableTip = \ + Remove unused variables from the local variable frame. +code_removal_exceptionTip = \ + Remove exceptions with empty catch blocks. +code_allocation_variableTip = \ + Optimize variable allocation on the local variable frame. + + +# +# File selection titles. +# +selectConfigurationFile = Select a configuration file... +saveConfigurationFile = Save configuration... +selectUsageFile = Select a usage output file... +selectPrintMappingFile = Select an output mapping file... +selectApplyMappingFile = Select an input mapping file... +selectObfuscationDictionaryFile = Select an obfuscation dictionary... +selectSeedsFile = Select a seeds output file... +selectDumpFile = Select a class dump file... +selectStackTraceFile = Select a stack trace file... + +cantOpenConfigurationFile = Can''t open the configuration file [{0}] +cantParseConfigurationFile = Can''t parse the configuration file [{0}] +cantSaveConfigurationFile = Can''t save the configuration file [{0}] +cantOpenStackTraceFile = Can''t open the stack trace file [{0}] + +jarWarEarZipExtensions = *.jar, *.war, *.ear, *.zip (archives and directories) +proExtension = *.pro (ProGuard configurations) + +addJars = Add one or more jars or directories... +chooseJars = Choose different jars or directories... +enterFilter = Optionally filter the file names contained in the selected entries. + +filters = Filters +nameFilter = File name filter +jarNameFilter = Jar name filter +warNameFilter = War name filter +earNameFilter = Ear name filter +zipNameFilter = Zip name filter + +outputFileTip = The optional output file. +inputFileTip = The input file. + +nameFilterTip = A filter on plain class file names and resource file names. +jarNameFilterTip = A filter on jar file names. +warNameFilterTip = A filter on war file names. +earNameFilterTip = A filter on ear file names. +zipNameFilterTip = A filter on zip file names. + +# +# Simple button texts. +# +previous = Previous +next = Next +browse = Browse... +advanced = Advanced options +basic = Basic options +selectAll = Select all +selectNone = Select none +ok = Ok +cancel = Cancel + +add = Add... +addInput = Add input... +addOutput = Add output... +edit = Edit... +filter = Filter... +remove = Remove +moveUp = Move up +moveDown = Move down + +moveToLibraries = Move to libraries +moveToProgram = Move to program + +addField = Add field... +addMethod = Add method... + +select = Select... + +loadConfiguration = Load configuration... +viewConfiguration = View configuration +saveConfiguration = Save configuration... +loadStackTrace = Load stack trace... +process = Process! +reTrace = ReTrace! + +advancedTip = Toggle between showing basic options and advanced options. + +addInputTip = Add an input jar, war, ear, zip, or directory. +addOutputTip = Add an output jar, war, ear, zip, or directory. +addTip = Add an entry. +editTip = Edit the selected entries. +filterTip = Put filters on the contents of the selected entries. +removeTip = Remove the selected entries. +moveUpTip = Move the selected entries up in the list. +moveDownTip = Move the selected entries down in the list. + +moveToLibrariesTip = Move to selected entries to the libraries. +moveToProgramTip = Move to selected entries to the program. + +addFieldTip = Add a field to the specification. +addMethodTip = Add a method to the specification. + +loadConfigurationTip = Optionally load an initial configuration. +viewConfigurationTip = View the current configuration. +saveConfigurationTip = Save the current configuration. +loadStackTraceTip = Load a stack trace from a file. +processTip = Start processing, based on the current configuration. +reTraceTip = De-obfuscate the given stack trace. + +# +# Progress messages and error messages. +# +warning = Warning +outOfMemory = Out of memory +outOfMemoryInfo = \n\ + You should run the ProGuard GUI with a larger java heap size, \ + with a command like\ + \n\n\t\ + java -Xms128m -Xmx192m -jar proguardgui.jar {0}\ + \n\n\ + or you can try running ProGuard from the command line. \ + with a command like\ + \n\n\t\ + java -jar proguard.jar @{0} +sampleConfigurationFileName = configuration.pro +errorProcessing = Error during processing +errorReTracing = Error during retracing diff --git a/src/proguard/gui/KeepSpecificationsPanel.java b/src/proguard/gui/KeepSpecificationsPanel.java new file mode 100644 index 0000000..4c3c953 --- /dev/null +++ b/src/proguard/gui/KeepSpecificationsPanel.java @@ -0,0 +1,81 @@ +/* + * 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 javax.swing.*; + +/** + * This <code>ListPanel</code> allows the user to add, edit, move, and remove + * KeepClassSpecification entries in a list. + * + * @author Eric Lafortune + */ +final class KeepSpecificationsPanel extends ClassSpecificationsPanel +{ + private final boolean markClasses; + private final boolean markConditionally; + private final boolean allowShrinking; + private final boolean allowOptimization; + private final boolean allowObfuscation; + + + public KeepSpecificationsPanel(JFrame owner, + boolean markClasses, + boolean markConditionally, + boolean allowShrinking, + boolean allowOptimization, + boolean allowObfuscation) + { + super(owner, true); + + this.markClasses = markClasses; + this.markConditionally = markConditionally; + this.allowShrinking = allowShrinking; + this.allowOptimization = allowOptimization; + this.allowObfuscation = allowObfuscation; + } + + + // Factory methods for ClassSpecificationsPanel. + + protected ClassSpecification createClassSpecification() + { + return new KeepClassSpecification(markClasses, + markConditionally, + allowShrinking, + allowOptimization, + allowObfuscation); + } + + + protected void setClassSpecification(ClassSpecification classSpecification) + { + classSpecificationDialog.setKeepSpecification((KeepClassSpecification)classSpecification); + } + + + protected ClassSpecification getClassSpecification() + { + return classSpecificationDialog.getKeepSpecification(); + } +} diff --git a/src/proguard/gui/ListPanel.java b/src/proguard/gui/ListPanel.java new file mode 100644 index 0000000..0132340 --- /dev/null +++ b/src/proguard/gui/ListPanel.java @@ -0,0 +1,341 @@ +/* + * 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 java.awt.*; +import java.awt.event.*; +import java.util.List; +import java.util.*; + +import javax.swing.*; +import javax.swing.event.*; + +/** + * This <code>Jpanel</code> allows the user to move and remove entries in a + * list and between lists. Extensions of this class should add buttons to add + * and possibly edit entries, and to set and get the resulting list. + * + * @author Eric Lafortune + */ +abstract class ListPanel extends JPanel +{ + protected final DefaultListModel listModel = new DefaultListModel(); + protected final JList list = new JList(listModel); + + protected int firstSelectionButton = 2; + + + protected ListPanel() + { + GridBagLayout layout = new GridBagLayout(); + setLayout(layout); + + GridBagConstraints listConstraints = new GridBagConstraints(); + listConstraints.gridheight = GridBagConstraints.REMAINDER; + listConstraints.fill = GridBagConstraints.BOTH; + listConstraints.weightx = 1.0; + listConstraints.weighty = 1.0; + listConstraints.anchor = GridBagConstraints.NORTHWEST; + listConstraints.insets = new Insets(0, 2, 0, 2); + + // Make sure some buttons are disabled or enabled depending on whether + // the selection is empty or not. + list.addListSelectionListener(new ListSelectionListener() + { + public void valueChanged(ListSelectionEvent e) + { + enableSelectionButtons(); + } + }); + + add(new JScrollPane(list), listConstraints); + + // something like the following calls are up to the extending class: + //addAddButton(); + //addEditButton(); + //addRemoveButton(); + //addUpButton(); + //addDownButton(); + // + //enableSelectionButtons(); + } + + + protected void addRemoveButton() + { + JButton removeButton = new JButton(msg("remove")); + removeButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + // Remove the selected elements. + removeElementsAt(list.getSelectedIndices()); + } + }); + + addButton(tip(removeButton, "removeTip")); + } + + + protected void addUpButton() + { + JButton upButton = new JButton(msg("moveUp")); + upButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + int[] selectedIndices = list.getSelectedIndices(); + if (selectedIndices.length > 0 && + selectedIndices[0] > 0) + { + // Move the selected elements up. + moveElementsAt(selectedIndices, -1); + } + } + }); + + addButton(tip(upButton, "moveUpTip")); + } + + + protected void addDownButton() + { + JButton downButton = new JButton(msg("moveDown")); + downButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + int[] selectedIndices = list.getSelectedIndices(); + if (selectedIndices.length > 0 && + selectedIndices[selectedIndices.length-1] < listModel.getSize()-1) + { + // Move the selected elements down. + moveElementsAt(selectedIndices, 1); + } + } + }); + + addButton(tip(downButton, "moveDownTip")); + } + + + /** + * Adds a button that allows to copy or move entries to another ListPanel. + * + * @param buttonTextKey the button text key. + * @param tipKey the tool tip key. + * @param panel the other ListPanel. + */ + public void addCopyToPanelButton(String buttonTextKey, + String tipKey, + final ListPanel panel) + { + JButton moveButton = new JButton(msg(buttonTextKey)); + moveButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + int[] selectedIndices = list.getSelectedIndices(); + Object[] selectedElements = list.getSelectedValues(); + + // Remove the selected elements from this panel. + removeElementsAt(selectedIndices); + + // Add the elements to the other panel. + panel.addElements(selectedElements); + } + }); + + addButton(tip(moveButton, tipKey)); + } + + + protected void addButton(JComponent button) + { + GridBagConstraints buttonConstraints = new GridBagConstraints(); + buttonConstraints.gridwidth = GridBagConstraints.REMAINDER; + buttonConstraints.fill = GridBagConstraints.HORIZONTAL; + buttonConstraints.anchor = GridBagConstraints.NORTHWEST; + buttonConstraints.insets = new Insets(0, 2, 0, 2); + + add(button, buttonConstraints); + } + + + /** + * Returns a list of all right-hand side buttons. + */ + public List getButtons() + { + List list = new ArrayList(getComponentCount()-1); + + // Add all buttons. + for (int index = 1; index < getComponentCount(); index++) + { + list.add(getComponent(index)); + } + + return list; + } + + + protected void addElement(Object element) + { + listModel.addElement(element); + + // Make sure it is selected. + list.setSelectedIndex(listModel.size() - 1); + } + + + protected void addElements(Object[] elements) + { + // Add the elements one by one. + for (int index = 0; index < elements.length; index++) + { + listModel.addElement(elements[index]); + } + + // Make sure they are selected. + int[] selectedIndices = new int[elements.length]; + for (int index = 0; index < selectedIndices.length; index++) + { + selectedIndices[index] = + listModel.size() - selectedIndices.length + index; + } + list.setSelectedIndices(selectedIndices); + } + + + protected void moveElementsAt(int[] indices, int offset) + { + // Remember the selected elements. + Object[] selectedElements = list.getSelectedValues(); + + // Remove the selected elements. + removeElementsAt(indices); + + // Update the element indices. + for (int index = 0; index < indices.length; index++) + { + indices[index] += offset; + } + + // Reinsert the selected elements. + insertElementsAt(selectedElements, indices); + } + + + protected void insertElementsAt(Object[] elements, int[] indices) + { + for (int index = 0; index < elements.length; index++) + { + listModel.insertElementAt(elements[index], indices[index]); + } + + // Make sure they are selected. + list.setSelectedIndices(indices); + } + + + protected void setElementAt(Object element, int index) + { + listModel.setElementAt(element, index); + + // Make sure it is selected. + list.setSelectedIndex(index); + } + + + protected void setElementsAt(Object[] elements, int[] indices) + { + for (int index = 0; index < elements.length; index++) + { + listModel.setElementAt(elements[index], indices[index]); + } + + // Make sure they are selected. + list.setSelectedIndices(indices); + } + + + protected void removeElementsAt(int[] indices) + { + for (int index = indices.length - 1; index >= 0; index--) + { + listModel.removeElementAt(indices[index]); + } + + // Make sure nothing is selected. + list.clearSelection(); + + // Make sure the selection buttons are properly enabled, + // since the above method doesn't seem to notify the listener. + enableSelectionButtons(); + } + + + protected void removeAllElements() + { + listModel.removeAllElements(); + + // Make sure the selection buttons are properly enabled, + // since the above method doesn't seem to notify the listener. + enableSelectionButtons(); + } + + + /** + * Enables or disables the buttons that depend on a selection. + */ + protected void enableSelectionButtons() + { + boolean selected = !list.isSelectionEmpty(); + + // Loop over all components, except the list itself and the Add button. + for (int index = firstSelectionButton; index < getComponentCount(); index++) + { + getComponent(index).setEnabled(selected); + } + } + + + /** + * 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); + } +} diff --git a/src/proguard/gui/MANIFEST.MF b/src/proguard/gui/MANIFEST.MF new file mode 100644 index 0000000..05403b4 --- /dev/null +++ b/src/proguard/gui/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: proguard.gui.ProGuardGUI +Class-Path: proguard.jar retrace.jar diff --git a/src/proguard/gui/MemberSpecificationDialog.java b/src/proguard/gui/MemberSpecificationDialog.java new file mode 100644 index 0000000..46a3f6f --- /dev/null +++ b/src/proguard/gui/MemberSpecificationDialog.java @@ -0,0 +1,497 @@ +/* + * 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.MemberSpecification; +import proguard.classfile.ClassConstants; +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 JRadioButton[] volatileRadioButtons; + private JRadioButton[] transientRadioButtons; + + private JRadioButton[] synchronizedRadioButtons; + private JRadioButton[] nativeRadioButtons; + private JRadioButton[] abstractRadioButtons; + private JRadioButton[] strictRadioButtons; + + 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); + + 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); + } + + // 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.INTERNAL_ACC_PUBLIC, publicRadioButtons); + setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_PRIVATE, privateRadioButtons); + setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_PROTECTED, protectedRadioButtons); + setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_STATIC, staticRadioButtons); + setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_FINAL, finalRadioButtons); + setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_VOLATILE, volatileRadioButtons); + setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_TRANSIENT, transientRadioButtons); + setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_SYNCHRONIZED, synchronizedRadioButtons); + setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_NATIVE, nativeRadioButtons); + setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_ABSTRACT, abstractRadioButtons); + setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_STRICT, strictRadioButtons); + + // 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 = ClassConstants.EXTERNAL_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.INTERNAL_ACC_PUBLIC, publicRadioButtons); + getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_PRIVATE, privateRadioButtons); + getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_PROTECTED, protectedRadioButtons); + getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_STATIC, staticRadioButtons); + getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_FINAL, finalRadioButtons); + getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_VOLATILE, volatileRadioButtons); + getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_TRANSIENT, transientRadioButtons); + getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_SYNCHRONIZED, synchronizedRadioButtons); + getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_NATIVE, nativeRadioButtons); + getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_ABSTRACT, abstractRadioButtons); + getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_STRICT, strictRadioButtons); + + 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); + } +} diff --git a/src/proguard/gui/MemberSpecificationsPanel.java b/src/proguard/gui/MemberSpecificationsPanel.java new file mode 100644 index 0000000..20b2f17 --- /dev/null +++ b/src/proguard/gui/MemberSpecificationsPanel.java @@ -0,0 +1,304 @@ +/* + * 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 java.awt.*; +import java.awt.event.*; +import java.util.*; +import java.util.List; + + +/** + * This <code>ListPanel</code> allows the user to add, edit, move, and remove + * MemberSpecification entries in a list. + * + * @author Eric Lafortune + */ +final class MemberSpecificationsPanel extends ListPanel +{ + private final MemberSpecificationDialog fieldSpecificationDialog; + private final MemberSpecificationDialog methodSpecificationDialog; + + + public MemberSpecificationsPanel(JDialog owner, boolean fullKeepOptions) + { + super(); + + super.firstSelectionButton = fullKeepOptions ? 3 : 2; + + list.setCellRenderer(new MyListCellRenderer()); + + fieldSpecificationDialog = new MemberSpecificationDialog(owner, true); + methodSpecificationDialog = new MemberSpecificationDialog(owner, false); + + if (fullKeepOptions) + { + addAddFieldButton(); + } + addAddMethodButton(); + addEditButton(); + addRemoveButton(); + addUpButton(); + addDownButton(); + + enableSelectionButtons(); + } + + + protected void addAddFieldButton() + { + JButton addFieldButton = new JButton(msg("addField")); + addFieldButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + fieldSpecificationDialog.setMemberSpecification(new MemberSpecification()); + int returnValue = fieldSpecificationDialog.showDialog(); + if (returnValue == MemberSpecificationDialog.APPROVE_OPTION) + { + // Add the new element. + addElement(new MyMemberSpecificationWrapper(fieldSpecificationDialog.getMemberSpecification(), + true)); + } + } + }); + + addButton(tip(addFieldButton, "addFieldTip")); + } + + + protected void addAddMethodButton() + { + JButton addMethodButton = new JButton(msg("addMethod")); + addMethodButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + methodSpecificationDialog.setMemberSpecification(new MemberSpecification()); + int returnValue = methodSpecificationDialog.showDialog(); + if (returnValue == MemberSpecificationDialog.APPROVE_OPTION) + { + // Add the new element. + addElement(new MyMemberSpecificationWrapper(methodSpecificationDialog.getMemberSpecification(), + false)); + } + } + }); + + addButton(tip(addMethodButton, "addMethodTip")); + } + + + protected void addEditButton() + { + JButton editButton = new JButton(msg("edit")); + editButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + MyMemberSpecificationWrapper wrapper = + (MyMemberSpecificationWrapper)list.getSelectedValue(); + + MemberSpecificationDialog memberSpecificationDialog = + wrapper.isField ? + fieldSpecificationDialog : + methodSpecificationDialog; + + memberSpecificationDialog.setMemberSpecification(wrapper.memberSpecification); + int returnValue = memberSpecificationDialog.showDialog(); + if (returnValue == MemberSpecificationDialog.APPROVE_OPTION) + { + // Replace the old element. + wrapper.memberSpecification = memberSpecificationDialog.getMemberSpecification(); + setElementAt(wrapper, + list.getSelectedIndex()); + } + } + }); + + addButton(tip(editButton, "editTip")); + } + + + /** + * Sets the MemberSpecification instances to be represented in this panel. + */ + public void setMemberSpecifications(List fieldSpecifications, + List methodSpecifications) + { + listModel.clear(); + + if (fieldSpecifications != null) + { + for (int index = 0; index < fieldSpecifications.size(); index++) + { + listModel.addElement( + new MyMemberSpecificationWrapper((MemberSpecification)fieldSpecifications.get(index), + true)); + } + } + + if (methodSpecifications != null) + { + for (int index = 0; index < methodSpecifications.size(); index++) + { + listModel.addElement( + new MyMemberSpecificationWrapper((MemberSpecification)methodSpecifications.get(index), + false)); + } + } + + // Make sure the selection buttons are properly enabled, + // since the clear method doesn't seem to notify the listener. + enableSelectionButtons(); + } + + + /** + * Returns the MemberSpecification instances currently represented in + * this panel, referring to fields or to methods. + * + * @param isField specifies whether specifications referring to fields or + * specifications referring to methods should be returned. + */ + public List getMemberSpecifications(boolean isField) + { + int size = listModel.size(); + if (size == 0) + { + return null; + } + + List memberSpecifications = new ArrayList(size); + for (int index = 0; index < size; index++) + { + MyMemberSpecificationWrapper wrapper = + (MyMemberSpecificationWrapper)listModel.get(index); + + if (wrapper.isField == isField) + { + memberSpecifications.add(wrapper.memberSpecification); + } + } + + return memberSpecifications; + } + + + /** + * This ListCellRenderer renders MemberSpecification objects. + */ + private static class MyListCellRenderer implements ListCellRenderer + { + private final JLabel label = new JLabel(); + + + // Implementations for ListCellRenderer. + + public Component getListCellRendererComponent(JList list, + Object value, + int index, + boolean isSelected, + boolean cellHasFocus) + { + MyMemberSpecificationWrapper wrapper = (MyMemberSpecificationWrapper)value; + + MemberSpecification option = wrapper.memberSpecification; + String name = option.name; + String descriptor = option.descriptor; + + label.setText(wrapper.isField ? + (descriptor == null ? name == null ? + "<fields>" : + "***" + ' ' + name : + ClassUtil.externalFullFieldDescription(0, + name == null ? "*" : name, + descriptor)) : + (descriptor == null ? name == null ? + "<methods>" : + "***" + ' ' + name + "(...)" : + ClassUtil.externalFullMethodDescription(ClassConstants.INTERNAL_METHOD_NAME_INIT, + 0, + name == null ? "*" : name, + descriptor))); + + if (isSelected) + { + label.setBackground(list.getSelectionBackground()); + label.setForeground(list.getSelectionForeground()); + } + else + { + label.setBackground(list.getBackground()); + label.setForeground(list.getForeground()); + } + + label.setOpaque(true); + + return label; + } + } + + + /** + * 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); + } + + + /** + * This class wraps a MemberSpecification, additionally storing whether + * the option refers to a field or to a method. + */ + private static class MyMemberSpecificationWrapper + { + public MemberSpecification memberSpecification; + public final boolean isField; + + public MyMemberSpecificationWrapper(MemberSpecification memberSpecification, + boolean isField) + { + this.memberSpecification = memberSpecification; + this.isField = isField; + } + } +} diff --git a/src/proguard/gui/MessageDialogRunnable.java b/src/proguard/gui/MessageDialogRunnable.java new file mode 100644 index 0000000..e58f1c6 --- /dev/null +++ b/src/proguard/gui/MessageDialogRunnable.java @@ -0,0 +1,90 @@ +/* + * 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 javax.swing.*; +import java.awt.*; +import java.lang.reflect.InvocationTargetException; + + +/** + * This <code>Runnable</code> can show a message dialog. + * + * @author Eric Lafortune + */ +final class MessageDialogRunnable implements Runnable +{ + private final Component parentComponent; + private final Object message; + private final String title; + private final int messageType; + + + /** + * Creates a new MessageDialogRunnable object. + * @see JOptionPane#showMessageDialog(Component, Object, String, int) + */ + public static void showMessageDialog(Component parentComponent, + Object message, + String title, + int messageType) + { + try + { + SwingUtil.invokeAndWait(new MessageDialogRunnable(parentComponent, + message, + title, + messageType)); + } + catch (Exception e) + { + // Nothing. + } + } + + + /** + * Creates a new MessageDialogRunnable object. + * @see JOptionPane#showMessageDialog(Component, Object, String, int) + */ + public MessageDialogRunnable(Component parentComponent, + Object message, + String title, + int messageType) + { + this.parentComponent = parentComponent; + this.message = message; + this.title = title; + this.messageType = messageType; + } + + + + // Implementation for Runnable. + + public void run() + { + JOptionPane.showMessageDialog(parentComponent, + message, + title, + messageType); + } +} diff --git a/src/proguard/gui/OptimizationsDialog.java b/src/proguard/gui/OptimizationsDialog.java new file mode 100644 index 0000000..044c338 --- /dev/null +++ b/src/proguard/gui/OptimizationsDialog.java @@ -0,0 +1,251 @@ +/* + * 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.optimize.Optimizer; +import proguard.util.*; + +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 OptimizationsDialog 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 JCheckBox[] optimizationCheckBoxes = new JCheckBox[Optimizer.OPTIMIZATION_NAMES.length]; + + private int returnValue; + + + public OptimizationsDialog(JFrame owner) + { + super(owner, msg("selectOptimizations"), true); + setResizable(true); + + // Create some constraints that can be reused. + GridBagConstraints constraintsLast = new GridBagConstraints(); + constraintsLast.gridwidth = GridBagConstraints.REMAINDER; + constraintsLast.anchor = GridBagConstraints.WEST; + constraintsLast.insets = new Insets(1, 2, 1, 2); + + GridBagConstraints constraintsLastStretch = new GridBagConstraints(); + constraintsLastStretch.gridwidth = GridBagConstraints.REMAINDER; + constraintsLastStretch.fill = GridBagConstraints.HORIZONTAL; + constraintsLastStretch.weightx = 1.0; + constraintsLastStretch.anchor = GridBagConstraints.WEST; + constraintsLastStretch.insets = constraintsLast.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 = constraintsLast.insets; + + GridBagConstraints selectButtonConstraints = new GridBagConstraints(); + selectButtonConstraints.weighty = 1.0; + selectButtonConstraints.anchor = GridBagConstraints.SOUTHWEST; + selectButtonConstraints.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 = selectButtonConstraints.insets; + + GridBagConstraints cancelButtonConstraints = new GridBagConstraints(); + cancelButtonConstraints.gridwidth = GridBagConstraints.REMAINDER; + cancelButtonConstraints.weighty = 1.0; + cancelButtonConstraints.anchor = GridBagConstraints.SOUTHEAST; + cancelButtonConstraints.insets = selectButtonConstraints.insets; + + GridBagLayout layout = new GridBagLayout(); + + Border etchedBorder = BorderFactory.createEtchedBorder(EtchedBorder.RAISED); + + // Create the optimizations panel. + JPanel optimizationsPanel = new JPanel(layout); + JPanel optimizationSubpanel = null; + String lastOptimizationPrefix = null; + + for (int index = 0; index < Optimizer.OPTIMIZATION_NAMES.length; index++) + { + String optimizationName = Optimizer.OPTIMIZATION_NAMES[index]; + + String optimizationPrefix = optimizationName.substring(0, optimizationName.indexOf('/')); + + if (optimizationSubpanel == null || !optimizationPrefix.equals(lastOptimizationPrefix)) + { + // Create a new keep subpanel and add it. + optimizationSubpanel = new JPanel(layout); + optimizationSubpanel.setBorder(BorderFactory.createTitledBorder(etchedBorder, msg(optimizationPrefix))); + optimizationsPanel.add(optimizationSubpanel, panelConstraints); + + lastOptimizationPrefix = optimizationPrefix; + } + + JCheckBox optimizationCheckBox = new JCheckBox(optimizationName); + optimizationCheckBoxes[index] = optimizationCheckBox; + + optimizationSubpanel.add(tip(optimizationCheckBox, optimizationName.replace('/', '_')+"Tip"), constraintsLastStretch); + } + + // Create the Select All button. + JButton selectAllButton = new JButton(msg("selectAll")); + selectAllButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + for (int index = 0; index < optimizationCheckBoxes.length; index++) + { + optimizationCheckBoxes[index].setSelected(true); + } + } + }); + + // Create the Select All button. + JButton selectNoneButton = new JButton(msg("selectNone")); + selectNoneButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + for (int index = 0; index < optimizationCheckBoxes.length; index++) + { + optimizationCheckBoxes[index].setSelected(false); + } + } + }); + + // 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. + optimizationsPanel.add(selectAllButton, selectButtonConstraints); + optimizationsPanel.add(selectNoneButton, selectButtonConstraints); + optimizationsPanel.add(okButton, okButtonConstraints); + optimizationsPanel.add(cancelButton, cancelButtonConstraints); + + getContentPane().add(new JScrollPane(optimizationsPanel)); + } + + + /** + * Sets the initial optimization filter to be used by the dialog. + */ + public void setFilter(String optimizations) + { + StringMatcher filter = optimizations != null && optimizations.length() > 0 ? + new ListParser(new NameParser()).parse(optimizations) : + new FixedStringMatcher(""); + + for (int index = 0; index < Optimizer.OPTIMIZATION_NAMES.length; index++) + { + optimizationCheckBoxes[index].setSelected(filter.matches(Optimizer.OPTIMIZATION_NAMES[index])); + } + } + + + /** + * Returns the optimization filter composed from the settings in the dialog. + */ + public String getFilter() + { + return new FilterBuilder(optimizationCheckBoxes, '/').buildFilter(); + } + + + /** + * 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; + } + + + /** + * 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); + } +}
\ No newline at end of file diff --git a/src/proguard/gui/ProGuardGUI.java b/src/proguard/gui/ProGuardGUI.java new file mode 100644 index 0000000..f27d698 --- /dev/null +++ b/src/proguard/gui/ProGuardGUI.java @@ -0,0 +1,1738 @@ +/* + * 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.util.ClassUtil; +import proguard.gui.splash.*; +import proguard.util.ListUtil; + +import javax.swing.*; +import javax.swing.border.*; +import java.awt.*; +import java.awt.event.*; +import java.io.*; +import java.net.URL; +import java.util.*; +import java.util.List; +import java.lang.reflect.InvocationTargetException; + + +/** + * GUI for configuring and executing ProGuard and ReTrace. + * + * @author Eric Lafortune + */ +public class ProGuardGUI extends JFrame +{ + private static final String NO_SPLASH_OPTION = "-nosplash"; + + private static final String TITLE_IMAGE_FILE = "vtitle.png"; + private static final String BOILERPLATE_CONFIGURATION = "boilerplate.pro"; + private static final String DEFAULT_CONFIGURATION = "default.pro"; + + private static final String OPTIMIZATIONS_DEFAULT = "*"; + private static final String KEEP_ATTRIBUTE_DEFAULT = "Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,LocalVariable*Table,*Annotation*,Synthetic,EnclosingMethod"; + private static final String SOURCE_FILE_ATTRIBUTE_DEFAULT = "SourceFile"; + private static final String ADAPT_RESOURCE_FILE_NAMES_DEFAULT = "**.properties"; + private static final String ADAPT_RESOURCE_FILE_CONTENTS_DEFAULT = "**.properties,META-INF/MANIFEST.MF"; + + private static final Border BORDER = BorderFactory.createEtchedBorder(EtchedBorder.RAISED); + + static boolean systemOutRedirected; + + private final JFileChooser configurationChooser = new JFileChooser(""); + private final JFileChooser fileChooser = new JFileChooser(""); + + private final SplashPanel splashPanel; + + private final ClassPathPanel programPanel = new ClassPathPanel(this, true); + private final ClassPathPanel libraryPanel = new ClassPathPanel(this, false); + + private KeepClassSpecification[] boilerplateKeep; + private final JCheckBox[] boilerplateKeepCheckBoxes; + private final JTextField[] boilerplateKeepTextFields; + + private final KeepSpecificationsPanel additionalKeepPanel = new KeepSpecificationsPanel(this, true, false, false, false, false); + + private KeepClassSpecification[] boilerplateKeepNames; + private final JCheckBox[] boilerplateKeepNamesCheckBoxes; + private final JTextField[] boilerplateKeepNamesTextFields; + + private final KeepSpecificationsPanel additionalKeepNamesPanel = new KeepSpecificationsPanel(this, true, false, true, false, false); + + private ClassSpecification[] boilerplateNoSideEffectMethods; + private final JCheckBox[] boilerplateNoSideEffectMethodCheckBoxes; + + private final ClassSpecificationsPanel additionalNoSideEffectsPanel = new ClassSpecificationsPanel(this, false); + + private final ClassSpecificationsPanel whyAreYouKeepingPanel = new ClassSpecificationsPanel(this, false); + + private final JCheckBox shrinkCheckBox = new JCheckBox(msg("shrink")); + private final JCheckBox printUsageCheckBox = new JCheckBox(msg("printUsage")); + + private final JCheckBox optimizeCheckBox = new JCheckBox(msg("optimize")); + private final JCheckBox allowAccessModificationCheckBox = new JCheckBox(msg("allowAccessModification")); + private final JCheckBox mergeInterfacesAggressivelyCheckBox = new JCheckBox(msg("mergeInterfacesAggressively")); + private final JLabel optimizationsLabel = new JLabel(msg("optimizations")); + private final JLabel optimizationPassesLabel = new JLabel(msg("optimizationPasses")); + + private final JSpinner optimizationPassesSpinner = new JSpinner(new SpinnerNumberModel(1, 1, 9, 1)); + + private final JCheckBox obfuscateCheckBox = new JCheckBox(msg("obfuscate")); + private final JCheckBox printMappingCheckBox = new JCheckBox(msg("printMapping")); + private final JCheckBox applyMappingCheckBox = new JCheckBox(msg("applyMapping")); + private final JCheckBox obfuscationDictionaryCheckBox = new JCheckBox(msg("obfuscationDictionary")); + private final JCheckBox classObfuscationDictionaryCheckBox = new JCheckBox(msg("classObfuscationDictionary")); + private final JCheckBox packageObfuscationDictionaryCheckBox = new JCheckBox(msg("packageObfuscationDictionary")); + private final JCheckBox overloadAggressivelyCheckBox = new JCheckBox(msg("overloadAggressively")); + private final JCheckBox useUniqueClassMemberNamesCheckBox = new JCheckBox(msg("useUniqueClassMemberNames")); + private final JCheckBox useMixedCaseClassNamesCheckBox = new JCheckBox(msg("useMixedCaseClassNames")); + private final JCheckBox keepPackageNamesCheckBox = new JCheckBox(msg("keepPackageNames")); + private final JCheckBox flattenPackageHierarchyCheckBox = new JCheckBox(msg("flattenPackageHierarchy")); + private final JCheckBox repackageClassesCheckBox = new JCheckBox(msg("repackageClasses")); + private final JCheckBox keepAttributesCheckBox = new JCheckBox(msg("keepAttributes")); + private final JCheckBox newSourceFileAttributeCheckBox = new JCheckBox(msg("renameSourceFileAttribute")); + private final JCheckBox adaptClassStringsCheckBox = new JCheckBox(msg("adaptClassStrings")); + private final JCheckBox adaptResourceFileNamesCheckBox = new JCheckBox(msg("adaptResourceFileNames")); + private final JCheckBox adaptResourceFileContentsCheckBox = new JCheckBox(msg("adaptResourceFileContents")); + + private final JCheckBox preverifyCheckBox = new JCheckBox(msg("preverify")); + private final JCheckBox microEditionCheckBox = new JCheckBox(msg("microEdition")); + private final JCheckBox targetCheckBox = new JCheckBox(msg("target")); + + private final JComboBox targetComboBox = new JComboBox(ListUtil.commaSeparatedList(msg("targets")).toArray()); + + private final JCheckBox verboseCheckBox = new JCheckBox(msg("verbose")); + private final JCheckBox noteCheckBox = new JCheckBox(msg("note")); + private final JCheckBox warnCheckBox = new JCheckBox(msg("warn")); + private final JCheckBox ignoreWarningsCheckBox = new JCheckBox(msg("ignoreWarnings")); + private final JCheckBox skipNonPublicLibraryClassesCheckBox = new JCheckBox(msg("skipNonPublicLibraryClasses")); + private final JCheckBox skipNonPublicLibraryClassMembersCheckBox = new JCheckBox(msg("skipNonPublicLibraryClassMembers")); + private final JCheckBox keepDirectoriesCheckBox = new JCheckBox(msg("keepDirectories")); + private final JCheckBox forceProcessingCheckBox = new JCheckBox(msg("forceProcessing")); + private final JCheckBox printSeedsCheckBox = new JCheckBox(msg("printSeeds")); + private final JCheckBox printConfigurationCheckBox = new JCheckBox(msg("printConfiguration")); + private final JCheckBox dumpCheckBox = new JCheckBox(msg("dump")); + + private final JTextField printUsageTextField = new JTextField(40); + private final JTextField optimizationsTextField = new JTextField(40); + private final JTextField printMappingTextField = new JTextField(40); + private final JTextField applyMappingTextField = new JTextField(40); + private final JTextField obfuscationDictionaryTextField = new JTextField(40); + private final JTextField classObfuscationDictionaryTextField = new JTextField(40); + private final JTextField packageObfuscationDictionaryTextField = new JTextField(40); + private final JTextField keepPackageNamesTextField = new JTextField(40); + private final JTextField flattenPackageHierarchyTextField = new JTextField(40); + private final JTextField repackageClassesTextField = new JTextField(40); + private final JTextField keepAttributesTextField = new JTextField(40); + private final JTextField newSourceFileAttributeTextField = new JTextField(40); + private final JTextField adaptClassStringsTextField = new JTextField(40); + private final JTextField adaptResourceFileNamesTextField = new JTextField(40); + private final JTextField adaptResourceFileContentsTextField = new JTextField(40); + private final JTextField noteTextField = new JTextField(40); + private final JTextField warnTextField = new JTextField(40); + private final JTextField keepDirectoriesTextField = new JTextField(40); + private final JTextField printSeedsTextField = new JTextField(40); + private final JTextField printConfigurationTextField = new JTextField(40); + private final JTextField dumpTextField = new JTextField(40); + + private final JTextArea consoleTextArea = new JTextArea(msg("processingInfo"), 3, 40); + + private final JCheckBox reTraceVerboseCheckBox = new JCheckBox(msg("verbose")); + private final JTextField reTraceMappingTextField = new JTextField(40); + private final JTextArea stackTraceTextArea = new JTextArea(3, 40); + private final JTextArea reTraceTextArea = new JTextArea(msg("reTraceInfo"), 3, 40); + + + /** + * Creates a new ProGuardGUI. + */ + public ProGuardGUI() + { + setTitle("ProGuard"); + setDefaultCloseOperation(EXIT_ON_CLOSE); + + // Create some constraints that can be reused. + GridBagConstraints constraints = new GridBagConstraints(); + constraints.anchor = GridBagConstraints.WEST; + constraints.insets = new Insets(0, 4, 0, 4); + + 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 splashPanelConstraints = new GridBagConstraints(); + splashPanelConstraints.gridwidth = GridBagConstraints.REMAINDER; + splashPanelConstraints.fill = GridBagConstraints.BOTH; + splashPanelConstraints.weightx = 1.0; + splashPanelConstraints.weighty = 0.02; + splashPanelConstraints.anchor = GridBagConstraints.NORTHWEST; + //splashPanelConstraints.insets = constraints.insets; + + GridBagConstraints welcomeTextAreaConstraints = new GridBagConstraints(); + welcomeTextAreaConstraints.gridwidth = GridBagConstraints.REMAINDER; + welcomeTextAreaConstraints.fill = GridBagConstraints.NONE; + welcomeTextAreaConstraints.weightx = 1.0; + welcomeTextAreaConstraints.weighty = 0.01; + welcomeTextAreaConstraints.anchor = GridBagConstraints.CENTER;//NORTHWEST; + welcomeTextAreaConstraints.insets = new Insets(20, 40, 20, 40); + + GridBagConstraints panelConstraints = new GridBagConstraints(); + panelConstraints.gridwidth = GridBagConstraints.REMAINDER; + panelConstraints.fill = GridBagConstraints.HORIZONTAL; + panelConstraints.weightx = 1.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 glueConstraints = new GridBagConstraints(); + glueConstraints.fill = GridBagConstraints.BOTH; + glueConstraints.weightx = 0.01; + glueConstraints.weighty = 0.01; + glueConstraints.anchor = GridBagConstraints.NORTHWEST; + glueConstraints.insets = constraints.insets; + + GridBagConstraints bottomButtonConstraints = new GridBagConstraints(); + bottomButtonConstraints.anchor = GridBagConstraints.SOUTHEAST; + bottomButtonConstraints.insets = new Insets(2, 2, 4, 6); + bottomButtonConstraints.ipadx = 10; + bottomButtonConstraints.ipady = 2; + + GridBagConstraints lastBottomButtonConstraints = new GridBagConstraints(); + lastBottomButtonConstraints.gridwidth = GridBagConstraints.REMAINDER; + lastBottomButtonConstraints.anchor = GridBagConstraints.SOUTHEAST; + lastBottomButtonConstraints.insets = bottomButtonConstraints.insets; + lastBottomButtonConstraints.ipadx = bottomButtonConstraints.ipadx; + lastBottomButtonConstraints.ipady = bottomButtonConstraints.ipady; + + // Leave room for a growBox on Mac OS X. + if (System.getProperty("os.name").toLowerCase().startsWith("mac os x")) + { + lastBottomButtonConstraints.insets = new Insets(2, 2, 4, 6 + 16); + } + + GridBagLayout layout = new GridBagLayout(); + + configurationChooser.addChoosableFileFilter( + new ExtensionFileFilter(msg("proExtension"), new String[] { ".pro" })); + + // Create the opening panel. + Sprite splash = + new CompositeSprite(new Sprite[] + { + new ColorSprite(new ConstantColor(Color.gray), + new FontSprite(new ConstantFont(new Font("sansserif", Font.BOLD, 90)), + new TextSprite(new ConstantString("ProGuard"), + new ConstantInt(160), + new LinearInt(-10, 120, new SmoothTiming(500, 1000))))), + + new ColorSprite(new ConstantColor(Color.white), + new FontSprite(new ConstantFont(new Font("sansserif", Font.BOLD, 45)), + new ShadowedSprite(new ConstantInt(3), + new ConstantInt(3), + new ConstantDouble(0.4), + new ConstantInt(1), + new CompositeSprite(new Sprite[] + { + new TextSprite(new ConstantString(msg("shrinking")), + new LinearInt(1000, 60, new SmoothTiming(1000, 2000)), + new ConstantInt(70)), + new TextSprite(new ConstantString(msg("optimization")), + new LinearInt(1000, 400, new SmoothTiming(1500, 2500)), + new ConstantInt(60)), + new TextSprite(new ConstantString(msg("obfuscation")), + new LinearInt(1000, 10, new SmoothTiming(2000, 3000)), + new ConstantInt(145)), + new TextSprite(new ConstantString(msg("preverification")), + new LinearInt(1000, 350, new SmoothTiming(2500, 3500)), + new ConstantInt(140)), + new FontSprite(new ConstantFont(new Font("sansserif", Font.BOLD, 30)), + new TextSprite(new TypeWriterString(msg("developed"), new LinearTiming(3500, 5500)), + new ConstantInt(250), + new ConstantInt(200))), + })))), + }); + splashPanel = new SplashPanel(splash, 0.5, 5500L); + splashPanel.setPreferredSize(new Dimension(0, 200)); + + JTextArea welcomeTextArea = new JTextArea(msg("proGuardInfo"), 18, 50); + welcomeTextArea.setOpaque(false); + welcomeTextArea.setEditable(false); + welcomeTextArea.setLineWrap(true); + welcomeTextArea.setWrapStyleWord(true); + welcomeTextArea.setPreferredSize(new Dimension(0, 0)); + welcomeTextArea.setBorder(new EmptyBorder(20, 20, 20, 20)); + addBorder(welcomeTextArea, "welcome"); + + JPanel proGuardPanel = new JPanel(layout); + proGuardPanel.add(splashPanel, splashPanelConstraints); + proGuardPanel.add(welcomeTextArea, welcomeTextAreaConstraints); + + // Create the input panel. + // TODO: properly clone the ClassPath objects. + // This is awkward to implement in the generic ListPanel.addElements(...) + // method, since the Object.clone() method is not public. + programPanel.addCopyToPanelButton("moveToLibraries", "moveToLibrariesTip", libraryPanel); + libraryPanel.addCopyToPanelButton("moveToProgram", "moveToProgramTip", programPanel); + + // Collect all buttons of these panels and make sure they are equally + // sized. + List panelButtons = new ArrayList(); + panelButtons.addAll(programPanel.getButtons()); + panelButtons.addAll(libraryPanel.getButtons()); + setCommonPreferredSize(panelButtons); + + addBorder(programPanel, "programJars" ); + addBorder(libraryPanel, "libraryJars" ); + + JPanel inputOutputPanel = new JPanel(layout); + inputOutputPanel.add(tip(programPanel, "programJarsTip"), stretchPanelConstraints); + inputOutputPanel.add(tip(libraryPanel, "libraryJarsTip"), stretchPanelConstraints); + + // Load the boiler plate options. + loadBoilerplateConfiguration(); + + // Create the boiler plate keep panels. + boilerplateKeepCheckBoxes = new JCheckBox[boilerplateKeep.length]; + boilerplateKeepTextFields = new JTextField[boilerplateKeep.length]; + + JButton printUsageBrowseButton = createBrowseButton(printUsageTextField, + msg("selectUsageFile")); + + JPanel shrinkingOptionsPanel = new JPanel(layout); + addBorder(shrinkingOptionsPanel, "options"); + + shrinkingOptionsPanel.add(tip(shrinkCheckBox, "shrinkTip"), constraintsLastStretch); + shrinkingOptionsPanel.add(tip(printUsageCheckBox, "printUsageTip"), constraints); + shrinkingOptionsPanel.add(tip(printUsageTextField, "outputFileTip"), constraintsStretch); + shrinkingOptionsPanel.add(tip(printUsageBrowseButton, "selectUsageFile"), constraintsLast); + + JPanel shrinkingPanel = new JPanel(layout); + + shrinkingPanel.add(shrinkingOptionsPanel, panelConstraints); + addClassSpecifications(extractClassSpecifications(boilerplateKeep), + shrinkingPanel, + boilerplateKeepCheckBoxes, + boilerplateKeepTextFields); + + addBorder(additionalKeepPanel, "keepAdditional"); + shrinkingPanel.add(tip(additionalKeepPanel, "keepAdditionalTip"), stretchPanelConstraints); + + // Create the boiler plate keep names panels. + boilerplateKeepNamesCheckBoxes = new JCheckBox[boilerplateKeepNames.length]; + boilerplateKeepNamesTextFields = new JTextField[boilerplateKeepNames.length]; + + JButton printMappingBrowseButton = createBrowseButton(printMappingTextField, + msg("selectPrintMappingFile")); + JButton applyMappingBrowseButton = createBrowseButton(applyMappingTextField, + msg("selectApplyMappingFile")); + JButton obfucationDictionaryBrowseButton = createBrowseButton(obfuscationDictionaryTextField, + msg("selectObfuscationDictionaryFile")); + JButton classObfucationDictionaryBrowseButton = createBrowseButton(classObfuscationDictionaryTextField, + msg("selectObfuscationDictionaryFile")); + JButton packageObfucationDictionaryBrowseButton = createBrowseButton(packageObfuscationDictionaryTextField, + msg("selectObfuscationDictionaryFile")); + + JPanel obfuscationOptionsPanel = new JPanel(layout); + addBorder(obfuscationOptionsPanel, "options"); + + obfuscationOptionsPanel.add(tip(obfuscateCheckBox, "obfuscateTip"), constraintsLastStretch); + obfuscationOptionsPanel.add(tip(printMappingCheckBox, "printMappingTip"), constraints); + obfuscationOptionsPanel.add(tip(printMappingTextField, "outputFileTip"), constraintsStretch); + obfuscationOptionsPanel.add(tip(printMappingBrowseButton, "selectPrintMappingFile"), constraintsLast); + obfuscationOptionsPanel.add(tip(applyMappingCheckBox, "applyMappingTip"), constraints); + obfuscationOptionsPanel.add(tip(applyMappingTextField, "inputFileTip"), constraintsStretch); + obfuscationOptionsPanel.add(tip(applyMappingBrowseButton, "selectApplyMappingFile"), constraintsLast); + obfuscationOptionsPanel.add(tip(obfuscationDictionaryCheckBox, "obfuscationDictionaryTip"), constraints); + obfuscationOptionsPanel.add(tip(obfuscationDictionaryTextField, "inputFileTip"), constraintsStretch); + obfuscationOptionsPanel.add(tip(obfucationDictionaryBrowseButton, "selectObfuscationDictionaryFile"), constraintsLast); + obfuscationOptionsPanel.add(tip(classObfuscationDictionaryCheckBox, "classObfuscationDictionaryTip"), constraints); + obfuscationOptionsPanel.add(tip(classObfuscationDictionaryTextField, "inputFileTip"), constraintsStretch); + obfuscationOptionsPanel.add(tip(classObfucationDictionaryBrowseButton, "selectObfuscationDictionaryFile"), constraintsLast); + obfuscationOptionsPanel.add(tip(packageObfuscationDictionaryCheckBox, "packageObfuscationDictionaryTip"), constraints); + obfuscationOptionsPanel.add(tip(packageObfuscationDictionaryTextField, "inputFileTip"), constraintsStretch); + obfuscationOptionsPanel.add(tip(packageObfucationDictionaryBrowseButton, "selectObfuscationDictionaryFile"), constraintsLast); + obfuscationOptionsPanel.add(tip(overloadAggressivelyCheckBox, "overloadAggressivelyTip"), constraintsLastStretch); + obfuscationOptionsPanel.add(tip(useUniqueClassMemberNamesCheckBox, "useUniqueClassMemberNamesTip"), constraintsLastStretch); + obfuscationOptionsPanel.add(tip(useMixedCaseClassNamesCheckBox, "useMixedCaseClassNamesTip"), constraintsLastStretch); + obfuscationOptionsPanel.add(tip(keepPackageNamesCheckBox, "keepPackageNamesTip"), constraints); + obfuscationOptionsPanel.add(tip(keepPackageNamesTextField, "packageNamesTip"), constraintsLastStretch); + obfuscationOptionsPanel.add(tip(flattenPackageHierarchyCheckBox, "flattenPackageHierarchyTip"), constraints); + obfuscationOptionsPanel.add(tip(flattenPackageHierarchyTextField, "packageTip"), constraintsLastStretch); + obfuscationOptionsPanel.add(tip(repackageClassesCheckBox, "repackageClassesTip"), constraints); + obfuscationOptionsPanel.add(tip(repackageClassesTextField, "packageTip"), constraintsLastStretch); + obfuscationOptionsPanel.add(tip(keepAttributesCheckBox, "keepAttributesTip"), constraints); + obfuscationOptionsPanel.add(tip(keepAttributesTextField, "attributesTip"), constraintsLastStretch); + obfuscationOptionsPanel.add(tip(newSourceFileAttributeCheckBox, "renameSourceFileAttributeTip"), constraints); + obfuscationOptionsPanel.add(tip(newSourceFileAttributeTextField, "sourceFileAttributeTip"), constraintsLastStretch); + obfuscationOptionsPanel.add(tip(adaptClassStringsCheckBox, "adaptClassStringsTip"), constraints); + obfuscationOptionsPanel.add(tip(adaptClassStringsTextField, "classNamesTip"), constraintsLastStretch); + obfuscationOptionsPanel.add(tip(adaptResourceFileNamesCheckBox, "adaptResourceFileNamesTip"), constraints); + obfuscationOptionsPanel.add(tip(adaptResourceFileNamesTextField, "fileNameFilterTip"), constraintsLastStretch); + obfuscationOptionsPanel.add(tip(adaptResourceFileContentsCheckBox, "adaptResourceFileContentsTip"), constraints); + obfuscationOptionsPanel.add(tip(adaptResourceFileContentsTextField, "fileNameFilterTip"), constraintsLastStretch); + + JPanel obfuscationPanel = new JPanel(layout); + + obfuscationPanel.add(obfuscationOptionsPanel, panelConstraints); + addClassSpecifications(extractClassSpecifications(boilerplateKeepNames), + obfuscationPanel, + boilerplateKeepNamesCheckBoxes, + boilerplateKeepNamesTextFields); + + addBorder(additionalKeepNamesPanel, "keepNamesAdditional"); + obfuscationPanel.add(tip(additionalKeepNamesPanel, "keepNamesAdditionalTip"), stretchPanelConstraints); + + // Create the boiler plate "no side effect methods" panels. + boilerplateNoSideEffectMethodCheckBoxes = new JCheckBox[boilerplateNoSideEffectMethods.length]; + + JPanel optimizationOptionsPanel = new JPanel(layout); + addBorder(optimizationOptionsPanel, "options"); + + JButton optimizationsButton = + createOptimizationsButton(optimizationsTextField); + + optimizationOptionsPanel.add(tip(optimizeCheckBox, "optimizeTip"), constraintsLastStretch); + optimizationOptionsPanel.add(tip(allowAccessModificationCheckBox, "allowAccessModificationTip"), constraintsLastStretch); + optimizationOptionsPanel.add(tip(mergeInterfacesAggressivelyCheckBox, "mergeInterfacesAggressivelyTip"), constraintsLastStretch); + optimizationOptionsPanel.add(tip(optimizationsLabel, "optimizationsTip"), constraints); + optimizationOptionsPanel.add(tip(optimizationsTextField, "optimizationsFilterTip"), constraintsStretch); + optimizationOptionsPanel.add(tip(optimizationsButton, "optimizationsSelectTip"), constraintsLast); + optimizationOptionsPanel.add(tip(optimizationPassesLabel, "optimizationPassesTip"), constraints); + optimizationOptionsPanel.add(tip(optimizationPassesSpinner, "optimizationPassesTip"), constraintsLast); + + JPanel optimizationPanel = new JPanel(layout); + + optimizationPanel.add(optimizationOptionsPanel, panelConstraints); + addClassSpecifications(boilerplateNoSideEffectMethods, + optimizationPanel, + boilerplateNoSideEffectMethodCheckBoxes, + null); + + addBorder(additionalNoSideEffectsPanel, "assumeNoSideEffectsAdditional"); + optimizationPanel.add(tip(additionalNoSideEffectsPanel, "assumeNoSideEffectsAdditionalTip"), stretchPanelConstraints); + + // Create the options panel. + JPanel preverificationOptionsPanel = new JPanel(layout); + addBorder(preverificationOptionsPanel, "preverificationAndTargeting"); + + preverificationOptionsPanel.add(tip(preverifyCheckBox, "preverifyTip"), constraintsLastStretch); + preverificationOptionsPanel.add(tip(microEditionCheckBox, "microEditionTip"), constraintsLastStretch); + preverificationOptionsPanel.add(tip(targetCheckBox, "targetTip"), constraints); + preverificationOptionsPanel.add(tip(targetComboBox, "targetTip"), constraintsLast); + + JButton printSeedsBrowseButton = + createBrowseButton(printSeedsTextField, msg("selectSeedsFile")); + + JButton printConfigurationBrowseButton = + createBrowseButton(printConfigurationTextField, msg( "selectConfigurationFile")); + + JButton dumpBrowseButton = + createBrowseButton(dumpTextField, msg("selectDumpFile")); + + // Select the most recent target by default. + targetComboBox.setSelectedIndex(targetComboBox.getItemCount() - 1); + + JPanel consistencyPanel = new JPanel(layout); + addBorder(consistencyPanel, "consistencyAndCorrectness"); + + consistencyPanel.add(tip(verboseCheckBox, "verboseTip"), constraintsLastStretch); + consistencyPanel.add(tip(noteCheckBox, "noteTip"), constraints); + consistencyPanel.add(tip(noteTextField, "noteFilterTip"), constraintsLastStretch); + consistencyPanel.add(tip(warnCheckBox, "warnTip"), constraints); + consistencyPanel.add(tip(warnTextField, "warnFilterTip"), constraintsLastStretch); + consistencyPanel.add(tip(ignoreWarningsCheckBox, "ignoreWarningsTip"), constraintsLastStretch); + consistencyPanel.add(tip(skipNonPublicLibraryClassesCheckBox, "skipNonPublicLibraryClassesTip"), constraintsLastStretch); + consistencyPanel.add(tip(skipNonPublicLibraryClassMembersCheckBox, "skipNonPublicLibraryClassMembersTip"), constraintsLastStretch); + consistencyPanel.add(tip(keepDirectoriesCheckBox, "keepDirectoriesTip"), constraints); + consistencyPanel.add(tip(keepDirectoriesTextField, "directoriesTip"), constraintsLastStretch); + consistencyPanel.add(tip(forceProcessingCheckBox, "forceProcessingTip"), constraintsLastStretch); + consistencyPanel.add(tip(printSeedsCheckBox, "printSeedsTip"), constraints); + consistencyPanel.add(tip(printSeedsTextField, "outputFileTip"), constraintsStretch); + consistencyPanel.add(tip(printSeedsBrowseButton, "selectSeedsFile"), constraintsLast); + consistencyPanel.add(tip(printConfigurationCheckBox, "printConfigurationTip"), constraints); + consistencyPanel.add(tip(printConfigurationTextField, "outputFileTip"), constraintsStretch); + consistencyPanel.add(tip(printConfigurationBrowseButton, "selectConfigurationFile"), constraintsLast); + consistencyPanel.add(tip(dumpCheckBox, "dumpTip"), constraints); + consistencyPanel.add(tip(dumpTextField, "outputFileTip"), constraintsStretch); + consistencyPanel.add(tip(dumpBrowseButton, "selectDumpFile"), constraintsLast); + + // Collect all components that are followed by text fields and make + // sure they are equally sized. That way the text fields start at the + // same horizontal position. + setCommonPreferredSize(Arrays.asList(new JComponent[] { + printMappingCheckBox, + applyMappingCheckBox, + flattenPackageHierarchyCheckBox, + repackageClassesCheckBox, + newSourceFileAttributeCheckBox, + })); + + JPanel optionsPanel = new JPanel(layout); + + optionsPanel.add(preverificationOptionsPanel, panelConstraints); + optionsPanel.add(consistencyPanel, panelConstraints); + + addBorder(whyAreYouKeepingPanel, "whyAreYouKeeping"); + optionsPanel.add(tip(whyAreYouKeepingPanel, "whyAreYouKeepingTip"), stretchPanelConstraints); + + // Create the process panel. + consoleTextArea.setOpaque(false); + consoleTextArea.setEditable(false); + consoleTextArea.setLineWrap(false); + consoleTextArea.setWrapStyleWord(false); + JScrollPane consoleScrollPane = new JScrollPane(consoleTextArea); + consoleScrollPane.setBorder(new EmptyBorder(1, 1, 1, 1)); + addBorder(consoleScrollPane, "processingConsole"); + + JPanel processPanel = new JPanel(layout); + processPanel.add(consoleScrollPane, stretchPanelConstraints); + + // Create the load, save, and process buttons. + JButton loadButton = new JButton(msg("loadConfiguration")); + loadButton.addActionListener(new MyLoadConfigurationActionListener()); + + JButton viewButton = new JButton(msg("viewConfiguration")); + viewButton.addActionListener(new MyViewConfigurationActionListener()); + + JButton saveButton = new JButton(msg("saveConfiguration")); + saveButton.addActionListener(new MySaveConfigurationActionListener()); + + JButton processButton = new JButton(msg("process")); + processButton.addActionListener(new MyProcessActionListener()); + + // Create the ReTrace panel. + JPanel reTraceSettingsPanel = new JPanel(layout); + addBorder(reTraceSettingsPanel, "reTraceSettings"); + + JButton reTraceMappingBrowseButton = createBrowseButton(reTraceMappingTextField, + msg("selectApplyMappingFile")); + + JLabel reTraceMappingLabel = new JLabel(msg("mappingFile")); + reTraceMappingLabel.setForeground(reTraceVerboseCheckBox.getForeground()); + + reTraceSettingsPanel.add(tip(reTraceVerboseCheckBox, "verboseTip"), constraintsLastStretch); + reTraceSettingsPanel.add(tip(reTraceMappingLabel, "mappingFileTip"), constraints); + reTraceSettingsPanel.add(tip(reTraceMappingTextField, "inputFileTip"), constraintsStretch); + reTraceSettingsPanel.add(tip(reTraceMappingBrowseButton, "selectApplyMappingFile"), constraintsLast); + + stackTraceTextArea.setOpaque(true); + stackTraceTextArea.setEditable(true); + stackTraceTextArea.setLineWrap(false); + stackTraceTextArea.setWrapStyleWord(true); + JScrollPane stackTraceScrollPane = new JScrollPane(stackTraceTextArea); + addBorder(stackTraceScrollPane, "obfuscatedStackTrace"); + + reTraceTextArea.setOpaque(false); + reTraceTextArea.setEditable(false); + reTraceTextArea.setLineWrap(true); + reTraceTextArea.setWrapStyleWord(true); + JScrollPane reTraceScrollPane = new JScrollPane(reTraceTextArea); + reTraceScrollPane.setBorder(new EmptyBorder(1, 1, 1, 1)); + addBorder(reTraceScrollPane, "deobfuscatedStackTrace"); + + JPanel reTracePanel = new JPanel(layout); + reTracePanel.add(reTraceSettingsPanel, panelConstraints); + reTracePanel.add(tip(stackTraceScrollPane, "obfuscatedStackTraceTip"), panelConstraints); + reTracePanel.add(reTraceScrollPane, stretchPanelConstraints); + + // Create the load button. + JButton loadStackTraceButton = new JButton(msg("loadStackTrace")); + loadStackTraceButton.addActionListener(new MyLoadStackTraceActionListener()); + + JButton reTraceButton = new JButton(msg("reTrace")); + reTraceButton.addActionListener(new MyReTraceActionListener()); + + // Create the main tabbed pane. + TabbedPane tabs = new TabbedPane(); + tabs.add(msg("proGuardTab"), proGuardPanel); + tabs.add(msg("inputOutputTab"), inputOutputPanel); + tabs.add(msg("shrinkingTab"), shrinkingPanel); + tabs.add(msg("obfuscationTab"), obfuscationPanel); + tabs.add(msg("optimizationTab"), optimizationPanel); + tabs.add(msg("informationTab"), optionsPanel); + tabs.add(msg("processTab"), processPanel); + tabs.add(msg("reTraceTab"), reTracePanel); + tabs.addImage(Toolkit.getDefaultToolkit().getImage( + this.getClass().getResource(TITLE_IMAGE_FILE))); + + // Add the bottom buttons to each panel. + proGuardPanel .add(Box.createGlue(), glueConstraints); + proGuardPanel .add(tip(loadButton, "loadConfigurationTip"), bottomButtonConstraints); + proGuardPanel .add(createNextButton(tabs), lastBottomButtonConstraints); + + inputOutputPanel .add(Box.createGlue(), glueConstraints); + inputOutputPanel .add(createPreviousButton(tabs), bottomButtonConstraints); + inputOutputPanel .add(createNextButton(tabs), lastBottomButtonConstraints); + + shrinkingPanel .add(Box.createGlue(), glueConstraints); + shrinkingPanel .add(createPreviousButton(tabs), bottomButtonConstraints); + shrinkingPanel .add(createNextButton(tabs), lastBottomButtonConstraints); + + obfuscationPanel .add(Box.createGlue(), glueConstraints); + obfuscationPanel .add(createPreviousButton(tabs), bottomButtonConstraints); + obfuscationPanel .add(createNextButton(tabs), lastBottomButtonConstraints); + + optimizationPanel .add(Box.createGlue(), glueConstraints); + optimizationPanel .add(createPreviousButton(tabs), bottomButtonConstraints); + optimizationPanel .add(createNextButton(tabs), lastBottomButtonConstraints); + + optionsPanel .add(Box.createGlue(), glueConstraints); + optionsPanel .add(createPreviousButton(tabs), bottomButtonConstraints); + optionsPanel .add(createNextButton(tabs), lastBottomButtonConstraints); + + processPanel .add(Box.createGlue(), glueConstraints); + processPanel .add(createPreviousButton(tabs), bottomButtonConstraints); + processPanel .add(tip(viewButton, "viewConfigurationTip"), bottomButtonConstraints); + processPanel .add(tip(saveButton, "saveConfigurationTip"), bottomButtonConstraints); + processPanel .add(tip(processButton, "processTip"), lastBottomButtonConstraints); + + reTracePanel .add(Box.createGlue(), glueConstraints); + reTracePanel .add(tip(loadStackTraceButton, "loadStackTraceTip"), bottomButtonConstraints); + reTracePanel .add(tip(reTraceButton, "reTraceTip"), lastBottomButtonConstraints); + + // Initialize the GUI settings to reasonable defaults. + loadConfiguration(this.getClass().getResource(DEFAULT_CONFIGURATION)); + + // Add the main tabs to the frame and pack it. + getContentPane().add(tabs); + } + + + public void startSplash() + { + splashPanel.start(); + } + + + public void skipSplash() + { + splashPanel.stop(); + } + + + /** + * Loads the boilerplate keep class options from the boilerplate file + * into the boilerplate array. + */ + private void loadBoilerplateConfiguration() + { + try + { + // Parse the boilerplate configuration file. + ConfigurationParser parser = new ConfigurationParser( + this.getClass().getResource(BOILERPLATE_CONFIGURATION)); + Configuration configuration = new Configuration(); + + try + { + parser.parse(configuration); + + // We're interested in the keep options. + boilerplateKeep = + extractKeepSpecifications(configuration.keep, false, false); + + // We're interested in the keep options. + boilerplateKeepNames = + extractKeepSpecifications(configuration.keep, true, false); + + // We're interested in the side effects options. + boilerplateNoSideEffectMethods = new ClassSpecification[configuration.assumeNoSideEffects.size()]; + configuration.assumeNoSideEffects.toArray(boilerplateNoSideEffectMethods); + } + finally + { + parser.close(); + } + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + + + /** + * Returns an array containing the ClassSpecifications instances with + * matching flags. + */ + private KeepClassSpecification[] extractKeepSpecifications(List keepSpecifications, + boolean allowShrinking, + boolean allowObfuscation) + { + List matches = new ArrayList(); + + for (int index = 0; index < keepSpecifications.size(); index++) + { + KeepClassSpecification keepClassSpecification = (KeepClassSpecification)keepSpecifications.get(index); + if (keepClassSpecification.allowShrinking == allowShrinking && + keepClassSpecification.allowObfuscation == allowObfuscation) + { + matches.add(keepClassSpecification); + } + } + + KeepClassSpecification[] matchingKeepClassSpecifications = new KeepClassSpecification[matches.size()]; + matches.toArray(matchingKeepClassSpecifications); + + return matchingKeepClassSpecifications; + } + + + /** + * Returns an array containing the ClassSpecification instances of the + * given array of KeepClassSpecification instances. + */ + private ClassSpecification[] extractClassSpecifications(KeepClassSpecification[] keepClassSpecifications) + { + ClassSpecification[] classSpecifications = new ClassSpecification[keepClassSpecifications.length]; + + for (int index = 0; index < classSpecifications.length; index++) + { + classSpecifications[index] = keepClassSpecifications[index]; + } + + return classSpecifications; + } + + + /** + * Creates a panel with the given boiler plate class specifications. + */ + private void addClassSpecifications(ClassSpecification[] boilerplateClassSpecifications, + JPanel classSpecificationsPanel, + JCheckBox[] boilerplateCheckBoxes, + JTextField[] boilerplateTextFields) + { + // Create some constraints that can be reused. + GridBagConstraints constraints = new GridBagConstraints(); + constraints.anchor = GridBagConstraints.WEST; + constraints.insets = new Insets(0, 4, 0, 4); + + 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.anchor = GridBagConstraints.NORTHWEST; + panelConstraints.insets = constraints.insets; + + GridBagLayout layout = new GridBagLayout(); + + String lastPanelName = null; + JPanel keepSubpanel = null; + for (int index = 0; index < boilerplateClassSpecifications.length; index++) + { + // The panel structure is derived from the comments. + String comments = boilerplateClassSpecifications[index].comments; + int dashIndex = comments.indexOf('-'); + int periodIndex = comments.indexOf('.', dashIndex); + String panelName = comments.substring(0, dashIndex).trim(); + String optionName = comments.substring(dashIndex + 1, periodIndex).replace('_', '.').trim(); + String toolTip = comments.substring(periodIndex + 1); + if (keepSubpanel == null || !panelName.equals(lastPanelName)) + { + // Create a new keep subpanel and add it. + keepSubpanel = new JPanel(layout); + keepSubpanel.setBorder(BorderFactory.createTitledBorder(BORDER, panelName)); + classSpecificationsPanel.add(keepSubpanel, panelConstraints); + + lastPanelName = panelName; + } + + // Add the check box to the subpanel. + JCheckBox boilerplateCheckBox = new JCheckBox(optionName); + boilerplateCheckBox.setToolTipText(toolTip); + boilerplateCheckBoxes[index] = boilerplateCheckBox; + keepSubpanel.add(boilerplateCheckBox, + boilerplateTextFields != null ? + constraints : + constraintsLastStretch); + + if (boilerplateTextFields != null) + { + // Add the text field to the subpanel. + boilerplateTextFields[index] = new JTextField(40); + keepSubpanel.add(tip(boilerplateTextFields[index], "classNamesTip"), constraintsLastStretch); + } + } + } + + + /** + * Adds a standard border with the title that corresponds to the given key + * in the GUI resources. + */ + private void addBorder(JComponent component, String titleKey) + { + Border oldBorder = component.getBorder(); + Border newBorder = BorderFactory.createTitledBorder(BORDER, msg(titleKey)); + + component.setBorder(oldBorder == null ? + newBorder : + new CompoundBorder(newBorder, oldBorder)); + } + + + /** + * Creates a Previous button for the given tabbed pane. + */ + private JButton createPreviousButton(final TabbedPane tabbedPane) + { + JButton browseButton = new JButton(msg("previous")); + browseButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + tabbedPane.previous(); + } + }); + + return browseButton; + } + + + /** + * Creates a Next button for the given tabbed pane. + */ + private JButton createNextButton(final TabbedPane tabbedPane) + { + JButton browseButton = new JButton(msg("next")); + browseButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + tabbedPane.next(); + } + }); + + return browseButton; + } + + + /** + * Creates a browse button that opens a file browser for the given text field. + */ + private JButton createBrowseButton(final JTextField textField, + final String title) + { + JButton browseButton = new JButton(msg("browse")); + browseButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + // Update the file chooser. + fileChooser.setDialogTitle(title); + fileChooser.setSelectedFile(new File(textField.getText())); + + int returnVal = fileChooser.showDialog(ProGuardGUI.this, msg("ok")); + if (returnVal == JFileChooser.APPROVE_OPTION) + { + // Update the text field. + textField.setText(fileChooser.getSelectedFile().getPath()); + } + } + }); + + return browseButton; + } + + + protected JButton createOptimizationsButton(final JTextField textField) + { + final OptimizationsDialog optimizationsDialog = new OptimizationsDialog(ProGuardGUI.this); + + JButton optimizationsButton = new JButton(msg("select")); + optimizationsButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + // Update the dialog. + optimizationsDialog.setFilter(textField.getText()); + + int returnValue = optimizationsDialog.showDialog(); + if (returnValue == OptimizationsDialog.APPROVE_OPTION) + { + // Update the text field. + textField.setText(optimizationsDialog.getFilter()); + } + } + }); + + return optimizationsButton; + } + + + /** + * Sets the preferred sizes of the given components to the maximum of their + * current preferred sizes. + */ + private void setCommonPreferredSize(List components) + { + // Find the maximum preferred size. + Dimension maximumSize = null; + for (int index = 0; index < components.size(); index++) + { + JComponent component = (JComponent)components.get(index); + Dimension size = component.getPreferredSize(); + if (maximumSize == null || + size.getWidth() > maximumSize.getWidth()) + { + maximumSize = size; + } + } + + // Set the size that we found as the preferred size for all components. + for (int index = 0; index < components.size(); index++) + { + JComponent component = (JComponent)components.get(index); + component.setPreferredSize(maximumSize); + } + } + + + /** + * Updates to GUI settings to reflect the given ProGuard configuration. + */ + private void setProGuardConfiguration(Configuration configuration) + { + // Set up the input and output jars and directories. + programPanel.setClassPath(configuration.programJars); + libraryPanel.setClassPath(configuration.libraryJars); + + // Set up the boilerplate keep options. + for (int index = 0; index < boilerplateKeep.length; index++) + { + String classNames = + findMatchingKeepSpecifications(boilerplateKeep[index], + configuration.keep); + + boilerplateKeepCheckBoxes[index].setSelected(classNames != null); + boilerplateKeepTextFields[index].setText(classNames == null ? "*" : classNames); + } + + + // Set up the boilerplate keep names options. + for (int index = 0; index < boilerplateKeepNames.length; index++) + { + String classNames = + findMatchingKeepSpecifications(boilerplateKeepNames[index], + configuration.keep); + + boilerplateKeepNamesCheckBoxes[index].setSelected(classNames != null); + boilerplateKeepNamesTextFields[index].setText(classNames == null ? "*" : classNames); + } + + // Set up the additional keep options. Note that the matched boilerplate + // options have been removed from the list. + additionalKeepPanel.setClassSpecifications(filteredKeepSpecifications(configuration.keep, + false)); + + // Set up the additional keep options. Note that the matched boilerplate + // options have been removed from the list. + additionalKeepNamesPanel.setClassSpecifications(filteredKeepSpecifications(configuration.keep, + true)); + + + // Set up the boilerplate "no side effect methods" options. + for (int index = 0; index < boilerplateNoSideEffectMethods.length; index++) + { + boolean found = + findClassSpecification(boilerplateNoSideEffectMethods[index], + configuration.assumeNoSideEffects); + + boilerplateNoSideEffectMethodCheckBoxes[index].setSelected(found); + } + + // Set up the additional keep options. Note that the matched boilerplate + // options have been removed from the list. + additionalNoSideEffectsPanel.setClassSpecifications(configuration.assumeNoSideEffects); + + // Set up the "why are you keeping" options. + whyAreYouKeepingPanel.setClassSpecifications(configuration.whyAreYouKeeping); + + // Set up the other options. + shrinkCheckBox .setSelected(configuration.shrink); + printUsageCheckBox .setSelected(configuration.printUsage != null); + + optimizeCheckBox .setSelected(configuration.optimize); + allowAccessModificationCheckBox .setSelected(configuration.allowAccessModification); + mergeInterfacesAggressivelyCheckBox .setSelected(configuration.mergeInterfacesAggressively); + optimizationPassesSpinner.getModel() .setValue(new Integer(configuration.optimizationPasses)); + + obfuscateCheckBox .setSelected(configuration.obfuscate); + printMappingCheckBox .setSelected(configuration.printMapping != null); + applyMappingCheckBox .setSelected(configuration.applyMapping != null); + obfuscationDictionaryCheckBox .setSelected(configuration.obfuscationDictionary != null); + classObfuscationDictionaryCheckBox .setSelected(configuration.classObfuscationDictionary != null); + packageObfuscationDictionaryCheckBox .setSelected(configuration.packageObfuscationDictionary != null); + overloadAggressivelyCheckBox .setSelected(configuration.overloadAggressively); + useUniqueClassMemberNamesCheckBox .setSelected(configuration.useUniqueClassMemberNames); + useMixedCaseClassNamesCheckBox .setSelected(configuration.useMixedCaseClassNames); + keepPackageNamesCheckBox .setSelected(configuration.keepPackageNames != null); + flattenPackageHierarchyCheckBox .setSelected(configuration.flattenPackageHierarchy != null); + repackageClassesCheckBox .setSelected(configuration.repackageClasses != null); + keepAttributesCheckBox .setSelected(configuration.keepAttributes != null); + newSourceFileAttributeCheckBox .setSelected(configuration.newSourceFileAttribute != null); + adaptClassStringsCheckBox .setSelected(configuration.adaptClassStrings != null); + adaptResourceFileNamesCheckBox .setSelected(configuration.adaptResourceFileNames != null); + adaptResourceFileContentsCheckBox .setSelected(configuration.adaptResourceFileContents != null); + + preverifyCheckBox .setSelected(configuration.preverify); + microEditionCheckBox .setSelected(configuration.microEdition); + targetCheckBox .setSelected(configuration.targetClassVersion != 0); + + verboseCheckBox .setSelected(configuration.verbose); + noteCheckBox .setSelected(configuration.note == null || !configuration.note.isEmpty()); + warnCheckBox .setSelected(configuration.warn == null || !configuration.warn.isEmpty()); + ignoreWarningsCheckBox .setSelected(configuration.ignoreWarnings); + skipNonPublicLibraryClassesCheckBox .setSelected(configuration.skipNonPublicLibraryClasses); + skipNonPublicLibraryClassMembersCheckBox.setSelected(configuration.skipNonPublicLibraryClassMembers); + keepDirectoriesCheckBox .setSelected(configuration.keepDirectories != null); + forceProcessingCheckBox .setSelected(configuration.lastModified == Long.MAX_VALUE); + printSeedsCheckBox .setSelected(configuration.printSeeds != null); + printConfigurationCheckBox .setSelected(configuration.printConfiguration != null); + dumpCheckBox .setSelected(configuration.dump != null); + + printUsageTextField .setText(fileName(configuration.printUsage)); + optimizationsTextField .setText(configuration.optimizations == null ? OPTIMIZATIONS_DEFAULT : ListUtil.commaSeparatedString(configuration.optimizations)); + printMappingTextField .setText(fileName(configuration.printMapping)); + applyMappingTextField .setText(fileName(configuration.applyMapping)); + obfuscationDictionaryTextField .setText(fileName(configuration.obfuscationDictionary)); + keepPackageNamesTextField .setText(configuration.keepPackageNames == null ? "" : ClassUtil.externalClassName(ListUtil.commaSeparatedString(configuration.keepPackageNames))); + flattenPackageHierarchyTextField .setText(configuration.flattenPackageHierarchy); + repackageClassesTextField .setText(configuration.repackageClasses); + keepAttributesTextField .setText(configuration.keepAttributes == null ? KEEP_ATTRIBUTE_DEFAULT : ListUtil.commaSeparatedString(configuration.keepAttributes)); + newSourceFileAttributeTextField .setText(configuration.newSourceFileAttribute == null ? SOURCE_FILE_ATTRIBUTE_DEFAULT : configuration.newSourceFileAttribute); + adaptClassStringsTextField .setText(configuration.adaptClassStrings == null ? "" : ClassUtil.externalClassName(ListUtil.commaSeparatedString(configuration.adaptClassStrings))); + adaptResourceFileNamesTextField .setText(configuration.adaptResourceFileNames == null ? ADAPT_RESOURCE_FILE_NAMES_DEFAULT : ListUtil.commaSeparatedString(configuration.adaptResourceFileNames)); + adaptResourceFileContentsTextField .setText(configuration.adaptResourceFileContents == null ? ADAPT_RESOURCE_FILE_CONTENTS_DEFAULT : ListUtil.commaSeparatedString(configuration.adaptResourceFileContents)); + noteTextField .setText(ListUtil.commaSeparatedString(configuration.note)); + warnTextField .setText(ListUtil.commaSeparatedString(configuration.warn)); + keepDirectoriesTextField .setText(ListUtil.commaSeparatedString(configuration.keepDirectories)); + printSeedsTextField .setText(fileName(configuration.printSeeds)); + printConfigurationTextField .setText(fileName(configuration.printConfiguration)); + dumpTextField .setText(fileName(configuration.dump)); + + if (configuration.targetClassVersion != 0) + { + targetComboBox.setSelectedItem(ClassUtil.externalClassVersion(configuration.targetClassVersion)); + } + else + { + targetComboBox.setSelectedIndex(targetComboBox.getItemCount() - 1); + } + + if (configuration.printMapping != null) + { + reTraceMappingTextField.setText(fileName(configuration.printMapping)); + } + } + + + /** + * Returns the ProGuard configuration that reflects the current GUI settings. + */ + private Configuration getProGuardConfiguration() + { + Configuration configuration = new Configuration(); + + // Get the input and output jars and directories. + configuration.programJars = programPanel.getClassPath(); + configuration.libraryJars = libraryPanel.getClassPath(); + + List keep = new ArrayList(); + + // Collect the additional keep options. + List additionalKeep = additionalKeepPanel.getClassSpecifications(); + if (additionalKeep != null) + { + keep.addAll(additionalKeep); + } + + // Collect the additional keep names options. + List additionalKeepNames = additionalKeepNamesPanel.getClassSpecifications(); + if (additionalKeepNames != null) + { + keep.addAll(additionalKeepNames); + } + + // Collect the boilerplate keep options. + for (int index = 0; index < boilerplateKeep.length; index++) + { + if (boilerplateKeepCheckBoxes[index].isSelected()) + { + keep.add(classSpecification(boilerplateKeep[index], + boilerplateKeepTextFields[index].getText())); + } + } + + // Collect the boilerplate keep names options. + for (int index = 0; index < boilerplateKeepNames.length; index++) + { + if (boilerplateKeepNamesCheckBoxes[index].isSelected()) + { + keep.add(classSpecification(boilerplateKeepNames[index], + boilerplateKeepNamesTextFields[index].getText())); + } + } + + // Put the list of keep specifications in the configuration. + if (keep.size() > 0) + { + configuration.keep = keep; + } + + + // Collect the boilerplate "no side effect methods" options. + List noSideEffectMethods = new ArrayList(); + + for (int index = 0; index < boilerplateNoSideEffectMethods.length; index++) + { + if (boilerplateNoSideEffectMethodCheckBoxes[index].isSelected()) + { + noSideEffectMethods.add(boilerplateNoSideEffectMethods[index]); + } + } + + // Collect the additional "no side effect methods" options. + List additionalNoSideEffectOptions = additionalNoSideEffectsPanel.getClassSpecifications(); + if (additionalNoSideEffectOptions != null) + { + noSideEffectMethods.addAll(additionalNoSideEffectOptions); + } + + // Put the list of "no side effect methods" options in the configuration. + if (noSideEffectMethods.size() > 0) + { + configuration.assumeNoSideEffects = noSideEffectMethods; + } + + + // Collect the "why are you keeping" options. + configuration.whyAreYouKeeping = whyAreYouKeepingPanel.getClassSpecifications(); + + + // Get the other options. + configuration.shrink = shrinkCheckBox .isSelected(); + configuration.printUsage = printUsageCheckBox .isSelected() ? new File(printUsageTextField .getText()) : null; + + configuration.optimize = optimizeCheckBox .isSelected(); + configuration.allowAccessModification = allowAccessModificationCheckBox .isSelected(); + configuration.mergeInterfacesAggressively = mergeInterfacesAggressivelyCheckBox .isSelected(); + configuration.optimizations = optimizationsTextField.getText().length() > 1 ? ListUtil.commaSeparatedList(optimizationsTextField .getText()) : null; + configuration.optimizationPasses = ((SpinnerNumberModel)optimizationPassesSpinner.getModel()).getNumber().intValue(); + + configuration.obfuscate = obfuscateCheckBox .isSelected(); + configuration.printMapping = printMappingCheckBox .isSelected() ? new File(printMappingTextField .getText()) : null; + configuration.applyMapping = applyMappingCheckBox .isSelected() ? new File(applyMappingTextField .getText()) : null; + configuration.obfuscationDictionary = obfuscationDictionaryCheckBox .isSelected() ? new File(obfuscationDictionaryTextField .getText()) : null; + configuration.classObfuscationDictionary = classObfuscationDictionaryCheckBox .isSelected() ? new File(classObfuscationDictionaryTextField .getText()) : null; + configuration.packageObfuscationDictionary = packageObfuscationDictionaryCheckBox .isSelected() ? new File(packageObfuscationDictionaryTextField .getText()) : null; + configuration.overloadAggressively = overloadAggressivelyCheckBox .isSelected(); + configuration.useUniqueClassMemberNames = useUniqueClassMemberNamesCheckBox .isSelected(); + configuration.useMixedCaseClassNames = useMixedCaseClassNamesCheckBox .isSelected(); + configuration.keepPackageNames = keepPackageNamesCheckBox .isSelected() ? keepPackageNamesTextField.getText().length() > 0 ? ListUtil.commaSeparatedList(ClassUtil.internalClassName(keepPackageNamesTextField.getText())) : new ArrayList() : null; + configuration.flattenPackageHierarchy = flattenPackageHierarchyCheckBox .isSelected() ? ClassUtil.internalClassName(flattenPackageHierarchyTextField .getText()) : null; + configuration.repackageClasses = repackageClassesCheckBox .isSelected() ? ClassUtil.internalClassName(repackageClassesTextField .getText()) : null; + configuration.keepAttributes = keepAttributesCheckBox .isSelected() ? ListUtil.commaSeparatedList(keepAttributesTextField .getText()) : null; + configuration.newSourceFileAttribute = newSourceFileAttributeCheckBox .isSelected() ? newSourceFileAttributeTextField .getText() : null; + configuration.adaptClassStrings = adaptClassStringsCheckBox .isSelected() ? adaptClassStringsTextField.getText().length() > 0 ? ListUtil.commaSeparatedList(ClassUtil.internalClassName(adaptClassStringsTextField.getText())) : new ArrayList() : null; + configuration.adaptResourceFileNames = adaptResourceFileNamesCheckBox .isSelected() ? ListUtil.commaSeparatedList(adaptResourceFileNamesTextField .getText()) : null; + configuration.adaptResourceFileContents = adaptResourceFileContentsCheckBox .isSelected() ? ListUtil.commaSeparatedList(adaptResourceFileContentsTextField .getText()) : null; + + configuration.preverify = preverifyCheckBox .isSelected(); + configuration.microEdition = microEditionCheckBox .isSelected(); + configuration.targetClassVersion = targetCheckBox .isSelected() ? ClassUtil.internalClassVersion(targetComboBox.getSelectedItem().toString()) : 0; + + configuration.verbose = verboseCheckBox .isSelected(); + configuration.note = noteCheckBox .isSelected() ? noteTextField.getText().length() > 0 ? ListUtil.commaSeparatedList(ClassUtil.internalClassName(noteTextField.getText())) : null : new ArrayList(); + configuration.warn = warnCheckBox .isSelected() ? warnTextField.getText().length() > 0 ? ListUtil.commaSeparatedList(ClassUtil.internalClassName(warnTextField.getText())) : null : new ArrayList(); + configuration.ignoreWarnings = ignoreWarningsCheckBox .isSelected(); + configuration.skipNonPublicLibraryClasses = skipNonPublicLibraryClassesCheckBox .isSelected(); + configuration.skipNonPublicLibraryClassMembers = skipNonPublicLibraryClassMembersCheckBox.isSelected(); + configuration.keepDirectories = keepDirectoriesCheckBox .isSelected() ? keepDirectoriesTextField.getText().length() > 0 ? ListUtil.commaSeparatedList(ClassUtil.internalClassName(keepDirectoriesTextField.getText())) : new ArrayList() : null; + configuration.lastModified = forceProcessingCheckBox .isSelected() ? Long.MAX_VALUE : System.currentTimeMillis(); + configuration.printSeeds = printSeedsCheckBox .isSelected() ? new File(printSeedsTextField .getText()) : null; + configuration.printConfiguration = printConfigurationCheckBox .isSelected() ? new File(printConfigurationTextField .getText()) : null; + configuration.dump = dumpCheckBox .isSelected() ? new File(dumpTextField .getText()) : null; + + return configuration; + } + + + /** + * Looks in the given list for a class specification that is identical to + * the given template. Returns true if it is found, and removes the matching + * class specification as a side effect. + */ + private boolean findClassSpecification(ClassSpecification classSpecificationTemplate, + List classSpecifications) + { + if (classSpecifications == null) + { + return false; + } + + for (int index = 0; index < classSpecifications.size(); index++) + { + if (classSpecificationTemplate.equals(classSpecifications.get(index))) + { + // Remove the matching option as a side effect. + classSpecifications.remove(index); + + return true; + } + } + + return false; + } + + + /** + * Returns the subset of the given list of keep specifications, with + * matching shrinking flag. + */ + private List filteredKeepSpecifications(List keepSpecifications, + boolean allowShrinking) + { + List filteredKeepSpecifications = new ArrayList(); + + for (int index = 0; index < keepSpecifications.size(); index++) + { + KeepClassSpecification keepClassSpecification = + (KeepClassSpecification)keepSpecifications.get(index); + + if (keepClassSpecification.allowShrinking == allowShrinking) + { + filteredKeepSpecifications.add(keepClassSpecification); + } + } + + return filteredKeepSpecifications; + } + + + /** + * Looks in the given list for keep specifications that match the given + * template. Returns a comma-separated string of class names from + * matching keep specifications, and removes the matching keep + * specifications as a side effect. + */ + private String findMatchingKeepSpecifications(KeepClassSpecification keepClassSpecificationTemplate, + List keepSpecifications) + { + if (keepSpecifications == null) + { + return null; + } + + StringBuffer buffer = null; + + for (int index = 0; index < keepSpecifications.size(); index++) + { + KeepClassSpecification listedKeepClassSpecification = + (KeepClassSpecification)keepSpecifications.get(index); + String className = listedKeepClassSpecification.className; + keepClassSpecificationTemplate.className = className; + if (keepClassSpecificationTemplate.equals(listedKeepClassSpecification)) + { + if (buffer == null) + { + buffer = new StringBuffer(); + } + else + { + buffer.append(','); + } + buffer.append(className == null ? "*" : ClassUtil.externalClassName(className)); + + // Remove the matching option as a side effect. + keepSpecifications.remove(index--); + } + } + + return buffer == null ? null : buffer.toString(); + } + + + /** + * Returns a class specification or keep specification, based on the given + * template and the class name to be filled in. + */ + private ClassSpecification classSpecification(ClassSpecification classSpecificationTemplate, + String className) + { + // Create a copy of the template. + ClassSpecification classSpecification = + (ClassSpecification)classSpecificationTemplate.clone(); + + // Set the class name in the copy. + classSpecification.className = + className.equals("") || + className.equals("*") ? + null : + ClassUtil.internalClassName(className); + + // Return the modified copy. + return classSpecification; + } + + + // Methods and internal classes related to actions. + + /** + * Loads the given ProGuard configuration into the GUI. + */ + private void loadConfiguration(File file) + { + // Set the default directory and file in the file choosers. + configurationChooser.setSelectedFile(file.getAbsoluteFile()); + fileChooser.setCurrentDirectory(file.getAbsoluteFile().getParentFile()); + + try + { + // Parse the configuration file. + ConfigurationParser parser = new ConfigurationParser(file); + Configuration configuration = new Configuration(); + + try + { + parser.parse(configuration); + + // Let the GUI reflect the configuration. + setProGuardConfiguration(configuration); + } + catch (ParseException ex) + { + JOptionPane.showMessageDialog(getContentPane(), + msg("cantParseConfigurationFile", file.getPath()), + msg("warning"), + JOptionPane.ERROR_MESSAGE); + } + finally + { + parser.close(); + } + } + catch (IOException ex) + { + JOptionPane.showMessageDialog(getContentPane(), + msg("cantOpenConfigurationFile", file.getPath()), + msg("warning"), + JOptionPane.ERROR_MESSAGE); + } + } + + + /** + * Loads the given ProGuard configuration into the GUI. + */ + private void loadConfiguration(URL url) + { + try + { + // Parse the configuration file. + ConfigurationParser parser = new ConfigurationParser(url); + Configuration configuration = new Configuration(); + + try + { + parser.parse(configuration); + + // Let the GUI reflect the configuration. + setProGuardConfiguration(configuration); + } + catch (ParseException ex) + { + JOptionPane.showMessageDialog(getContentPane(), + msg("cantParseConfigurationFile", url), + msg("warning"), + JOptionPane.ERROR_MESSAGE); + } + finally + { + parser.close(); + } + } + catch (IOException ex) + { + JOptionPane.showMessageDialog(getContentPane(), + msg("cantOpenConfigurationFile", url), + msg("warning"), + JOptionPane.ERROR_MESSAGE); + } + } + + + /** + * Saves the current ProGuard configuration to the given file. + */ + private void saveConfiguration(File file) + { + try + { + // Save the configuration file. + ConfigurationWriter writer = new ConfigurationWriter(file); + writer.write(getProGuardConfiguration()); + writer.close(); + } + catch (Exception ex) + { + JOptionPane.showMessageDialog(getContentPane(), + msg("cantSaveConfigurationFile", file.getPath()), + msg("warning"), + JOptionPane.ERROR_MESSAGE); + } + } + + + /** + * Loads the given stack trace into the GUI. + */ + private void loadStackTrace(String fileName) + { + try + { + StringBuffer buffer = new StringBuffer(1024); + + Reader reader = new BufferedReader(new FileReader(fileName)); + try + { + while (true) + { + int c = reader.read(); + if (c < 0) + { + break; + } + + buffer.append(c); + } + } + finally + { + reader.close(); + } + + // Put the stack trace in the text area. + stackTraceTextArea.setText(buffer.toString()); + } + catch (IOException ex) + { + JOptionPane.showMessageDialog(getContentPane(), + msg("cantOpenStackTraceFile", fileName), + msg("warning"), + JOptionPane.ERROR_MESSAGE); + } + } + + + /** + * This ActionListener loads a ProGuard configuration file and initializes + * the GUI accordingly. + */ + private class MyLoadConfigurationActionListener implements ActionListener + { + public void actionPerformed(ActionEvent e) + { + configurationChooser.setDialogTitle(msg("selectConfigurationFile")); + + int returnValue = configurationChooser.showOpenDialog(ProGuardGUI.this); + if (returnValue == JFileChooser.APPROVE_OPTION) + { + loadConfiguration(configurationChooser.getSelectedFile()); + } + } + } + + + /** + * This ActionListener saves a ProGuard configuration file based on the + * current GUI settings. + */ + private class MySaveConfigurationActionListener implements ActionListener + { + public void actionPerformed(ActionEvent e) + { + configurationChooser.setDialogTitle(msg("saveConfigurationFile")); + + int returnVal = configurationChooser.showSaveDialog(ProGuardGUI.this); + if (returnVal == JFileChooser.APPROVE_OPTION) + { + saveConfiguration(configurationChooser.getSelectedFile()); + } + } + } + + + /** + * This ActionListener displays the ProGuard configuration specified by the + * current GUI settings. + */ + private class MyViewConfigurationActionListener implements ActionListener + { + public void actionPerformed(ActionEvent e) + { + // Make sure System.out has not been redirected yet. + if (!systemOutRedirected) + { + consoleTextArea.setText(""); + + TextAreaOutputStream outputStream = + new TextAreaOutputStream(consoleTextArea); + + try + { + // TODO: write out relative path names and path names with system properties. + + // Write the configuration. + ConfigurationWriter writer = new ConfigurationWriter(outputStream); + try + { + writer.write(getProGuardConfiguration()); + } + finally + { + writer.close(); + } + } + catch (IOException ex) + { + // This shouldn't happen. + } + + // Scroll to the top of the configuration. + consoleTextArea.setCaretPosition(0); + } + } + } + + + /** + * This ActionListener executes ProGuard based on the current GUI settings. + */ + private class MyProcessActionListener implements ActionListener + { + public void actionPerformed(ActionEvent e) + { + // Make sure System.out has not been redirected yet. + if (!systemOutRedirected) + { + systemOutRedirected = true; + + // Get the informational configuration file name. + File configurationFile = configurationChooser.getSelectedFile(); + String configurationFileName = configurationFile != null ? + configurationFile.getName() : + msg("sampleConfigurationFileName"); + + // Create the ProGuard thread. + Thread proGuardThread = + new Thread(new ProGuardRunnable(consoleTextArea, + getProGuardConfiguration(), + configurationFileName)); + + // Run it. + proGuardThread.start(); + } + } + } + + + /** + * This ActionListener loads an obfuscated stack trace from a file and puts + * it in the proper text area. + */ + private class MyLoadStackTraceActionListener implements ActionListener + { + public void actionPerformed(ActionEvent e) + { + fileChooser.setDialogTitle(msg("selectStackTraceFile")); + fileChooser.setSelectedFile(null); + + int returnValue = fileChooser.showOpenDialog(ProGuardGUI.this); + if (returnValue == JFileChooser.APPROVE_OPTION) + { + File selectedFile = fileChooser.getSelectedFile(); + String fileName = selectedFile.getPath(); + + loadStackTrace(fileName); + } + } + } + + + /** + * This ActionListener executes ReTrace based on the current GUI settings. + */ + private class MyReTraceActionListener implements ActionListener + { + public void actionPerformed(ActionEvent e) + { + // Make sure System.out has not been redirected yet. + if (!systemOutRedirected) + { + systemOutRedirected = true; + + boolean verbose = reTraceVerboseCheckBox.isSelected(); + File retraceMappingFile = new File(reTraceMappingTextField.getText()); + String stackTrace = stackTraceTextArea.getText(); + + // Create the ReTrace runnable. + Runnable reTraceRunnable = new ReTraceRunnable(reTraceTextArea, + verbose, + retraceMappingFile, + stackTrace); + + // Run it in this thread, because it won't take long anyway. + reTraceRunnable.run(); + } + } + } + + + // Small utility methods. + + /** + * Returns the file name of the given file, if any. + */ + private static String fileName(File file) + { + return file == null ? "" : file.getAbsolutePath(); + } + + + /** + * 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); + } + + + /** + * Returns the message from the GUI resources that corresponds to the given + * key and argument. + */ + private String msg(String messageKey, + Object messageArgument) + { + return GUIResources.getMessage(messageKey, new Object[] {messageArgument}); + } + + + /** + * The main method for the ProGuard GUI. + */ + public static void main(final String[] args) + { + try + { + SwingUtil.invokeAndWait(new Runnable() + { + public void run() + { + ProGuardGUI gui = new ProGuardGUI(); + gui.pack(); + + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + Dimension guiSize = gui.getSize(); + gui.setLocation((screenSize.width - guiSize.width) / 2, + (screenSize.height - guiSize.height) / 2); + gui.show(); + + // Start the splash animation, unless specified otherwise. + int argIndex = 0; + if (argIndex < args.length && + NO_SPLASH_OPTION.startsWith(args[argIndex])) + { + gui.skipSplash(); + argIndex++; + } + else + { + gui.startSplash(); + } + + // Load an initial configuration, if specified. + if (argIndex < args.length) + { + gui.loadConfiguration(new File(args[argIndex])); + argIndex++; + } + + if (argIndex < args.length) + { + System.out.println(gui.getClass().getName() + ": ignoring extra arguments [" + args[argIndex] + "...]"); + } + + } + }); + } + catch (Exception e) + { + // Nothing. + } + } +} diff --git a/src/proguard/gui/ProGuardRunnable.java b/src/proguard/gui/ProGuardRunnable.java new file mode 100644 index 0000000..c5c5937 --- /dev/null +++ b/src/proguard/gui/ProGuardRunnable.java @@ -0,0 +1,154 @@ +/* + * 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 javax.swing.*; +import java.awt.*; +import java.io.PrintStream; + + +/** + * This <code>Runnable</code> runs ProGuard, sending console output to a text + * area and any exceptions to message dialogs. + * + * @see ProGuard + * @author Eric Lafortune + */ +final class ProGuardRunnable implements Runnable +{ + private final JTextArea consoleTextArea; + private final Configuration configuration; + private final String configurationFileName; + + + /** + * Creates a new ProGuardRunnable object. + * @param consoleTextArea the text area to send the console output to. + * @param configuration the ProGuard configuration. + * @param configurationFileName the optional file name of the configuration, + * for informational purposes. + */ + public ProGuardRunnable(JTextArea consoleTextArea, + Configuration configuration, + String configurationFileName) + { + this.consoleTextArea = consoleTextArea; + this.configuration = configuration; + this.configurationFileName = configurationFileName; + } + + + // Implementation for Runnable. + + public void run() + { + consoleTextArea.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + consoleTextArea.setText(""); + + // Redirect the System's out and err streams to the console text area. + PrintStream oldOut = System.out; + PrintStream oldErr = System.err; + + PrintStream printStream = + new PrintStream(new TextAreaOutputStream(consoleTextArea), true); + + System.setOut(printStream); + System.setErr(printStream); + + try + { + // Create a new ProGuard object with the GUI's configuration. + ProGuard proGuard = new ProGuard(configuration); + + // Run it. + proGuard.execute(); + + // Print out the completion message. + System.out.println("Processing completed successfully"); + } + catch (Exception ex) + { + //ex.printStackTrace(); + + // Print out the exception message. + System.out.println(ex.getMessage()); + + // Show a dialog as well. + MessageDialogRunnable.showMessageDialog(consoleTextArea, + ex.getMessage(), + msg("errorProcessing"), + JOptionPane.ERROR_MESSAGE); + } + catch (OutOfMemoryError er) + { + // Forget about the ProGuard object as quickly as possible. + System.gc(); + + // Print out a message suggesting what to do next. + System.out.println(msg("outOfMemoryInfo", configurationFileName)); + + // Show a dialog as well. + MessageDialogRunnable.showMessageDialog(consoleTextArea, + msg("outOfMemory"), + msg("errorProcessing"), + JOptionPane.ERROR_MESSAGE); + } + finally + { + // Make sure all output has been sent to the console text area. + printStream.close(); + + // Restore the old System's out and err streams. + System.setOut(oldOut); + System.setErr(oldErr); + } + + consoleTextArea.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + + // Reset the global static redirection lock. + ProGuardGUI.systemOutRedirected = false; + } + + + // Small utility methods. + + /** + * Returns the message from the GUI resources that corresponds to the given + * key. + */ + private String msg(String messageKey) + { + return GUIResources.getMessage(messageKey); + } + + + /** + * Returns the message from the GUI resources that corresponds to the given + * key and argument. + */ + private String msg(String messageKey, + Object messageArgument) + { + return GUIResources.getMessage(messageKey, new Object[] {messageArgument}); + } +} diff --git a/src/proguard/gui/ReTraceRunnable.java b/src/proguard/gui/ReTraceRunnable.java new file mode 100644 index 0000000..1ca19ca --- /dev/null +++ b/src/proguard/gui/ReTraceRunnable.java @@ -0,0 +1,149 @@ +/* + * 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.retrace.ReTrace; + +import javax.swing.*; +import java.awt.*; +import java.io.*; + + +/** + * This <code>Runnable</code> runs ReTrace, sending console output to a text + * area and any exceptions to message dialogs. + * + * @see ReTrace + * @author Eric Lafortune + */ +final class ReTraceRunnable implements Runnable +{ + private final JTextArea consoleTextArea; + private final boolean verbose; + private final File mappingFile; + private final String stackTrace; + + + /** + * Creates a new ProGuardRunnable object. + * @param consoleTextArea the text area to send the console output to. + * @param verbose specifies whether the de-obfuscated stack trace + * should be verbose. + * @param mappingFile the mapping file that was written out by ProGuard. + */ + public ReTraceRunnable(JTextArea consoleTextArea, + boolean verbose, + File mappingFile, + String stackTrace) + { + this.consoleTextArea = consoleTextArea; + this.verbose = verbose; + this.mappingFile = mappingFile; + this.stackTrace = stackTrace; + } + + + // Implementation for Runnable. + + public void run() + { + consoleTextArea.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + consoleTextArea.setText(""); + + // Redirect the stack trace string to the System's in stream, and the + // out and err streams to the console text area. + InputStream oldIn = System.in; + PrintStream oldOut = System.out; + PrintStream oldErr = System.err; + + ByteArrayInputStream inputStream = + new ByteArrayInputStream(stackTrace.getBytes()); + + PrintStream printStream = + new PrintStream(new TextAreaOutputStream(consoleTextArea), true); + + System.setIn(inputStream); + System.setOut(printStream); + System.setErr(printStream); + + try + { + // Create a new ProGuard object with the GUI's configuration. + ReTrace reTrace = new ReTrace(ReTrace.STACK_TRACE_EXPRESSION, + verbose, + mappingFile); + + // Run it. + reTrace.execute(); + } + catch (Exception ex) + { + // Print out the exception message. + System.out.println(ex.getMessage()); + + // Show a dialog as well. + MessageDialogRunnable.showMessageDialog(consoleTextArea, + ex.getMessage(), + msg("errorReTracing"), + JOptionPane.ERROR_MESSAGE); + } + catch (OutOfMemoryError er) + { + // Forget about the ProGuard object as quickly as possible. + System.gc(); + + // Print out a message suggesting what to do next. + System.out.println(msg("outOfMemory")); + + // Show a dialog as well. + MessageDialogRunnable.showMessageDialog(consoleTextArea, + msg("outOfMemory"), + msg("errorReTracing"), + JOptionPane.ERROR_MESSAGE); + } + + // Make sure all output has been sent to the console text area. + printStream.flush(); + + // Restore the old System's in, out, and err streams. + System.setIn(oldIn); + System.setOut(oldOut); + System.setErr(oldErr); + + consoleTextArea.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + consoleTextArea.setCaretPosition(0); + + // Reset the global static redirection lock. + ProGuardGUI.systemOutRedirected = false; + } + + + // Small utility methods. + + /** + * Returns the message from the GUI resources that corresponds to the given + * key. + */ + private String msg(String messageKey) + { + return GUIResources.getMessage(messageKey); + } +} diff --git a/src/proguard/gui/SwingUtil.java b/src/proguard/gui/SwingUtil.java new file mode 100644 index 0000000..75d2f02 --- /dev/null +++ b/src/proguard/gui/SwingUtil.java @@ -0,0 +1,82 @@ +/* + * 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 javax.swing.*; +import java.lang.reflect.InvocationTargetException; + + +/** + * This utility class provides variants of the invocation method from the + * <code>SwingUtilities</code> class. + * + * @see SwingUtilities + * @author Eric Lafortune + */ +public class SwingUtil +{ + /** + * Invokes the given Runnable in the AWT event dispatching thread, + * and waits for it to finish. This method may be called from any thread, + * including the event dispatching thread itself. + * @see SwingUtilities#invokeAndWait(Runnable) + * @param runnable the Runnable to be executed. + */ + public static void invokeAndWait(Runnable runnable) + throws InterruptedException, InvocationTargetException + { + try + { + if (SwingUtilities.isEventDispatchThread()) + { + runnable.run(); + } + else + { + SwingUtilities.invokeAndWait(runnable); + } + } + catch (Exception ex) + { + // Ignore any exceptions. + } + } + + + /** + * Invokes the given Runnable in the AWT event dispatching thread, not + * necessarily right away. This method may be called from any thread, + * including the event dispatching thread itself. + * @see SwingUtilities#invokeLater(Runnable) + * @param runnable the Runnable to be executed. + */ + public static void invokeLater(Runnable runnable) + { + if (SwingUtilities.isEventDispatchThread()) + { + runnable.run(); + } + else + { + SwingUtilities.invokeLater(runnable); + } + } +} diff --git a/src/proguard/gui/TabbedPane.java b/src/proguard/gui/TabbedPane.java new file mode 100644 index 0000000..a6460f5 --- /dev/null +++ b/src/proguard/gui/TabbedPane.java @@ -0,0 +1,229 @@ +/* + * 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 javax.swing.*; +import java.awt.*; +import java.awt.event.*; + + +/** + * This <code>Jpanel</code> is similar to a <code>JTabbedPane</code>. + * It uses buttons on the left-hand side to switch between panels. + * An image can be added below these buttons. + * Some methods are provided to switch between tabs. + * + * @author Eric Lafortune + */ +public class TabbedPane + extends JPanel +{ + private final CardLayout cardLayout = new CardLayout(); + private final JPanel cardPanel = new JPanel(cardLayout); + private final ButtonGroup buttonGroup = new ButtonGroup(); + + + /** + * Creates a new TabbedPane. + */ + public TabbedPane() + { + GridBagLayout layout = new GridBagLayout(); + setLayout(layout); + + GridBagConstraints cardConstraints = new GridBagConstraints(); + cardConstraints.gridx = 1; + cardConstraints.gridy = 0; + cardConstraints.gridheight = GridBagConstraints.REMAINDER; + cardConstraints.fill = GridBagConstraints.BOTH; + cardConstraints.weightx = 1.0; + cardConstraints.weighty = 1.0; + cardConstraints.anchor = GridBagConstraints.NORTHWEST; + + add(cardPanel, cardConstraints); + } + + + /** + * Adds a component with a given title to the tabbed pane. + * + * @param title the title that will be used in the tab button. + * @param component the component that will be added as a tab. + */ + public Component add(final String title, Component component) + { + GridBagConstraints buttonConstraints = new GridBagConstraints(); + buttonConstraints.gridx = 0; + buttonConstraints.fill = GridBagConstraints.HORIZONTAL; + buttonConstraints.anchor = GridBagConstraints.NORTHWEST; + buttonConstraints.ipadx = 10; + buttonConstraints.ipady = 4; + + JToggleButton button = new JToggleButton(title); + + // Let the button react on the mouse press, instead of waiting for the + // mouse release. + button.setModel(new JToggleButton.ToggleButtonModel() + { + public void setPressed(boolean b) + { + if ((isPressed() == b) || !isEnabled()) + { + return; + } + + if (!b && isArmed()) + { + setSelected(!this.isSelected()); + } + + if (b) + { + stateMask |= PRESSED; + } + else + { + stateMask &= ~PRESSED; + } + + fireStateChanged(); + + if (isPressed()) + { + fireActionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, getActionCommand())); + } + } + + }); + + // Switch to the tab on a button press. + button.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + cardLayout.show(cardPanel, title); + } + }); + + // Only one button can be selected at the same time. + buttonGroup.add(button); + + // If this is the first tab, make sure its button is selected. + if (cardPanel.getComponentCount() == 0) + { + button.setSelected(true); + } + + // Add the button and its panel. + add(button, buttonConstraints); + cardPanel.add(title, component); + + return component; + } + + + /** + * Adds an image below the tab buttons, after all tabs have been added. + * The image will only be as visible as permitted by the available space. + * + * @param image the image. + * @return the component containing the image. + */ + public Component addImage(final Image image) + { + GridBagConstraints imageConstraints = new GridBagConstraints(); + imageConstraints.gridx = 0; + imageConstraints.weighty = 1.0; + imageConstraints.fill = GridBagConstraints.BOTH; + imageConstraints.anchor = GridBagConstraints.SOUTHWEST; + + JButton component = new JButton(new ImageIcon(image)); + component.setFocusPainted(false); + component.setFocusable(false); + component.setRequestFocusEnabled(false); + component.setRolloverEnabled(false); + component.setMargin(new Insets(0, 0, 0, 0)); + component.setHorizontalAlignment(JButton.LEFT); + component.setVerticalAlignment(JButton.BOTTOM); + component.setPreferredSize(new Dimension(0, 0)); + + add(component, imageConstraints); + + return component; + } + + + /** + * Selects the first tab. + */ + public void first() + { + cardLayout.first(cardPanel); + updateButtonSelection(); + } + + + /** + * Selects the last tab. + */ + public void last() + { + cardLayout.last(cardPanel); + updateButtonSelection(); + } + + + /** + * Selects the previous tab. + */ + public void previous() + { + cardLayout.previous(cardPanel); + updateButtonSelection(); + } + + + /** + * Selects the next tab. + */ + public void next() + { + cardLayout.next(cardPanel); + updateButtonSelection(); + } + + + /** + * Lets the button selection reflect the currently visible panel. + */ + private void updateButtonSelection() + { + int count = cardPanel.getComponentCount(); + for (int index = 0 ; index < count ; index++) { + Component card = cardPanel.getComponent(index); + if (card.isShowing()) + { + JToggleButton button = (JToggleButton)getComponent(index+1); + button.setSelected(true); + } + } + } +} diff --git a/src/proguard/gui/TextAreaOutputStream.java b/src/proguard/gui/TextAreaOutputStream.java new file mode 100644 index 0000000..57f983d --- /dev/null +++ b/src/proguard/gui/TextAreaOutputStream.java @@ -0,0 +1,81 @@ +/* + * 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 javax.swing.*; +import java.io.*; +import java.lang.reflect.InvocationTargetException; + + +/** + * This <code>PrintStream</code> appends its output to a given text area. + * + * @author Eric Lafortune + */ +final class TextAreaOutputStream extends FilterOutputStream implements Runnable +{ + private final JTextArea textArea; + + + public TextAreaOutputStream(JTextArea textArea) + { + super(new ByteArrayOutputStream()); + + this.textArea = textArea; + } + + + // Implementation for FilterOutputStream. + + public void flush() throws IOException + { + super.flush(); + + try + { + // Append the accumulated buffer contents to the text area. + SwingUtil.invokeAndWait(this); + } + catch (Exception e) + { + // Nothing. + } + } + + + // Implementation for Runnable. + + public void run() + { + ByteArrayOutputStream out = (ByteArrayOutputStream)super.out; + + // Has any new text been written? + String text = out.toString(); + if (text.length() > 0) + { + // Append the accumulated text to the text area. + textArea.append(text); + + // Clear the buffer. + out.reset(); + } + } +} diff --git a/src/proguard/gui/arrow.gif b/src/proguard/gui/arrow.gif Binary files differnew file mode 100644 index 0000000..c58e34e --- /dev/null +++ b/src/proguard/gui/arrow.gif diff --git a/src/proguard/gui/boilerplate.pro b/src/proguard/gui/boilerplate.pro new file mode 100644 index 0000000..70efb82 --- /dev/null +++ b/src/proguard/gui/boilerplate.pro @@ -0,0 +1,410 @@ +# Keep - Applications. Keep all application classes, along with their 'main' +# methods. +-keepclasseswithmembers public class * { + public static void main(java.lang.String[]); +} + +# Keep - Applets. Keep all extensions of java.applet.Applet. +-keep public class * extends java.applet.Applet + +# Keep - Servlets. Keep all extensions of javax.servlet.Servlet. +-keep public class * extends javax.servlet.Servlet + +# Keep - Midlets. Keep all extensions of javax.microedition.midlet.MIDlet. +-keep public class * extends javax.microedition.midlet.MIDlet + +# Keep - Xlets. Keep all extensions of javax.tv.xlet.Xlet. +-keep public class * extends javax.tv.xlet.Xlet + +# Keep - Library. Keep all public and protected classes, fields, and methods. +-keep public class * { + public protected <fields>; + public protected <methods>; +} + +# Also keep - Enumerations. Keep the special static methods that are required in +# enumeration classes. +-keepclassmembers enum * { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +# Also keep - Serialization code. Keep all fields and methods that are used for +# serialization. +-keepclassmembers class * extends java.io.Serializable { + static final long serialVersionUID; + static final java.io.ObjectStreamField[] serialPersistentFields; + private void writeObject(java.io.ObjectOutputStream); + private void readObject(java.io.ObjectInputStream); + java.lang.Object writeReplace(); + java.lang.Object readResolve(); +} + +# Also keep - BeanInfo classes. Keep all implementations of java.beans.BeanInfo. +-keep class * implements java.beans.BeanInfo + +# Also keep - Bean classes. Keep all specified classes, along with their getters +# and setters. +-keep class * { + void set*(***); + void set*(int,***); + + boolean is*(); + boolean is*(int); + + *** get*(); + *** get*(int); +} + +# Also keep - Database drivers. Keep all implementations of java.sql.Driver. +-keep class * implements java.sql.Driver + +# Also keep - Swing UI L&F. Keep all extensions of javax.swing.plaf.ComponentUI, +# along with the special 'createUI' method. +-keep class * extends javax.swing.plaf.ComponentUI { + public static javax.swing.plaf.ComponentUI createUI(javax.swing.JComponent); +} + +# Also keep - RMI interfaces. Keep all interfaces that extend the +# java.rmi.Remote interface, and their methods. +-keep interface * extends java.rmi.Remote { + <methods>; +} + +# Also keep - RMI implementations. Keep all implementations of java.rmi.Remote, +# including any explicit or implicit implementations of Activatable, with their +# two-argument constructors. +-keep class * implements java.rmi.Remote { + <init>(java.rmi.activation.ActivationID,java.rmi.MarshalledObject); +} + +# Keep names - Native method names. Keep all native class/method names. +-keepclasseswithmembernames class * { + native <methods>; +} + +# Keep names - _class method names. Keep all .class method names. This may be +# useful for libraries that will be obfuscated again with different obfuscators. +-keepclassmembernames class * { + java.lang.Class class$(java.lang.String); + java.lang.Class class$(java.lang.String,boolean); +} + +# Remove - System method calls. Remove all invocations of System +# methods without side effects whose return values are not used. +-assumenosideeffects public class java.lang.System { + public static long currentTimeMillis(); + static java.lang.Class getCallerClass(); + public static int identityHashCode(java.lang.Object); + public static java.lang.SecurityManager getSecurityManager(); + public static java.util.Properties getProperties(); + public static java.lang.String getProperty(java.lang.String); + public static java.lang.String getenv(java.lang.String); + public static java.lang.String mapLibraryName(java.lang.String); + public static java.lang.String getProperty(java.lang.String,java.lang.String); +} + +# Remove - Math method calls. Remove all invocations of Math +# methods without side effects whose return values are not used. +-assumenosideeffects public class java.lang.Math { + public static double sin(double); + public static double cos(double); + public static double tan(double); + public static double asin(double); + public static double acos(double); + public static double atan(double); + public static double toRadians(double); + public static double toDegrees(double); + public static double exp(double); + public static double log(double); + public static double log10(double); + public static double sqrt(double); + public static double cbrt(double); + public static double IEEEremainder(double,double); + public static double ceil(double); + public static double floor(double); + public static double rint(double); + public static double atan2(double,double); + public static double pow(double,double); + public static int round(float); + public static long round(double); + public static double random(); + public static int abs(int); + public static long abs(long); + public static float abs(float); + public static double abs(double); + public static int max(int,int); + public static long max(long,long); + public static float max(float,float); + public static double max(double,double); + public static int min(int,int); + public static long min(long,long); + public static float min(float,float); + public static double min(double,double); + public static double ulp(double); + public static float ulp(float); + public static double signum(double); + public static float signum(float); + public static double sinh(double); + public static double cosh(double); + public static double tanh(double); + public static double hypot(double,double); + public static double expm1(double); + public static double log1p(double); +} + +# Remove - Number method calls. Remove all invocations of Number +# methods without side effects whose return values are not used. +-assumenosideeffects public class java.lang.* extends java.lang.Number { + public static java.lang.String toString(byte); + public static java.lang.Byte valueOf(byte); + public static byte parseByte(java.lang.String); + public static byte parseByte(java.lang.String,int); + public static java.lang.Byte valueOf(java.lang.String,int); + public static java.lang.Byte valueOf(java.lang.String); + public static java.lang.Byte decode(java.lang.String); + public int compareTo(java.lang.Byte); + + public static java.lang.String toString(short); + public static short parseShort(java.lang.String); + public static short parseShort(java.lang.String,int); + public static java.lang.Short valueOf(java.lang.String,int); + public static java.lang.Short valueOf(java.lang.String); + public static java.lang.Short valueOf(short); + public static java.lang.Short decode(java.lang.String); + public static short reverseBytes(short); + public int compareTo(java.lang.Short); + + public static java.lang.String toString(int,int); + public static java.lang.String toHexString(int); + public static java.lang.String toOctalString(int); + public static java.lang.String toBinaryString(int); + public static java.lang.String toString(int); + public static int parseInt(java.lang.String,int); + public static int parseInt(java.lang.String); + public static java.lang.Integer valueOf(java.lang.String,int); + public static java.lang.Integer valueOf(java.lang.String); + public static java.lang.Integer valueOf(int); + public static java.lang.Integer getInteger(java.lang.String); + public static java.lang.Integer getInteger(java.lang.String,int); + public static java.lang.Integer getInteger(java.lang.String,java.lang.Integer); + public static java.lang.Integer decode(java.lang.String); + public static int highestOneBit(int); + public static int lowestOneBit(int); + public static int numberOfLeadingZeros(int); + public static int numberOfTrailingZeros(int); + public static int bitCount(int); + public static int rotateLeft(int,int); + public static int rotateRight(int,int); + public static int reverse(int); + public static int signum(int); + public static int reverseBytes(int); + public int compareTo(java.lang.Integer); + + public static java.lang.String toString(long,int); + public static java.lang.String toHexString(long); + public static java.lang.String toOctalString(long); + public static java.lang.String toBinaryString(long); + public static java.lang.String toString(long); + public static long parseLong(java.lang.String,int); + public static long parseLong(java.lang.String); + public static java.lang.Long valueOf(java.lang.String,int); + public static java.lang.Long valueOf(java.lang.String); + public static java.lang.Long valueOf(long); + public static java.lang.Long decode(java.lang.String); + public static java.lang.Long getLong(java.lang.String); + public static java.lang.Long getLong(java.lang.String,long); + public static java.lang.Long getLong(java.lang.String,java.lang.Long); + public static long highestOneBit(long); + public static long lowestOneBit(long); + public static int numberOfLeadingZeros(long); + public static int numberOfTrailingZeros(long); + public static int bitCount(long); + public static long rotateLeft(long,int); + public static long rotateRight(long,int); + public static long reverse(long); + public static int signum(long); + public static long reverseBytes(long); + public int compareTo(java.lang.Long); + + public static java.lang.String toString(float); + public static java.lang.String toHexString(float); + public static java.lang.Float valueOf(java.lang.String); + public static java.lang.Float valueOf(float); + public static float parseFloat(java.lang.String); + public static boolean isNaN(float); + public static boolean isInfinite(float); + public static int floatToIntBits(float); + public static int floatToRawIntBits(float); + public static float intBitsToFloat(int); + public static int compare(float,float); + public boolean isNaN(); + public boolean isInfinite(); + public int compareTo(java.lang.Float); + + public static java.lang.String toString(double); + public static java.lang.String toHexString(double); + public static java.lang.Double valueOf(java.lang.String); + public static java.lang.Double valueOf(double); + public static double parseDouble(java.lang.String); + public static boolean isNaN(double); + public static boolean isInfinite(double); + public static long doubleToLongBits(double); + public static long doubleToRawLongBits(double); + public static double longBitsToDouble(long); + public static int compare(double,double); + public boolean isNaN(); + public boolean isInfinite(); + public int compareTo(java.lang.Double); + + public <init>(byte); + public <init>(short); + public <init>(int); + public <init>(long); + public <init>(float); + public <init>(double); + public <init>(java.lang.String); + public byte byteValue(); + public short shortValue(); + public int intValue(); + public long longValue(); + public float floatValue(); + public double doubleValue(); + + public int compareTo(java.lang.Object); + public boolean equals(java.lang.Object); + public int hashCode(); + public java.lang.String toString(); +} + +# Remove - String method calls. Remove all invocations of String +# methods without side effects whose return values are not used. +-assumenosideeffects public class java.lang.String { + public java.lang.String(); + public java.lang.String(byte[]); + public java.lang.String(byte[],int); + public java.lang.String(byte[],int,int); + public java.lang.String(byte[],int,int,int); + public java.lang.String(byte[],int,int,java.lang.String); + public java.lang.String(byte[],java.lang.String); + public java.lang.String(char[]); + public java.lang.String(char[],int,int); + public java.lang.String(java.lang.String); + public java.lang.String(java.lang.StringBuffer); + public static java.lang.String copyValueOf(char[]); + public static java.lang.String copyValueOf(char[],int,int); + public static java.lang.String valueOf(boolean); + public static java.lang.String valueOf(char); + public static java.lang.String valueOf(char[]); + public static java.lang.String valueOf(char[],int,int); + public static java.lang.String valueOf(double); + public static java.lang.String valueOf(float); + public static java.lang.String valueOf(int); + public static java.lang.String valueOf(java.lang.Object); + public static java.lang.String valueOf(long); + public boolean contentEquals(java.lang.StringBuffer); + public boolean endsWith(java.lang.String); + public boolean equalsIgnoreCase(java.lang.String); + public boolean equals(java.lang.Object); + public boolean matches(java.lang.String); + public boolean regionMatches(boolean,int,java.lang.String,int,int); + public boolean regionMatches(int,java.lang.String,int,int); + public boolean startsWith(java.lang.String); + public boolean startsWith(java.lang.String,int); + public byte[] getBytes(); + public byte[] getBytes(java.lang.String); + public char charAt(int); + public char[] toCharArray(); + public int compareToIgnoreCase(java.lang.String); + public int compareTo(java.lang.Object); + public int compareTo(java.lang.String); + public int hashCode(); + public int indexOf(int); + public int indexOf(int,int); + public int indexOf(java.lang.String); + public int indexOf(java.lang.String,int); + public int lastIndexOf(int); + public int lastIndexOf(int,int); + public int lastIndexOf(java.lang.String); + public int lastIndexOf(java.lang.String,int); + public int length(); + public java.lang.CharSequence subSequence(int,int); + public java.lang.String concat(java.lang.String); + public java.lang.String replaceAll(java.lang.String,java.lang.String); + public java.lang.String replace(char,char); + public java.lang.String replaceFirst(java.lang.String,java.lang.String); + public java.lang.String[] split(java.lang.String); + public java.lang.String[] split(java.lang.String,int); + public java.lang.String substring(int); + public java.lang.String substring(int,int); + public java.lang.String toLowerCase(); + public java.lang.String toLowerCase(java.util.Locale); + public java.lang.String toString(); + public java.lang.String toUpperCase(); + public java.lang.String toUpperCase(java.util.Locale); + public java.lang.String trim(); +} + +# Remove - StringBuffer method calls. Remove all invocations of StringBuffer +# methods without side effects whose return values are not used. +-assumenosideeffects public class java.lang.StringBuffer { + public java.lang.StringBuffer(); + public java.lang.StringBuffer(int); + public java.lang.StringBuffer(java.lang.String); + public java.lang.StringBuffer(java.lang.CharSequence); + public java.lang.String toString(); + public char charAt(int); + public int capacity(); + public int codePointAt(int); + public int codePointBefore(int); + public int indexOf(java.lang.String,int); + public int lastIndexOf(java.lang.String); + public int lastIndexOf(java.lang.String,int); + public int length(); + public java.lang.String substring(int); + public java.lang.String substring(int,int); +} + +# Remove - StringBuilder method calls. Remove all invocations of StringBuilder +# methods without side effects whose return values are not used. +-assumenosideeffects public class java.lang.StringBuilder { + public java.lang.StringBuilder(); + public java.lang.StringBuilder(int); + public java.lang.StringBuilder(java.lang.String); + public java.lang.StringBuilder(java.lang.CharSequence); + public java.lang.String toString(); + public char charAt(int); + public int capacity(); + public int codePointAt(int); + public int codePointBefore(int); + public int indexOf(java.lang.String,int); + public int lastIndexOf(java.lang.String); + public int lastIndexOf(java.lang.String,int); + public int length(); + public java.lang.String substring(int); + public java.lang.String substring(int,int); +} + +# Remove debugging - Throwable_printStackTrace calls. Remove all invocations of +# Throwable.printStackTrace(). +-assumenosideeffects public class java.lang.Throwable { + public void printStackTrace(); +} + +# Remove debugging - Thread_dumpStack calls. Remove all invocations of +# Thread.dumpStack(). +-assumenosideeffects public class java.lang.Thread { + public static void dumpStack(); +} + +# Remove debugging - All logging API calls. Remove all invocations of the +# logging API whose return values are not used. +-assumenosideeffects public class java.util.logging.* { + <methods>; +} + +# Remove debugging - All Log4j API calls. Remove all invocations of the +# Log4j API whose return values are not used. +-assumenosideeffects public class org.apache.log4j.** { + <methods>; +} diff --git a/src/proguard/gui/default.pro b/src/proguard/gui/default.pro new file mode 100644 index 0000000..3bfe2d6 --- /dev/null +++ b/src/proguard/gui/default.pro @@ -0,0 +1,318 @@ +# The default configuration when starting up the GUI. + +-libraryjars <java.home>/lib/rt.jar + +# Keep - Applications. Keep all application classes, along with their 'main' +# methods. +-keepclasseswithmembers public class * { + public static void main(java.lang.String[]); +} + +# Also keep - Enumerations. Keep the special static methods that are required in +# enumeration classes. +-keepclassmembers enum * { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +# Also keep - Database drivers. Keep all implementations of java.sql.Driver. +-keep class * extends java.sql.Driver + +# Also keep - Swing UI L&F. Keep all extensions of javax.swing.plaf.ComponentUI, +# along with the special 'createUI' method. +-keep class * extends javax.swing.plaf.ComponentUI { + public static javax.swing.plaf.ComponentUI createUI(javax.swing.JComponent); +} + +# Keep names - Native method names. Keep all native class/method names. +-keepclasseswithmembers,allowshrinking class * { + native <methods>; +} + +# Remove - System method calls. Remove all invocations of System +# methods without side effects whose return values are not used. +-assumenosideeffects public class java.lang.System { + public static long currentTimeMillis(); + static java.lang.Class getCallerClass(); + public static int identityHashCode(java.lang.Object); + public static java.lang.SecurityManager getSecurityManager(); + public static java.util.Properties getProperties(); + public static java.lang.String getProperty(java.lang.String); + public static java.lang.String getenv(java.lang.String); + public static java.lang.String mapLibraryName(java.lang.String); + public static java.lang.String getProperty(java.lang.String,java.lang.String); +} + +# Remove - Math method calls. Remove all invocations of Math +# methods without side effects whose return values are not used. +-assumenosideeffects public class java.lang.Math { + public static double sin(double); + public static double cos(double); + public static double tan(double); + public static double asin(double); + public static double acos(double); + public static double atan(double); + public static double toRadians(double); + public static double toDegrees(double); + public static double exp(double); + public static double log(double); + public static double log10(double); + public static double sqrt(double); + public static double cbrt(double); + public static double IEEEremainder(double,double); + public static double ceil(double); + public static double floor(double); + public static double rint(double); + public static double atan2(double,double); + public static double pow(double,double); + public static int round(float); + public static long round(double); + public static double random(); + public static int abs(int); + public static long abs(long); + public static float abs(float); + public static double abs(double); + public static int max(int,int); + public static long max(long,long); + public static float max(float,float); + public static double max(double,double); + public static int min(int,int); + public static long min(long,long); + public static float min(float,float); + public static double min(double,double); + public static double ulp(double); + public static float ulp(float); + public static double signum(double); + public static float signum(float); + public static double sinh(double); + public static double cosh(double); + public static double tanh(double); + public static double hypot(double,double); + public static double expm1(double); + public static double log1p(double); +} + +# Remove - Number method calls. Remove all invocations of Number +# methods without side effects whose return values are not used. +-assumenosideeffects public class java.lang.* extends java.lang.Number { + public static java.lang.String toString(byte); + public static java.lang.Byte valueOf(byte); + public static byte parseByte(java.lang.String); + public static byte parseByte(java.lang.String,int); + public static java.lang.Byte valueOf(java.lang.String,int); + public static java.lang.Byte valueOf(java.lang.String); + public static java.lang.Byte decode(java.lang.String); + public int compareTo(java.lang.Byte); + public static java.lang.String toString(short); + public static short parseShort(java.lang.String); + public static short parseShort(java.lang.String,int); + public static java.lang.Short valueOf(java.lang.String,int); + public static java.lang.Short valueOf(java.lang.String); + public static java.lang.Short valueOf(short); + public static java.lang.Short decode(java.lang.String); + public static short reverseBytes(short); + public int compareTo(java.lang.Short); + public static java.lang.String toString(int,int); + public static java.lang.String toHexString(int); + public static java.lang.String toOctalString(int); + public static java.lang.String toBinaryString(int); + public static java.lang.String toString(int); + public static int parseInt(java.lang.String,int); + public static int parseInt(java.lang.String); + public static java.lang.Integer valueOf(java.lang.String,int); + public static java.lang.Integer valueOf(java.lang.String); + public static java.lang.Integer valueOf(int); + public static java.lang.Integer getInteger(java.lang.String); + public static java.lang.Integer getInteger(java.lang.String,int); + public static java.lang.Integer getInteger(java.lang.String,java.lang.Integer); + public static java.lang.Integer decode(java.lang.String); + public static int highestOneBit(int); + public static int lowestOneBit(int); + public static int numberOfLeadingZeros(int); + public static int numberOfTrailingZeros(int); + public static int bitCount(int); + public static int rotateLeft(int,int); + public static int rotateRight(int,int); + public static int reverse(int); + public static int signum(int); + public static int reverseBytes(int); + public int compareTo(java.lang.Integer); + public static java.lang.String toString(long,int); + public static java.lang.String toHexString(long); + public static java.lang.String toOctalString(long); + public static java.lang.String toBinaryString(long); + public static java.lang.String toString(long); + public static long parseLong(java.lang.String,int); + public static long parseLong(java.lang.String); + public static java.lang.Long valueOf(java.lang.String,int); + public static java.lang.Long valueOf(java.lang.String); + public static java.lang.Long valueOf(long); + public static java.lang.Long decode(java.lang.String); + public static java.lang.Long getLong(java.lang.String); + public static java.lang.Long getLong(java.lang.String,long); + public static java.lang.Long getLong(java.lang.String,java.lang.Long); + public static long highestOneBit(long); + public static long lowestOneBit(long); + public static int numberOfLeadingZeros(long); + public static int numberOfTrailingZeros(long); + public static int bitCount(long); + public static long rotateLeft(long,int); + public static long rotateRight(long,int); + public static long reverse(long); + public static int signum(long); + public static long reverseBytes(long); + public int compareTo(java.lang.Long); + public static java.lang.String toString(float); + public static java.lang.String toHexString(float); + public static java.lang.Float valueOf(java.lang.String); + public static java.lang.Float valueOf(float); + public static float parseFloat(java.lang.String); + public static boolean isNaN(float); + public static boolean isInfinite(float); + public static int floatToIntBits(float); + public static int floatToRawIntBits(float); + public static float intBitsToFloat(int); + public static int compare(float,float); + public boolean isNaN(); + public boolean isInfinite(); + public int compareTo(java.lang.Float); + public static java.lang.String toString(double); + public static java.lang.String toHexString(double); + public static java.lang.Double valueOf(java.lang.String); + public static java.lang.Double valueOf(double); + public static double parseDouble(java.lang.String); + public static boolean isNaN(double); + public static boolean isInfinite(double); + public static long doubleToLongBits(double); + public static long doubleToRawLongBits(double); + public static double longBitsToDouble(long); + public static int compare(double,double); + public boolean isNaN(); + public boolean isInfinite(); + public int compareTo(java.lang.Double); + public <init>(byte); + public <init>(short); + public <init>(int); + public <init>(long); + public <init>(float); + public <init>(double); + public <init>(java.lang.String); + public byte byteValue(); + public short shortValue(); + public int intValue(); + public long longValue(); + public float floatValue(); + public double doubleValue(); + public int compareTo(java.lang.Object); + public boolean equals(java.lang.Object); + public int hashCode(); + public java.lang.String toString(); +} + +# Remove - String method calls. Remove all invocations of String +# methods without side effects whose return values are not used. +-assumenosideeffects public class java.lang.String { + public <init>(); + public <init>(byte[]); + public <init>(byte[],int); + public <init>(byte[],int,int); + public <init>(byte[],int,int,int); + public <init>(byte[],int,int,java.lang.String); + public <init>(byte[],java.lang.String); + public <init>(char[]); + public <init>(char[],int,int); + public <init>(java.lang.String); + public <init>(java.lang.StringBuffer); + public static java.lang.String copyValueOf(char[]); + public static java.lang.String copyValueOf(char[],int,int); + public static java.lang.String valueOf(boolean); + public static java.lang.String valueOf(char); + public static java.lang.String valueOf(char[]); + public static java.lang.String valueOf(char[],int,int); + public static java.lang.String valueOf(double); + public static java.lang.String valueOf(float); + public static java.lang.String valueOf(int); + public static java.lang.String valueOf(java.lang.Object); + public static java.lang.String valueOf(long); + public boolean contentEquals(java.lang.StringBuffer); + public boolean endsWith(java.lang.String); + public boolean equalsIgnoreCase(java.lang.String); + public boolean equals(java.lang.Object); + public boolean matches(java.lang.String); + public boolean regionMatches(boolean,int,java.lang.String,int,int); + public boolean regionMatches(int,java.lang.String,int,int); + public boolean startsWith(java.lang.String); + public boolean startsWith(java.lang.String,int); + public byte[] getBytes(); + public byte[] getBytes(java.lang.String); + public char charAt(int); + public char[] toCharArray(); + public int compareToIgnoreCase(java.lang.String); + public int compareTo(java.lang.Object); + public int compareTo(java.lang.String); + public int hashCode(); + public int indexOf(int); + public int indexOf(int,int); + public int indexOf(java.lang.String); + public int indexOf(java.lang.String,int); + public int lastIndexOf(int); + public int lastIndexOf(int,int); + public int lastIndexOf(java.lang.String); + public int lastIndexOf(java.lang.String,int); + public int length(); + public java.lang.CharSequence subSequence(int,int); + public java.lang.String concat(java.lang.String); + public java.lang.String replaceAll(java.lang.String,java.lang.String); + public java.lang.String replace(char,char); + public java.lang.String replaceFirst(java.lang.String,java.lang.String); + public java.lang.String[] split(java.lang.String); + public java.lang.String[] split(java.lang.String,int); + public java.lang.String substring(int); + public java.lang.String substring(int,int); + public java.lang.String toLowerCase(); + public java.lang.String toLowerCase(java.util.Locale); + public java.lang.String toString(); + public java.lang.String toUpperCase(); + public java.lang.String toUpperCase(java.util.Locale); + public java.lang.String trim(); +} + +# Remove - StringBuffer method calls. Remove all invocations of StringBuffer +# methods without side effects whose return values are not used. +-assumenosideeffects public class java.lang.StringBuffer { + public <init>(); + public <init>(int); + public <init>(java.lang.String); + public <init>(java.lang.CharSequence); + public java.lang.String toString(); + public char charAt(int); + public int capacity(); + public int codePointAt(int); + public int codePointBefore(int); + public int indexOf(java.lang.String,int); + public int lastIndexOf(java.lang.String); + public int lastIndexOf(java.lang.String,int); + public int length(); + public java.lang.String substring(int); + public java.lang.String substring(int,int); +} + +# Remove - StringBuilder method calls. Remove all invocations of StringBuilder +# methods without side effects whose return values are not used. +-assumenosideeffects public class java.lang.StringBuilder { + public <init>(); + public <init>(int); + public <init>(java.lang.String); + public <init>(java.lang.CharSequence); + public java.lang.String toString(); + public char charAt(int); + public int capacity(); + public int codePointAt(int); + public int codePointBefore(int); + public int indexOf(java.lang.String,int); + public int lastIndexOf(java.lang.String); + public int lastIndexOf(java.lang.String,int); + public int length(); + public java.lang.String substring(int); + public java.lang.String substring(int,int); +} diff --git a/src/proguard/gui/package.html b/src/proguard/gui/package.html new file mode 100644 index 0000000..4eedcc2 --- /dev/null +++ b/src/proguard/gui/package.html @@ -0,0 +1,3 @@ +<body> +This package contains a GUI for ProGuard and ReTrace. +</body> diff --git a/src/proguard/gui/splash/BufferedSprite.java b/src/proguard/gui/splash/BufferedSprite.java new file mode 100644 index 0000000..8427832 --- /dev/null +++ b/src/proguard/gui/splash/BufferedSprite.java @@ -0,0 +1,145 @@ +/* + * 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.splash; + +import java.awt.*; +import java.awt.image.BufferedImage; + +/** + * This Sprite encapsulates another Sprite, which is then buffered in an Image. + * + * @author Eric Lafortune + */ +public class BufferedSprite implements Sprite +{ + private final int bufferX; + private final int bufferY; + private final Image bufferImage; + private final Color backgroundColor; + private final Sprite sprite; + private final VariableInt x; + private final VariableInt y; + + private long cachedTime = -1; + + + /** + * Creates a new BufferedSprite with an ABGR image. + * @param bufferX the x offset of the buffer image. + * @param bufferY the y offset of the buffer image. + * @param width the width of the buffer image. + * @param height the height of the buffer image. + * @param sprite the Sprite that is painted in the buffer. + * @param x the variable x ordinate of the image buffer for painting. + * @param y the variable y ordinate of the image buffer for painting. + * + */ + public BufferedSprite(int bufferX, + int bufferY, + int width, + int height, + Sprite sprite, + VariableInt x, + VariableInt y) + { + + this(bufferX, + bufferY, + new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR), + null, + sprite, + x, + y); + } + + + /** + * Creates a new BufferedSprite with the given image. + * @param bufferX the x offset of the buffer image. + * @param bufferY the y offset of the buffer image. + * @param bufferImage the Image that is used for the buffering. + * @param backgroundColor the background color that is used for the buffer. + * @param sprite the Sprite that is painted in the buffer. + * @param x the variable x ordinate of the image buffer for + * painting. + * @param y the variable y ordinate of the image buffer for + * painting. + */ + public BufferedSprite(int bufferX, + int bufferY, + Image bufferImage, + Color backgroundColor, + Sprite sprite, + VariableInt x, + VariableInt y) + { + this.bufferX = bufferX; + this.bufferY = bufferY; + this.bufferImage = bufferImage; + this.backgroundColor = backgroundColor; + this.sprite = sprite; + this.x = x; + this.y = y; + } + + + // Implementation for Sprite. + + public void paint(Graphics graphics, long time) + { + if (time != cachedTime) + { + Graphics bufferGraphics = bufferImage.getGraphics(); + + // Clear the background. + if (backgroundColor != null) + { + Graphics2D bufferGraphics2D = (Graphics2D)bufferGraphics; + bufferGraphics2D.setComposite(AlphaComposite.Clear); + bufferGraphics.fillRect(0, 0, bufferImage.getWidth(null), bufferImage.getHeight(null)); + bufferGraphics2D.setComposite(AlphaComposite.Src); + } + else + { + bufferGraphics.setColor(backgroundColor); + bufferGraphics.fillRect(0, 0, bufferImage.getWidth(null), bufferImage.getHeight(null)); + } + + // Set up the buffer graphics. + bufferGraphics.translate(-bufferX, -bufferY); + bufferGraphics.setColor(graphics.getColor()); + bufferGraphics.setFont(graphics.getFont()); + + // Draw the sprite. + sprite.paint(bufferGraphics, time); + + bufferGraphics.dispose(); + + cachedTime = time; + } + + // Draw the buffer image. + graphics.drawImage(bufferImage, + bufferX + x.getInt(time), + bufferY + y.getInt(time), + null); + } +} diff --git a/src/proguard/gui/splash/CircleSprite.java b/src/proguard/gui/splash/CircleSprite.java new file mode 100644 index 0000000..5dc65eb --- /dev/null +++ b/src/proguard/gui/splash/CircleSprite.java @@ -0,0 +1,74 @@ +/* + * 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.splash; + +import java.awt.*; + +/** + * This Sprite represents an animated circle. It can optionally be filled. + * + * @author Eric Lafortune + */ +public class CircleSprite implements Sprite +{ + private final boolean filled; + private final VariableInt x; + private final VariableInt y; + private final VariableInt radius; + + + /** + * Creates a new CircleSprite. + * @param filled specifies whether the rectangle should be filled. + * @param x the variable x-coordinate of the center of the circle. + * @param y the variable y-coordinate of the center of the circle. + * @param radius the variable radius of the circle. + */ + public CircleSprite(boolean filled, + VariableInt x, + VariableInt y, + VariableInt radius) + { + this.filled = filled; + this.x = x; + this.y = y; + this.radius = radius; + } + + + // Implementation for Sprite. + + public void paint(Graphics graphics, long time) + { + int xt = x.getInt(time); + int yt = y.getInt(time); + int r = radius.getInt(time); + + if (filled) + { + graphics.fillOval(xt - r, yt - r, 2 * r, 2 * r); + } + else + { + graphics.drawOval(xt - r, yt - r, 2 * r, 2 * r); + } + } +} diff --git a/src/proguard/gui/splash/ClipSprite.java b/src/proguard/gui/splash/ClipSprite.java new file mode 100644 index 0000000..55f9eac --- /dev/null +++ b/src/proguard/gui/splash/ClipSprite.java @@ -0,0 +1,85 @@ +/* + * 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.splash; + +import java.awt.*; + +/** + * This Sprite encapsulates another Sprite, which is clipped by a clip Sprite. + * + * @author Eric Lafortune + */ +public class ClipSprite implements Sprite +{ + private final VariableColor insideClipColor; + private final VariableColor outsideClipColor; + private final Sprite clipSprite; + private final Sprite sprite; + + + /** + * Creates a new ClipSprite. + * @param insideClipColor the background color inside the clip sprite. + * @param outsideClipColor the background color outside the clip sprite. + * @param clipSprite the clip Sprite. + * @param sprite the clipped Sprite. + */ + public ClipSprite(VariableColor insideClipColor, + VariableColor outsideClipColor, + Sprite clipSprite, + Sprite sprite) + { + this.insideClipColor = insideClipColor; + this.outsideClipColor = outsideClipColor; + this.clipSprite = clipSprite; + this.sprite = sprite; + } + + + // Implementation for Sprite. + + public void paint(Graphics graphics, long time) + { + // Clear the background. + Color outsideColor = outsideClipColor.getColor(time); + Rectangle clip = graphics.getClipBounds(); + graphics.setPaintMode(); + graphics.setColor(outsideColor); + graphics.fillRect(0, 0, clip.width, clip.height); + + // Draw the sprite in XOR mode. + OverrideGraphics2D g = new OverrideGraphics2D((Graphics2D)graphics); + Color insideColor = insideClipColor.getColor(time); + g.setOverrideXORMode(insideColor); + sprite.paint(g, time); + g.setOverrideXORMode(null); + + // Clear the clip area. + g.setOverrideColor(insideColor); + clipSprite.paint(g, time); + g.setOverrideColor(null); + + // Draw the sprite in XOR mode. + g.setOverrideXORMode(insideColor); + sprite.paint(g, time); + g.setOverrideXORMode(null); + } +} diff --git a/src/proguard/gui/splash/ColorSprite.java b/src/proguard/gui/splash/ColorSprite.java new file mode 100644 index 0000000..3f9bc3b --- /dev/null +++ b/src/proguard/gui/splash/ColorSprite.java @@ -0,0 +1,65 @@ +/* + * 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.splash; + +import java.awt.*; + +/** + * This Sprite colors another given sprite. + * + * @author Eric Lafortune + */ +public class ColorSprite implements Sprite +{ + private final VariableColor color; + private final Sprite sprite; + + + /** + * Creates a new ColorSprite. + * @param color the variable color of the given sprite. + * @param sprite the sprite that will be colored and painted. + */ + public ColorSprite(VariableColor color, + Sprite sprite) + { + this.color = color; + this.sprite = sprite; + } + + + // Implementation for Sprite. + + public void paint(Graphics graphics, long time) + { + // Save the old color. + Color oldColor = graphics.getColor(); + + // Set the new color. + graphics.setColor(color.getColor(time)); + + // Paint the actual sprite. + sprite.paint(graphics, time); + + // Restore the old color. + graphics.setColor(oldColor); + } +} diff --git a/src/proguard/gui/splash/CompositeSprite.java b/src/proguard/gui/splash/CompositeSprite.java new file mode 100644 index 0000000..2480ead --- /dev/null +++ b/src/proguard/gui/splash/CompositeSprite.java @@ -0,0 +1,56 @@ +/* + * 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.splash; + +import java.awt.*; + +/** + * This Sprite is the composition of a list of Sprite objects. + * + * @author Eric Lafortune + */ +public class CompositeSprite implements Sprite +{ + private final Sprite[] sprites; + + + /** + * Creates a new CompositeSprite. + * @param sprites the array of Sprite objects to which the painting will + * be delegated, starting with the first element. + */ + public CompositeSprite(Sprite[] sprites) + { + this.sprites = sprites; + } + + + // Implementation for Sprite. + + public void paint(Graphics graphics, long time) + { + // Draw the sprites. + for (int index = 0; index < sprites.length; index++) + { + sprites[index].paint(graphics, time); + } + } +} diff --git a/src/proguard/gui/splash/ConstantColor.java b/src/proguard/gui/splash/ConstantColor.java new file mode 100644 index 0000000..94c78df --- /dev/null +++ b/src/proguard/gui/splash/ConstantColor.java @@ -0,0 +1,51 @@ +/* + * 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.splash; + +import java.awt.*; + +/** + * This VariableColor is constant over time. + * + * @author Eric Lafortune + */ +public class ConstantColor implements VariableColor +{ + private final Color value; + + + /** + * Creates a new ConstantColor. + * @param value the constant value. + */ + public ConstantColor(Color value) + { + this.value = value; + } + + + // Implementation for VariableColor. + + public Color getColor(long time) + { + return value; + } +} diff --git a/src/proguard/gui/splash/ConstantDouble.java b/src/proguard/gui/splash/ConstantDouble.java new file mode 100644 index 0000000..0874d6d --- /dev/null +++ b/src/proguard/gui/splash/ConstantDouble.java @@ -0,0 +1,49 @@ +/* + * 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.splash; + +/** + * This VariableDouble is constant over time. + * + * @author Eric Lafortune + */ +public class ConstantDouble implements VariableDouble +{ + private final double value; + + + /** + * Creates a new ConstantDouble. + * @param value the constant value. + */ + public ConstantDouble(double value) + { + this.value = value; + } + + + // Implementation for VariableDouble. + + public double getDouble(long time) + { + return value; + } +} diff --git a/src/proguard/gui/splash/ConstantFont.java b/src/proguard/gui/splash/ConstantFont.java new file mode 100644 index 0000000..3f1ac03 --- /dev/null +++ b/src/proguard/gui/splash/ConstantFont.java @@ -0,0 +1,46 @@ +/* + * 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.splash; + +import java.awt.*; + +/** + * This VariableFont is constant over time. + * + * @author Eric Lafortune + */ +public class ConstantFont implements VariableFont +{ + private final Font value; + + public ConstantFont(Font value) + { + this.value = value; + } + + + // Implementation for VariableFont. + + public Font getFont(long time) + { + return value; + } +} diff --git a/src/proguard/gui/splash/ConstantInt.java b/src/proguard/gui/splash/ConstantInt.java new file mode 100644 index 0000000..537196d --- /dev/null +++ b/src/proguard/gui/splash/ConstantInt.java @@ -0,0 +1,49 @@ +/* + * 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.splash; + +/** + * This VariableInt is constant over time. + * + * @author Eric Lafortune + */ +public class ConstantInt implements VariableInt +{ + private final int value; + + + /** + * Creates a new ConstantInt. + * @param value the constant value. + */ + public ConstantInt(int value) + { + this.value = value; + } + + + // Implementation for VariableInt. + + public int getInt(long time) + { + return value; + } +} diff --git a/src/proguard/gui/splash/ConstantString.java b/src/proguard/gui/splash/ConstantString.java new file mode 100644 index 0000000..7617c3f --- /dev/null +++ b/src/proguard/gui/splash/ConstantString.java @@ -0,0 +1,49 @@ +/* + * 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.splash; + +/** + * This VariableString is constant over time. + * + * @author Eric Lafortune + */ +public class ConstantString implements VariableString +{ + private final String value; + + + /** + * Creates a new ConstantString. + * @param value the constant value. + */ + public ConstantString(String value) + { + this.value = value; + } + + + // Implementation for VariableString. + + public String getString(long time) + { + return value; + } +} diff --git a/src/proguard/gui/splash/ConstantTiming.java b/src/proguard/gui/splash/ConstantTiming.java new file mode 100644 index 0000000..dfde644 --- /dev/null +++ b/src/proguard/gui/splash/ConstantTiming.java @@ -0,0 +1,57 @@ +/* + * 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.splash; + +/** + * This Timing is constant over time. + * + * @author Eric Lafortune + */ +public class ConstantTiming implements Timing +{ + private final double timing; + + + /** + * Creates a new ConstantTiming with a value of 0. + */ + public ConstantTiming() + { + this(0.0); + } + + /** + * Creates a new ConstantTiming with a given value. + * @param timing the constant value of the timing. + */ + public ConstantTiming(double timing) + { + this.timing = timing; + } + + + // Implementation for Timing. + + public double getTiming(long time) + { + return timing; + } +} diff --git a/src/proguard/gui/splash/FontSprite.java b/src/proguard/gui/splash/FontSprite.java new file mode 100644 index 0000000..9a554ba --- /dev/null +++ b/src/proguard/gui/splash/FontSprite.java @@ -0,0 +1,65 @@ +/* + * 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.splash; + +import java.awt.*; + +/** + * This Sprite sets the font for another given sprite. + * + * @author Eric Lafortune + */ +public class FontSprite implements Sprite +{ + private final VariableFont font; + private final Sprite sprite; + + + /** + * Creates a new FontSprite. + * @param font the variable Font of the given sprite. + * @param sprite the sprite that will be provided of a font and painted. + */ + public FontSprite(VariableFont font, + Sprite sprite) + { + this.font = font; + this.sprite = sprite; + } + + + // Implementation for Sprite. + + public void paint(Graphics graphics, long time) + { + // Save the old font. + Font oldFont = graphics.getFont(); + + // Set the new font. + graphics.setFont(font.getFont(time)); + + // Paint the actual sprite. + sprite.paint(graphics, time); + + // Restore the old font. + graphics.setFont(oldFont); + } +} diff --git a/src/proguard/gui/splash/ImageSprite.java b/src/proguard/gui/splash/ImageSprite.java new file mode 100644 index 0000000..6e7c189 --- /dev/null +++ b/src/proguard/gui/splash/ImageSprite.java @@ -0,0 +1,76 @@ +/* + * 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.splash; + +import java.awt.*; + +/** + * This Sprite represents an animated image. + * + * @author Eric Lafortune + */ +public class ImageSprite implements Sprite +{ + private final Image image; + private final VariableInt x; + private final VariableInt y; + private final VariableDouble scaleX; + private final VariableDouble scaleY; + + + /** + * Creates a new ImageSprite. + * @param image the Image to be painted. + * @param x the variable x-coordinate of the upper-left corner of the image. + * @param y the variable y-coordinate of the upper-left corner of the image. + * @param scaleX the variable x-scale of the image. + * @param scaleY the variable y-scale of the image. + */ + public ImageSprite(Image image, + VariableInt x, + VariableInt y, + VariableDouble scaleX, + VariableDouble scaleY) + { + this.image = image; + this.x = x; + this.y = y; + this.scaleX = scaleX; + this.scaleY = scaleY; + } + + + // Implementation for Sprite. + + public void paint(Graphics graphics, long time) + { + int xt = x.getInt(time); + int yt = y.getInt(time); + + double scale_x = scaleX.getDouble(time); + double scale_y = scaleY.getDouble(time); + + int width = (int)(image.getWidth(null) * scale_x); + int height = (int)(image.getHeight(null) * scale_y); + + graphics.drawImage(image, xt, yt, width, height, null); + } +} diff --git a/src/proguard/gui/splash/LinearColor.java b/src/proguard/gui/splash/LinearColor.java new file mode 100644 index 0000000..3a7674d --- /dev/null +++ b/src/proguard/gui/splash/LinearColor.java @@ -0,0 +1,72 @@ +/* + * 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.splash; + +import java.awt.*; + +/** + * This VariableColor varies linearly with respect to its Timing. + * + * @author Eric Lafortune + */ +public class LinearColor implements VariableColor +{ + private final Color fromValue; + private final Color toValue; + private final Timing timing; + + private double cachedTiming = -1.0; + private Color cachedColor; + + + /** + * Creates a new LinearColor. + * @param fromValue the value that corresponds to a timing of 0. + * @param toValue the value that corresponds to a timing of 1. + * @param timing the applied timing. + */ + public LinearColor(Color fromValue, Color toValue, Timing timing) + { + this.fromValue = fromValue; + this.toValue = toValue; + this.timing = timing; + } + + + // Implementation for VariableColor. + + public Color getColor(long time) + { + double t = timing.getTiming(time); + if (t != cachedTiming) + { + cachedTiming = t; + cachedColor = + t == 0.0 ? fromValue : + t == 1.0 ? toValue : + new Color((int)(fromValue.getRed() + t * (toValue.getRed() - fromValue.getRed())), + (int)(fromValue.getGreen() + t * (toValue.getGreen() - fromValue.getGreen())), + (int)(fromValue.getBlue() + t * (toValue.getBlue() - fromValue.getBlue()))); + } + + return cachedColor; + } +} diff --git a/src/proguard/gui/splash/LinearDouble.java b/src/proguard/gui/splash/LinearDouble.java new file mode 100644 index 0000000..046ae84 --- /dev/null +++ b/src/proguard/gui/splash/LinearDouble.java @@ -0,0 +1,55 @@ +/* + * 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.splash; + +/** + * This VariableDouble varies linearly with respect to its Timing. + * + * @author Eric Lafortune + */ +public class LinearDouble implements VariableDouble +{ + private final double fromValue; + private final double toValue; + private final Timing timing; + + + /** + * Creates a new LinearDouble. + * @param fromValue the value that corresponds to a timing of 0. + * @param toValue the value that corresponds to a timing of 1. + * @param timing the applied timing. + */ + public LinearDouble(double fromValue, double toValue, Timing timing) + { + this.fromValue = fromValue; + this.toValue = toValue; + this.timing = timing; + } + + + // Implementation for VariableDouble. + + public double getDouble(long time) + { + return fromValue + timing.getTiming(time) * (toValue - fromValue); + } +} diff --git a/src/proguard/gui/splash/LinearInt.java b/src/proguard/gui/splash/LinearInt.java new file mode 100644 index 0000000..8d299bc --- /dev/null +++ b/src/proguard/gui/splash/LinearInt.java @@ -0,0 +1,55 @@ +/* + * 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.splash; + +/** + * This VariableColor varies linearly with respect to its Timing. + * + * @author Eric Lafortune + */ +public class LinearInt implements VariableInt +{ + private final int fromValue; + private final int toValue; + private final Timing timing; + + + /** + * Creates a new LinearInt. + * @param fromValue the value that corresponds to a timing of 0. + * @param toValue the value that corresponds to a timing of 1. + * @param timing the applied timing. + */ + public LinearInt(int fromValue, int toValue, Timing timing) + { + this.fromValue = fromValue; + this.toValue = toValue; + this.timing = timing; + } + + + // Implementation for VariableInt. + + public int getInt(long time) + { + return (int) (fromValue + timing.getTiming(time) * (toValue - fromValue)); + } +} diff --git a/src/proguard/gui/splash/LinearTiming.java b/src/proguard/gui/splash/LinearTiming.java new file mode 100644 index 0000000..9b26644 --- /dev/null +++ b/src/proguard/gui/splash/LinearTiming.java @@ -0,0 +1,55 @@ +/* + * 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.splash; + +/** + * This Timing ramps up linearly from 0 to 1 in a given time interval. + * + * @author Eric Lafortune + */ +public class LinearTiming implements Timing +{ + private final long fromTime; + private final long toTime; + + + /** + * Creates a new LinearTiming. + * @param fromTime the time at which the timing starts ramping up from 0. + * @param toTime the time at which the timing stops ramping up at 1. + */ + public LinearTiming(long fromTime, long toTime) + { + this.fromTime = fromTime; + this.toTime = toTime; + } + + + // Implementation for Timing. + + public double getTiming(long time) + { + // Compute the clamped linear interpolation. + return time <= fromTime ? 0.0 : + time >= toTime ? 1.0 : + (double)(time - fromTime) / (double)(toTime - fromTime); + } +} diff --git a/src/proguard/gui/splash/OverrideGraphics2D.java b/src/proguard/gui/splash/OverrideGraphics2D.java new file mode 100644 index 0000000..4333459 --- /dev/null +++ b/src/proguard/gui/splash/OverrideGraphics2D.java @@ -0,0 +1,598 @@ +/* + * 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.splash; + +import java.awt.*; +import java.awt.RenderingHints.Key; +import java.awt.font.*; +import java.awt.geom.AffineTransform; +import java.awt.image.*; +import java.awt.image.renderable.RenderableImage; +import java.text.AttributedCharacterIterator; +import java.util.Map; + +/** + * This Graphics2D allows to fix some basic settings (Color, Font, Paint, Stroke, + * XORMode) of a delegate Graphics2D, overriding any subsequent attempts to + * change those settings. + * + * @author Eric Lafortune + * @noinspection deprecation + */ +final class OverrideGraphics2D extends Graphics2D +{ + private final Graphics2D graphics; + + private Color overrideColor; + private Font overrideFont; + private Paint overridePaint; + private Stroke overrideStroke; + private Color overrideXORMode; + + private Color color; + private Font font; + private Paint paint; + private Stroke stroke; + + + /** + * Creates a new OverrideGraphics2D. + * @param graphics the delegate Graphics2D. + */ + public OverrideGraphics2D(Graphics2D graphics) + { + this.graphics = graphics; + this.color = graphics.getColor(); + this.font = graphics.getFont(); + this.paint = graphics.getPaint(); + this.stroke = graphics.getStroke(); + } + + + /** + * Fixes the Color of the Graphics2D. + * + * @param color the fixed Color, or <code>null</code> to undo the fixing. + */ + public void setOverrideColor(Color color) + { + this.overrideColor = color; + graphics.setColor(color != null ? color : this.color); + } + + /** + * Fixes the Font of the Graphics2D. + * + * @param font the fixed Font, or <code>null</code> to undo the fixing. + */ + public void setOverrideFont(Font font) + { + this.overrideFont = font; + graphics.setFont(font != null ? font : this.font); + } + + /** + * Fixes the Paint of the Graphics2D. + * + * @param paint the fixed Paint, or <code>null</code> to undo the fixing. + */ + public void setOverridePaint(Paint paint) + { + this.overridePaint = paint; + graphics.setPaint(paint != null ? paint : this.paint); + } + + /** + * Fixes the Stroke of the Graphics2D. + * + * @param stroke the fixed Stroke, or <code>null</code> to undo the fixing. + */ + public void setOverrideStroke(Stroke stroke) + { + this.overrideStroke = stroke; + graphics.setStroke(stroke != null ? stroke : this.stroke); + } + + /** + * Fixes the XORMode of the Graphics2D. + * + * @param color the fixed XORMode Color, or <code>null</code> to undo the fixing. + */ + public void setOverrideXORMode(Color color) + { + this.overrideXORMode = color; + if (color != null) + { + graphics.setXORMode(color); + } + else + { + graphics.setPaintMode(); + } + } + + + // Implementations for Graphics2D. + + public void setColor(Color color) + { + this.color = color; + if (overrideColor == null) + { + graphics.setColor(color); + } + } + + public void setFont(Font font) + { + this.font = font; + if (overrideFont == null) + { + graphics.setFont(font); + } + } + + public void setPaint(Paint paint) + { + this.paint = paint; + if (overridePaint == null) + { + graphics.setPaint(paint); + } + } + + public void setStroke(Stroke stroke) + { + this.stroke = stroke; + if (overrideStroke == null) + { + graphics.setStroke(stroke); + } + } + + public void setXORMode(Color color) + { + if (overrideXORMode == null) + { + graphics.setXORMode(color); + } + } + + public void setPaintMode() + { + if (overrideXORMode == null) + { + graphics.setPaintMode(); + } + } + + + public Color getColor() + { + return overrideColor != null ? color : graphics.getColor(); + } + + public Font getFont() + { + return overrideFont != null ? font : graphics.getFont(); + } + + public Paint getPaint() + { + return overridePaint != null ? paint : graphics.getPaint(); + } + + public Stroke getStroke() + { + return overrideStroke != null ? stroke : graphics.getStroke(); + } + + + public Graphics create() + { + OverrideGraphics2D g = new OverrideGraphics2D((Graphics2D)graphics.create()); + g.setOverrideColor(overrideColor); + g.setOverrideFont(overrideFont); + g.setOverridePaint(overridePaint); + g.setOverrideStroke(overrideStroke); + + return g; + } + + public Graphics create(int x, int y, int width, int height) + { + OverrideGraphics2D g = new OverrideGraphics2D((Graphics2D)graphics.create(x, y, width, height)); + g.setOverrideColor(overrideColor); + g.setOverrideFont(overrideFont); + g.setOverridePaint(overridePaint); + g.setOverrideStroke(overrideStroke); + + return g; + } + + + // Delegation for Graphics2D + + public void addRenderingHints(Map hints) + { + graphics.addRenderingHints(hints); + } + + public void clearRect(int x, int y, int width, int height) + { + graphics.clearRect(x, y, width, height); + } + + public void clip(Shape s) + { + graphics.clip(s); + } + + public void clipRect(int x, int y, int width, int height) + { + graphics.clipRect(x, y, width, height); + } + + public void copyArea(int x, int y, int width, int height, int dx, int dy) + { + graphics.copyArea(x, y, width, height, dx, dy); + } + + public void dispose() + { + graphics.dispose(); + } + + public void draw(Shape s) + { + graphics.draw(s); + } + + public void draw3DRect(int x, int y, int width, int height, boolean raised) + { + graphics.draw3DRect(x, y, width, height, raised); + } + + public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) + { + graphics.drawArc(x, y, width, height, startAngle, arcAngle); + } + + public void drawBytes(byte[] data, int offset, int length, int x, int y) + { + graphics.drawBytes(data, offset, length, x, y); + } + + public void drawChars(char[] data, int offset, int length, int x, int y) + { + graphics.drawChars(data, offset, length, x, y); + } + + public void drawGlyphVector(GlyphVector g, float x, float y) + { + graphics.drawGlyphVector(g, x, y); + } + + public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor, ImageObserver observer) + { + return graphics.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, bgcolor, observer); + } + + public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer) + { + return graphics.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer); + } + + public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer) + { + return graphics.drawImage(img, x, y, width, height, bgcolor, observer); + } + + public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) + { + return graphics.drawImage(img, x, y, width, height, observer); + } + + public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer) + { + return graphics.drawImage(img, x, y, bgcolor, observer); + } + + public boolean drawImage(Image img, int x, int y, ImageObserver observer) + { + return graphics.drawImage(img, x, y, observer); + } + + public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) + { + return graphics.drawImage(img, xform, obs); + } + + public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) + { + graphics.drawImage(img, op, x, y); + } + + public void drawLine(int x1, int y1, int x2, int y2) + { + graphics.drawLine(x1, y1, x2, y2); + } + + public void drawOval(int x, int y, int width, int height) + { + graphics.drawOval(x, y, width, height); + } + + public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) + { + graphics.drawPolygon(xPoints, yPoints, nPoints); + } + + public void drawPolygon(Polygon p) + { + graphics.drawPolygon(p); + } + + public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) + { + graphics.drawPolyline(xPoints, yPoints, nPoints); + } + + public void drawRect(int x, int y, int width, int height) + { + graphics.drawRect(x, y, width, height); + } + + public void drawRenderableImage(RenderableImage img, AffineTransform xform) + { + graphics.drawRenderableImage(img, xform); + } + + public void drawRenderedImage(RenderedImage img, AffineTransform xform) + { + graphics.drawRenderedImage(img, xform); + } + + public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) + { + graphics.drawRoundRect(x, y, width, height, arcWidth, arcHeight); + } + + public void drawString(String s, float x, float y) + { + graphics.drawString(s, x, y); + } + + public void drawString(String str, int x, int y) + { + graphics.drawString(str, x, y); + } + + public void drawString(AttributedCharacterIterator iterator, float x, float y) + { + graphics.drawString(iterator, x, y); + } + + public void drawString(AttributedCharacterIterator iterator, int x, int y) + { + graphics.drawString(iterator, x, y); + } + + public boolean equals(Object obj) + { + return graphics.equals(obj); + } + + public void fill(Shape s) + { + graphics.fill(s); + } + + public void fill3DRect(int x, int y, int width, int height, boolean raised) + { + graphics.fill3DRect(x, y, width, height, raised); + } + + public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) + { + graphics.fillArc(x, y, width, height, startAngle, arcAngle); + } + + public void fillOval(int x, int y, int width, int height) + { + graphics.fillOval(x, y, width, height); + } + + public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) + { + graphics.fillPolygon(xPoints, yPoints, nPoints); + } + + public void fillPolygon(Polygon p) + { + graphics.fillPolygon(p); + } + + public void fillRect(int x, int y, int width, int height) + { + graphics.fillRect(x, y, width, height); + } + + public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) + { + graphics.fillRoundRect(x, y, width, height, arcWidth, arcHeight); + } + + public Color getBackground() + { + return graphics.getBackground(); + } + + public Shape getClip() + { + return graphics.getClip(); + } + + public Rectangle getClipBounds() + { + return graphics.getClipBounds(); + } + + public Rectangle getClipBounds(Rectangle r) + { + return graphics.getClipBounds(r); + } + + public Rectangle getClipRect() + { + return graphics.getClipRect(); + } + + public Composite getComposite() + { + return graphics.getComposite(); + } + + public GraphicsConfiguration getDeviceConfiguration() + { + return graphics.getDeviceConfiguration(); + } + + public FontMetrics getFontMetrics() + { + return graphics.getFontMetrics(); + } + + public FontMetrics getFontMetrics(Font f) + { + return graphics.getFontMetrics(f); + } + + public FontRenderContext getFontRenderContext() + { + return graphics.getFontRenderContext(); + } + + public Object getRenderingHint(Key hintKey) + { + return graphics.getRenderingHint(hintKey); + } + + public RenderingHints getRenderingHints() + { + return graphics.getRenderingHints(); + } + + public AffineTransform getTransform() + { + return graphics.getTransform(); + } + + public int hashCode() + { + return graphics.hashCode(); + } + + public boolean hit(Rectangle rect, Shape s, boolean onStroke) + { + return graphics.hit(rect, s, onStroke); + } + + public boolean hitClip(int x, int y, int width, int height) + { + return graphics.hitClip(x, y, width, height); + } + + public void rotate(double theta) + { + graphics.rotate(theta); + } + + public void rotate(double theta, double x, double y) + { + graphics.rotate(theta, x, y); + } + + public void scale(double sx, double sy) + { + graphics.scale(sx, sy); + } + + public void setBackground(Color color) + { + graphics.setBackground(color); + } + + public void setClip(int x, int y, int width, int height) + { + graphics.setClip(x, y, width, height); + } + + public void setClip(Shape clip) + { + graphics.setClip(clip); + } + + public void setComposite(Composite comp) + { + graphics.setComposite(comp); + } + + public void setRenderingHint(Key hintKey, Object hintValue) + { + graphics.setRenderingHint(hintKey, hintValue); + } + + public void setRenderingHints(Map hints) + { + graphics.setRenderingHints(hints); + } + + public void setTransform(AffineTransform Tx) + { + graphics.setTransform(Tx); + } + + public void shear(double shx, double shy) + { + graphics.shear(shx, shy); + } + + public String toString() + { + return graphics.toString(); + } + + public void transform(AffineTransform Tx) + { + graphics.transform(Tx); + } + + public void translate(double tx, double ty) + { + graphics.translate(tx, ty); + } + + public void translate(int x, int y) + { + graphics.translate(x, y); + } +} diff --git a/src/proguard/gui/splash/RectangleSprite.java b/src/proguard/gui/splash/RectangleSprite.java new file mode 100644 index 0000000..d204831 --- /dev/null +++ b/src/proguard/gui/splash/RectangleSprite.java @@ -0,0 +1,114 @@ +/* + * 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.splash; + +import java.awt.*; + +/** + * This Sprite represents an animated rounded rectangle. It can optionally be filled. + * + * @author Eric Lafortune + */ +public class RectangleSprite implements Sprite +{ + private final boolean filled; + private final VariableColor color; + private final VariableInt x; + private final VariableInt y; + private final VariableInt width; + private final VariableInt height; + private final VariableInt arcWidth; + private final VariableInt arcHeight; + + + /** + * Creates a new rectangular RectangleSprite. + * @param filled specifies whether the rectangle should be filled. + * @param color the variable color of the rectangle. + * @param x the variable x-ordinate of the upper-left corner of the rectangle. + * @param y the variable y-ordinate of the upper-left corner of the rectangle. + * @param width the variable width of the rectangle. + * @param height the variable height of the rectangle. + */ + public RectangleSprite(boolean filled, + VariableColor color, + VariableInt x, + VariableInt y, + VariableInt width, + VariableInt height) + { + this(filled, color, x, y, width, height, new ConstantInt(0), new ConstantInt(0)); + } + + + /** + * Creates a new RectangleSprite with rounded corners. + * @param filled specifies whether the rectangle should be filled. + * @param color the variable color of the rectangle. + * @param x the variable x-ordinate of the upper-left corner of the rectangle. + * @param y the variable y-ordinate of the upper-left corner of the rectangle. + * @param width the variable width of the rectangle. + * @param height the variable height of the rectangle. + * @param arcWidth the variable width of the corner arcs. + * @param arcHeight the variable height of the corner arcs. + */ + public RectangleSprite(boolean filled, + VariableColor color, + VariableInt x, + VariableInt y, + VariableInt width, + VariableInt height, + VariableInt arcWidth, + VariableInt arcHeight) + { + this.filled = filled; + this.color = color; + this.x = x; + this.y = y; + this.width = width; + this.height = height; + this.arcWidth = arcWidth; + this.arcHeight = arcHeight; + } + + // Implementation for Sprite. + + public void paint(Graphics graphics, long time) + { + graphics.setColor(color.getColor(time)); + + int xt = x.getInt(time); + int yt = y.getInt(time); + int w = width.getInt(time); + int h = height.getInt(time); + int aw = arcWidth.getInt(time); + int ah = arcHeight.getInt(time); + + if (filled) + { + graphics.fillRoundRect(xt, yt, w, h, aw, ah); + } + else + { + graphics.drawRoundRect(xt, yt, w, h, aw, ah); + } + } +} diff --git a/src/proguard/gui/splash/SawToothTiming.java b/src/proguard/gui/splash/SawToothTiming.java new file mode 100644 index 0000000..076d5e2 --- /dev/null +++ b/src/proguard/gui/splash/SawToothTiming.java @@ -0,0 +1,53 @@ +/* + * 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.splash; + +/** + * This Timing ramps up linearly from 0 to 1 in a given repeated time interval. + * + * @author Eric Lafortune + */ +public class SawToothTiming implements Timing +{ + private final long period; + private final long phase; + + + /** + * Creates a new SawToothTiming. + * @param period the time period for a full cycle. + * @param phase the phase of the cycle, which is added to the actual time. + */ + public SawToothTiming(long period, long phase) + { + this.period = period; + this.phase = phase; + } + + + // Implementation for Timing. + + public double getTiming(long time) + { + // Compute the translated and scaled saw-tooth function. + return (double)((time + phase) % period) / (double)period; + } +} diff --git a/src/proguard/gui/splash/ShadowedSprite.java b/src/proguard/gui/splash/ShadowedSprite.java new file mode 100644 index 0000000..c3504f3 --- /dev/null +++ b/src/proguard/gui/splash/ShadowedSprite.java @@ -0,0 +1,109 @@ +/* + * 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.splash; + +import java.awt.*; + +/** + * This Sprite adds a drop shadow to another Sprite. + * + * @author Eric Lafortune + */ +public class ShadowedSprite implements Sprite +{ + private final VariableInt xOffset; + private final VariableInt yOffset; + private final VariableDouble alpha; + private final VariableInt blur; + private final Sprite sprite; + + private float cachedAlpha = -1.0f; + private Color cachedColor; + + + /** + * Creates a new ShadowedSprite. + * @param xOffset the variable x-offset of the shadow, relative to the sprite itself. + * @param yOffset the variable y-offset of the shadow, relative to the sprite itself. + * @param alpha the variable darkness of the shadow (between 0 and 1). + * @param blur the variable blur of the shadow (0 for sharp shadows, 1 or + * more for increasingly blurry shadows). + * @param sprite the Sprite to be painted with its shadow. + */ + public ShadowedSprite(VariableInt xOffset, + VariableInt yOffset, + VariableDouble alpha, + VariableInt blur, + Sprite sprite) + { + this.xOffset = xOffset; + this.yOffset = yOffset; + this.alpha = alpha; + this.blur = blur; + this.sprite = sprite; + } + + + // Implementation for Sprite. + + public void paint(Graphics graphics, long time) + { + double l = alpha.getDouble(time); + int b = blur.getInt(time) + 1; + + float a = 1.0f - (float)Math.pow(1.0 - l, 1.0/(b*b)); + if (a != cachedAlpha) + { + cachedAlpha = a; + cachedColor = new Color(0f, 0f, 0f, a); + } + + // Set up the shadow graphics. + //OverrideGraphics2D g = new OverrideGraphics2D((Graphics2D)graphics); + //g.setOverrideColor(cachedColor); + + // Set the shadow color. + Color actualColor = graphics.getColor(); + graphics.setColor(cachedColor); + + int xo = xOffset.getInt(time) - b/2; + int yo = yOffset.getInt(time) - b/2; + + // Draw the sprite's shadow. + for (int x = 0; x < b; x++) + { + for (int y = 0; y < b; y++) + { + int xt = xo + x; + int yt = yo + y; + graphics.translate(xt, yt); + sprite.paint(graphics, time); + graphics.translate(-xt, -yt); + } + } + + // Restore the actual sprite color. + graphics.setColor(actualColor); + + // Draw the sprite itself in the ordinary graphics. + sprite.paint(graphics, time); + } +} diff --git a/src/proguard/gui/splash/SineTiming.java b/src/proguard/gui/splash/SineTiming.java new file mode 100644 index 0000000..eb0a7cc --- /dev/null +++ b/src/proguard/gui/splash/SineTiming.java @@ -0,0 +1,53 @@ +/* + * 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.splash; + +/** + * This Timing varies between 0 and 1, as a sine wave over time. + * + * @author Eric Lafortune + */ +public class SineTiming implements Timing +{ + private final long period; + private final long phase; + + + /** + * Creates a new SineTiming. + * @param period the time period for a full cycle. + * @param phase the phase of the cycle, which is added to the actual time. + */ + public SineTiming(long period, long phase) + { + this.period = period; + this.phase = phase; + } + + + // Implementation for Timing. + + public double getTiming(long time) + { + // Compute the translated and scaled sine function. + return 0.5 + 0.5 * Math.sin(2.0 * Math.PI * (time + phase) / period); + } +} diff --git a/src/proguard/gui/splash/SmoothTiming.java b/src/proguard/gui/splash/SmoothTiming.java new file mode 100644 index 0000000..a985712 --- /dev/null +++ b/src/proguard/gui/splash/SmoothTiming.java @@ -0,0 +1,66 @@ +/* + * 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.splash; + +/** + * This Timing ramps up smoothly from 0 to 1 in a given time interval. + * + * @author Eric Lafortune + */ +public class SmoothTiming implements Timing +{ + private final long fromTime; + private final long toTime; + + + /** + * Creates a new SmoothTiming. + * @param fromTime the time at which the timing starts ramping up from 0. + * @param toTime the time at which the timing stops ramping up at 1. + */ + public SmoothTiming(long fromTime, long toTime) + { + this.fromTime = fromTime; + this.toTime = toTime; + } + + + // Implementation for Timing. + + public double getTiming(long time) + { + if (time <= fromTime) + { + return 0.0; + } + + if (time >= toTime) + { + return 1.0; + } + + // Compute the linear interpolation. + double timing = (double) (time - fromTime) / (double) (toTime - fromTime); + + // Smooth the interpolation at the ends. + return timing * timing * (3.0 - 2.0 * timing); + } +} diff --git a/src/proguard/gui/splash/SplashPanel.java b/src/proguard/gui/splash/SplashPanel.java new file mode 100644 index 0000000..23a9ce4 --- /dev/null +++ b/src/proguard/gui/splash/SplashPanel.java @@ -0,0 +1,235 @@ +/* + * 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.splash; + +import proguard.gui.SwingUtil; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.lang.reflect.InvocationTargetException; + +/** + * This JPanel renders an animated Sprite. + * + * @author Eric Lafortune + */ +public class SplashPanel extends JPanel +{ + private final MyAnimator animator = new MyAnimator(); + private final MyRepainter repainter = new MyRepainter(); + + private final Sprite sprite; + private final double sleepFactor; + + private long startTime = Long.MAX_VALUE; + private final long stopTime; + + private volatile Thread animationThread; + + + /** + * Creates a new SplashPanel with the given Sprite, which will be animated + * indefinitely. + * @param sprite the Sprite that will be animated. + * @param processorLoad the fraction of processing time to be spend on + * animating the Sprite (between 0 and 1). + */ + public SplashPanel(Sprite sprite, double processorLoad) + { + this(sprite, processorLoad, (long)Integer.MAX_VALUE); + } + + + /** + * Creates a new SplashPanel with the given Sprite, which will be animated + * for a limited period of time. + * @param sprite the Sprite that will be animated. + * @param processorLoad the fraction of processing time to be spend on + * animating the Sprite (between 0 and 1). + * @param stopTime the number of milliseconds after which the + * animation will be stopped automatically. + */ + public SplashPanel(Sprite sprite, double processorLoad, long stopTime) + { + this.sprite = sprite; + this.sleepFactor = (1.0-processorLoad) / processorLoad; + this.stopTime = stopTime; + + // Restart the animation on a mouse click. + addMouseListener(new MouseAdapter() + { + public void mouseClicked(MouseEvent e) + { + SplashPanel.this.start(); + } + }); + } + + + /** + * Starts the animation. + */ + public void start() + { + // Go to the beginning of the animation. + startTime = System.currentTimeMillis(); + + // Make sure we have an animation thread running. + if (animationThread == null) + { + animationThread = new Thread(animator); + animationThread.start(); + } + } + + + /** + * Stops the animation. + */ + public void stop() + { + // Go to the end of the animation. + startTime = 0L; + + // Let the animation thread stop itself. + animationThread = null; + + // Repaint the SplashPanel one last time. + try + { + SwingUtil.invokeAndWait(repainter); + } + catch (InterruptedException ex) + { + // Nothing. + } + catch (InvocationTargetException ex) + { + // Nothing. + } + } + + + // Implementation for JPanel. + + public void paintComponent(Graphics graphics) + { + super.paintComponent(graphics); + + sprite.paint(graphics, System.currentTimeMillis() - startTime); + } + + + /** + * This Runnable makes sure its SplashPanel gets repainted regularly, + * depending on the targeted processor load. + */ + private class MyAnimator implements Runnable + { + public void run() + { + try + { + while (animationThread != null) + { + // Check if we should stop the animation. + long time = System.currentTimeMillis(); + if (time > startTime + stopTime) + { + animationThread = null; + } + + // Do a repaint and time it. + SwingUtil.invokeAndWait(repainter); + + // Sleep for a proportional while. + long repaintTime = System.currentTimeMillis() - time; + long sleepTime = (long)(sleepFactor * repaintTime); + if (sleepTime < 10L) + { + sleepTime = 10L; + } + + Thread.sleep(sleepTime); + } + } + catch (InterruptedException ex) + { + // Nothing. + } + catch (InvocationTargetException ex) + { + // Nothing. + } + } + } + + + /** + * This Runnable repaints its SplashPanel. + */ + private class MyRepainter implements Runnable + { + public void run() + { + SplashPanel.this.repaint(); + } + } + + + /** + * A main method for testing the splash panel. + */ + public static void main(String[] args) + { + JFrame frame = new JFrame(); + frame.setTitle("Animation"); + frame.setSize(800, 600); + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + Dimension frameSize = frame.getSize(); + frame.setLocation((screenSize.width - frameSize.width) / 2, + (screenSize.height - frameSize.height) / 2); + + Sprite sprite = + new ClipSprite( + new ConstantColor(Color.white), + new ConstantColor(Color.lightGray), + new CircleSprite(true, + new LinearInt(200, 600, new SineTiming(2345L, 0L)), + new LinearInt(200, 400, new SineTiming(3210L, 0L)), + new ConstantInt(150)), + new ColorSprite(new ConstantColor(Color.gray), + new FontSprite(new ConstantFont(new Font("sansserif", Font.BOLD, 90)), + new TextSprite(new ConstantString("ProGuard"), + new ConstantInt(200), + new ConstantInt(300))))); + + SplashPanel panel = new SplashPanel(sprite, 0.5); + panel.setBackground(Color.white); + + frame.getContentPane().add(panel); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setVisible(true); + + panel.start(); + } +} diff --git a/src/proguard/gui/splash/Sprite.java b/src/proguard/gui/splash/Sprite.java new file mode 100644 index 0000000..ada7a81 --- /dev/null +++ b/src/proguard/gui/splash/Sprite.java @@ -0,0 +1,41 @@ +/* + * 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.splash; + +import java.awt.*; + +/** + * This interface describes objects that can paint themselves, possibly varying + * as a function of time. + * + * @author Eric Lafortune + */ +public interface Sprite +{ + /** + * Paints the object. + * + * @param graphics the Graphics to paint on. + * @param time the time since the start of the animation, expressed in + * milliseconds. + */ + public void paint(Graphics graphics, long time); +} diff --git a/src/proguard/gui/splash/TextSprite.java b/src/proguard/gui/splash/TextSprite.java new file mode 100644 index 0000000..bbf37d4 --- /dev/null +++ b/src/proguard/gui/splash/TextSprite.java @@ -0,0 +1,89 @@ +/* + * 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.splash; + +import java.awt.*; + +/** + * This Sprite represents a text. + * + * @author Eric Lafortune + */ +public class TextSprite implements Sprite +{ + private final VariableString[] text; + private final VariableInt spacing; + private final VariableInt x; + private final VariableInt y; + + + /** + * Creates a new TextSprite containing a single line of text. + * @param text the variable text string. + * @param x the variable x-coordinate of the lower-left corner of the text. + * @param y the variable y-coordinate of the lower-left corner of the text. + */ + public TextSprite(VariableString text, + VariableInt x, + VariableInt y) + { + this(new VariableString[] { text }, new ConstantInt(0), x, y); + } + + + /** + * Creates a new TextSprite containing a multiple lines of text. + * @param text the variable text strings. + * @param spacing the variable spacing between the lines of text. + * @param x the variable x-coordinate of the lower-left corner of the + * first line of text. + * @param y the variable y-coordinate of the lower-left corner of the + * first line of text. + */ + public TextSprite(VariableString[] text, + VariableInt spacing, + VariableInt x, + VariableInt y) + { + + this.text = text; + this.spacing = spacing; + this.x = x; + this.y = y; + } + + + // Implementation for Sprite. + + public void paint(Graphics graphics, long time) + { + + int xt = x.getInt(time); + int yt = y.getInt(time); + + int spacingt = spacing.getInt(time); + + for (int index = 0; index < text.length; index++) + { + graphics.drawString(text[index].getString(time), xt, yt + index * spacingt); + } + } +} diff --git a/src/proguard/gui/splash/TimeSwitchSprite.java b/src/proguard/gui/splash/TimeSwitchSprite.java new file mode 100644 index 0000000..921bef2 --- /dev/null +++ b/src/proguard/gui/splash/TimeSwitchSprite.java @@ -0,0 +1,75 @@ +/* + * 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.splash; + +import java.awt.*; + +/** + * This Sprite displays another Sprite in a given time interval. + * The time of the encapsulated Sprite is shifted by the start time. + * + * @author Eric Lafortune + */ +public class TimeSwitchSprite implements Sprite +{ + private final long onTime; + private final long offTime; + private final Sprite sprite; + + + /** + * Creates a new TimeSwitchSprite for displaying a given Sprite starting at + * a given time. + * @param onTime the start time. + * @param sprite the toggled Sprite. + */ + public TimeSwitchSprite(long onTime, Sprite sprite) + { + this(onTime, 0L, sprite); + } + + + /** + * Creates a new TimeSwitchSprite for displaying a given Sprite in a given + * time interval. + * @param onTime the start time. + * @param offTime the stop time. + * @param sprite the toggled Sprite. + */ + public TimeSwitchSprite(long onTime, long offTime, Sprite sprite) + { + this.onTime = onTime; + this.offTime = offTime; + this.sprite = sprite; + } + + + // Implementation for Sprite. + + public void paint(Graphics graphics, long time) + { + if (time >= onTime && (offTime <= 0 || time <= offTime)) + { + sprite.paint(graphics, time - onTime); + } + + } +} diff --git a/src/proguard/gui/splash/Timing.java b/src/proguard/gui/splash/Timing.java new file mode 100644 index 0000000..887d737 --- /dev/null +++ b/src/proguard/gui/splash/Timing.java @@ -0,0 +1,34 @@ +/* + * 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.splash; + +/** + * This interface maps a time to a normalized timing between 0 and 1. + * + * @author Eric Lafortune + */ +interface Timing +{ + /** + * Returns the timing for the given time. + */ + public double getTiming(long time); +} diff --git a/src/proguard/gui/splash/TypeWriterString.java b/src/proguard/gui/splash/TypeWriterString.java new file mode 100644 index 0000000..9f1441e --- /dev/null +++ b/src/proguard/gui/splash/TypeWriterString.java @@ -0,0 +1,71 @@ +/* + * 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.splash; + +/** + * This VariableString produces a String that grows linearly with respect to its + * Timing, as if it is being written on a typewriter. A cursor at the end + * precedes the typed characters. + * + * @author Eric Lafortune + */ +public class TypeWriterString implements VariableString +{ + private final String string; + private final Timing timing; + + private int cachedLength = -1; + private String cachedString; + + + /** + * Creates a new TypeWriterString. + * @param string the basic String. + * @param timing the applied timing. + */ + public TypeWriterString(String string, Timing timing) + { + this.string = string; + this.timing = timing; + } + + + // Implementation for VariableString. + + public String getString(long time) + { + double t = timing.getTiming(time); + + int stringLength = string.length(); + int length = (int)(stringLength * t + 0.5); + if (length != cachedLength) + { + cachedLength = length; + cachedString = string.substring(0, length); + if (t > 0.0 && length < stringLength) + { + cachedString += "_"; + } + } + + return cachedString; + } +} diff --git a/src/proguard/gui/splash/VariableColor.java b/src/proguard/gui/splash/VariableColor.java new file mode 100644 index 0000000..6a30062 --- /dev/null +++ b/src/proguard/gui/splash/VariableColor.java @@ -0,0 +1,36 @@ +/* + * 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.splash; + +import java.awt.*; + +/** + * This interface represents a Color that varies with time. + * + * @author Eric Lafortune + */ +interface VariableColor +{ + /** + * Returns the Color for the given time. + */ + public Color getColor(long time); +} diff --git a/src/proguard/gui/splash/VariableDouble.java b/src/proguard/gui/splash/VariableDouble.java new file mode 100644 index 0000000..39302dd --- /dev/null +++ b/src/proguard/gui/splash/VariableDouble.java @@ -0,0 +1,34 @@ +/* + * 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.splash; + +/** + * This interface represents a double that varies with time. + * + * @author Eric Lafortune + */ +interface VariableDouble +{ + /** + * Returns the double for the given time. + */ + public double getDouble(long time); +} diff --git a/src/proguard/gui/splash/VariableFont.java b/src/proguard/gui/splash/VariableFont.java new file mode 100644 index 0000000..a7de8d7 --- /dev/null +++ b/src/proguard/gui/splash/VariableFont.java @@ -0,0 +1,36 @@ +/* + * 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.splash; + +import java.awt.*; + +/** + * This interface represents a Font that varies with time. + * + * @author Eric Lafortune + */ +interface VariableFont +{ + /** + * Returns the Font for the given time. + */ + public Font getFont(long time); +} diff --git a/src/proguard/gui/splash/VariableInt.java b/src/proguard/gui/splash/VariableInt.java new file mode 100644 index 0000000..68a33af --- /dev/null +++ b/src/proguard/gui/splash/VariableInt.java @@ -0,0 +1,34 @@ +/* + * 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.splash; + +/** + * This interface represents an integer that varies with time. + * + * @author Eric Lafortune + */ +interface VariableInt +{ + /** + * Returns the integer for the given time. + */ + public int getInt(long time); +} diff --git a/src/proguard/gui/splash/VariableSizeFont.java b/src/proguard/gui/splash/VariableSizeFont.java new file mode 100644 index 0000000..e36d28c --- /dev/null +++ b/src/proguard/gui/splash/VariableSizeFont.java @@ -0,0 +1,65 @@ +/* + * 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.splash; + +import java.awt.*; + +/** + * This VariableFont varies in size with respect to its Timing. + * + * @author Eric Lafortune + */ +public class VariableSizeFont implements VariableFont +{ + private final Font font; + private final VariableDouble size; + + private float cachedSize = -1.0f; + private Font cachedFont; + + + /** + * Creates a new VariableSizeFont + * @param font the base font. + * @param size the variable size of the font. + */ + public VariableSizeFont(Font font, VariableDouble size) + { + this.font = font; + this.size = size; + } + + + // Implementation for VariableFont. + + public Font getFont(long time) + { + float s = (float)size.getDouble(time); + + if (s != cachedSize) + { + cachedSize = s; + cachedFont = font.deriveFont((float)s); + } + + return cachedFont; + } +} diff --git a/src/proguard/gui/splash/VariableString.java b/src/proguard/gui/splash/VariableString.java new file mode 100644 index 0000000..1dec23b --- /dev/null +++ b/src/proguard/gui/splash/VariableString.java @@ -0,0 +1,34 @@ +/* + * 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.splash; + +/** + * This interface represents a String that varies with time. + * + * @author Eric Lafortune + */ +interface VariableString +{ + /** + * Returns the String for the given time. + */ + public String getString(long time); +} diff --git a/src/proguard/gui/splash/package.html b/src/proguard/gui/splash/package.html new file mode 100644 index 0000000..209fad7 --- /dev/null +++ b/src/proguard/gui/splash/package.html @@ -0,0 +1,4 @@ +<body> +This package contains a library for creating splash screens and animations +with text, graphical elements, and some special effects. +</body> diff --git a/src/proguard/gui/vtitle.png b/src/proguard/gui/vtitle.png Binary files differnew file mode 100644 index 0000000..218f716 --- /dev/null +++ b/src/proguard/gui/vtitle.png |