Home | History | Annotate | Download | only in gui
      1 /*
      2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
      3  *             of Java bytecode.
      4  *
      5  * Copyright (c) 2002-2013 Eric Lafortune (eric (at) graphics.cornell.edu)
      6  *
      7  * This program is free software; you can redistribute it and/or modify it
      8  * under the terms of the GNU General Public License as published by the Free
      9  * Software Foundation; either version 2 of the License, or (at your option)
     10  * any later version.
     11  *
     12  * This program is distributed in the hope that it will be useful, but WITHOUT
     13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     14  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
     15  * more details.
     16  *
     17  * You should have received a copy of the GNU General Public License along
     18  * with this program; if not, write to the Free Software Foundation, Inc.,
     19  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
     20  */
     21 package proguard.gui;
     22 
     23 import proguard.*;
     24 import proguard.classfile.util.ClassUtil;
     25 import proguard.gui.splash.*;
     26 import proguard.util.ListUtil;
     27 
     28 import javax.swing.*;
     29 import javax.swing.border.*;
     30 import java.awt.*;
     31 import java.awt.event.*;
     32 import java.io.*;
     33 import java.net.URL;
     34 import java.util.*;
     35 import java.util.List;
     36 
     37 
     38 /**
     39  * GUI for configuring and executing ProGuard and ReTrace.
     40  *
     41  * @author Eric Lafortune
     42  */
     43 public class ProGuardGUI extends JFrame
     44 {
     45     private static final String NO_SPLASH_OPTION = "-nosplash";
     46 
     47     private static final String TITLE_IMAGE_FILE          = "vtitle.png";
     48     private static final String BOILERPLATE_CONFIGURATION = "boilerplate.pro";
     49     private static final String DEFAULT_CONFIGURATION     = "default.pro";
     50 
     51     private static final String OPTIMIZATIONS_DEFAULT                = "*";
     52     private static final String KEEP_ATTRIBUTE_DEFAULT               = "Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,LocalVariable*Table,*Annotation*,Synthetic,EnclosingMethod";
     53     private static final String SOURCE_FILE_ATTRIBUTE_DEFAULT        = "SourceFile";
     54     private static final String ADAPT_RESOURCE_FILE_NAMES_DEFAULT    = "**.properties";
     55     private static final String ADAPT_RESOURCE_FILE_CONTENTS_DEFAULT = "**.properties,META-INF/MANIFEST.MF";
     56 
     57     private static final Border BORDER = BorderFactory.createEtchedBorder(EtchedBorder.RAISED);
     58 
     59     static boolean systemOutRedirected;
     60 
     61     private final JFileChooser configurationChooser = new JFileChooser("");
     62     private final JFileChooser fileChooser          = new JFileChooser("");
     63 
     64     private final SplashPanel splashPanel;
     65 
     66     private final ClassPathPanel programPanel = new ClassPathPanel(this, true);
     67     private final ClassPathPanel libraryPanel = new ClassPathPanel(this, false);
     68 
     69     private       KeepClassSpecification[] boilerplateKeep;
     70     private final JCheckBox[]              boilerplateKeepCheckBoxes;
     71     private final JTextField[]             boilerplateKeepTextFields;
     72 
     73     private final KeepSpecificationsPanel additionalKeepPanel = new KeepSpecificationsPanel(this, true, false, false, false, false);
     74 
     75     private       KeepClassSpecification[] boilerplateKeepNames;
     76     private final JCheckBox[]              boilerplateKeepNamesCheckBoxes;
     77     private final JTextField[]             boilerplateKeepNamesTextFields;
     78 
     79     private final KeepSpecificationsPanel additionalKeepNamesPanel = new KeepSpecificationsPanel(this, true, false, true, false, false);
     80 
     81     private       ClassSpecification[] boilerplateNoSideEffectMethods;
     82     private final JCheckBox[]          boilerplateNoSideEffectMethodCheckBoxes;
     83 
     84     private final ClassSpecificationsPanel additionalNoSideEffectsPanel = new ClassSpecificationsPanel(this, false);
     85 
     86     private final ClassSpecificationsPanel whyAreYouKeepingPanel = new ClassSpecificationsPanel(this, false);
     87 
     88     private final JCheckBox shrinkCheckBox     = new JCheckBox(msg("shrink"));
     89     private final JCheckBox printUsageCheckBox = new JCheckBox(msg("printUsage"));
     90 
     91     private final JCheckBox optimizeCheckBox                    = new JCheckBox(msg("optimize"));
     92     private final JCheckBox allowAccessModificationCheckBox     = new JCheckBox(msg("allowAccessModification"));
     93     private final JCheckBox mergeInterfacesAggressivelyCheckBox = new JCheckBox(msg("mergeInterfacesAggressively"));
     94     private final JLabel    optimizationsLabel                  = new JLabel(msg("optimizations"));
     95     private final JLabel    optimizationPassesLabel             = new JLabel(msg("optimizationPasses"));
     96 
     97     private final JSpinner optimizationPassesSpinner = new JSpinner(new SpinnerNumberModel(1, 1, 9, 1));
     98 
     99     private final JCheckBox obfuscateCheckBox                    = new JCheckBox(msg("obfuscate"));
    100     private final JCheckBox printMappingCheckBox                 = new JCheckBox(msg("printMapping"));
    101     private final JCheckBox applyMappingCheckBox                 = new JCheckBox(msg("applyMapping"));
    102     private final JCheckBox obfuscationDictionaryCheckBox        = new JCheckBox(msg("obfuscationDictionary"));
    103     private final JCheckBox classObfuscationDictionaryCheckBox   = new JCheckBox(msg("classObfuscationDictionary"));
    104     private final JCheckBox packageObfuscationDictionaryCheckBox = new JCheckBox(msg("packageObfuscationDictionary"));
    105     private final JCheckBox overloadAggressivelyCheckBox         = new JCheckBox(msg("overloadAggressively"));
    106     private final JCheckBox useUniqueClassMemberNamesCheckBox    = new JCheckBox(msg("useUniqueClassMemberNames"));
    107     private final JCheckBox useMixedCaseClassNamesCheckBox       = new JCheckBox(msg("useMixedCaseClassNames"));
    108     private final JCheckBox keepPackageNamesCheckBox             = new JCheckBox(msg("keepPackageNames"));
    109     private final JCheckBox flattenPackageHierarchyCheckBox      = new JCheckBox(msg("flattenPackageHierarchy"));
    110     private final JCheckBox repackageClassesCheckBox             = new JCheckBox(msg("repackageClasses"));
    111     private final JCheckBox keepAttributesCheckBox               = new JCheckBox(msg("keepAttributes"));
    112     private final JCheckBox keepParameterNamesCheckBox           = new JCheckBox(msg("keepParameterNames"));
    113     private final JCheckBox newSourceFileAttributeCheckBox       = new JCheckBox(msg("renameSourceFileAttribute"));
    114     private final JCheckBox adaptClassStringsCheckBox            = new JCheckBox(msg("adaptClassStrings"));
    115     private final JCheckBox adaptResourceFileNamesCheckBox       = new JCheckBox(msg("adaptResourceFileNames"));
    116     private final JCheckBox adaptResourceFileContentsCheckBox    = new JCheckBox(msg("adaptResourceFileContents"));
    117 
    118     private final JCheckBox preverifyCheckBox    = new JCheckBox(msg("preverify"));
    119     private final JCheckBox microEditionCheckBox = new JCheckBox(msg("microEdition"));
    120     private final JCheckBox targetCheckBox       = new JCheckBox(msg("target"));
    121 
    122     private final JComboBox targetComboBox = new JComboBox(ListUtil.commaSeparatedList(msg("targets")).toArray());
    123 
    124     private final JCheckBox verboseCheckBox                          = new JCheckBox(msg("verbose"));
    125     private final JCheckBox noteCheckBox                             = new JCheckBox(msg("note"));
    126     private final JCheckBox warnCheckBox                             = new JCheckBox(msg("warn"));
    127     private final JCheckBox ignoreWarningsCheckBox                   = new JCheckBox(msg("ignoreWarnings"));
    128     private final JCheckBox skipNonPublicLibraryClassesCheckBox      = new JCheckBox(msg("skipNonPublicLibraryClasses"));
    129     private final JCheckBox skipNonPublicLibraryClassMembersCheckBox = new JCheckBox(msg("skipNonPublicLibraryClassMembers"));
    130     private final JCheckBox keepDirectoriesCheckBox                  = new JCheckBox(msg("keepDirectories"));
    131     private final JCheckBox forceProcessingCheckBox                  = new JCheckBox(msg("forceProcessing"));
    132     private final JCheckBox printSeedsCheckBox                       = new JCheckBox(msg("printSeeds"));
    133     private final JCheckBox printConfigurationCheckBox               = new JCheckBox(msg("printConfiguration"));
    134     private final JCheckBox dumpCheckBox                             = new JCheckBox(msg("dump"));
    135 
    136     private final JTextField printUsageTextField                   = new JTextField(40);
    137     private final JTextField optimizationsTextField                = new JTextField(40);
    138     private final JTextField printMappingTextField                 = new JTextField(40);
    139     private final JTextField applyMappingTextField                 = new JTextField(40);
    140     private final JTextField obfuscationDictionaryTextField        = new JTextField(40);
    141     private final JTextField classObfuscationDictionaryTextField   = new JTextField(40);
    142     private final JTextField packageObfuscationDictionaryTextField = new JTextField(40);
    143     private final JTextField keepPackageNamesTextField             = new JTextField(40);
    144     private final JTextField flattenPackageHierarchyTextField      = new JTextField(40);
    145     private final JTextField repackageClassesTextField             = new JTextField(40);
    146     private final JTextField keepAttributesTextField               = new JTextField(40);
    147     private final JTextField newSourceFileAttributeTextField       = new JTextField(40);
    148     private final JTextField adaptClassStringsTextField            = new JTextField(40);
    149     private final JTextField adaptResourceFileNamesTextField       = new JTextField(40);
    150     private final JTextField adaptResourceFileContentsTextField    = new JTextField(40);
    151     private final JTextField noteTextField                         = new JTextField(40);
    152     private final JTextField warnTextField                         = new JTextField(40);
    153     private final JTextField keepDirectoriesTextField              = new JTextField(40);
    154     private final JTextField printSeedsTextField                   = new JTextField(40);
    155     private final JTextField printConfigurationTextField           = new JTextField(40);
    156     private final JTextField dumpTextField                         = new JTextField(40);
    157 
    158     private final JTextArea  consoleTextArea = new JTextArea(msg("processingInfo"), 3, 40);
    159 
    160     private final JCheckBox  reTraceVerboseCheckBox  = new JCheckBox(msg("verbose"));
    161     private final JTextField reTraceMappingTextField = new JTextField(40);
    162     private final JTextArea  stackTraceTextArea      = new JTextArea(3, 40);
    163     private final JTextArea  reTraceTextArea         = new JTextArea(msg("reTraceInfo"), 3, 40);
    164 
    165 
    166     /**
    167      * Creates a new ProGuardGUI.
    168      */
    169     public ProGuardGUI()
    170     {
    171         setTitle("ProGuard");
    172         setDefaultCloseOperation(EXIT_ON_CLOSE);
    173 
    174         // Create some constraints that can be reused.
    175         GridBagConstraints constraints = new GridBagConstraints();
    176         constraints.anchor = GridBagConstraints.WEST;
    177         constraints.insets = new Insets(0, 4, 0, 4);
    178 
    179         GridBagConstraints constraintsStretch = new GridBagConstraints();
    180         constraintsStretch.fill    = GridBagConstraints.HORIZONTAL;
    181         constraintsStretch.weightx = 1.0;
    182         constraintsStretch.anchor  = GridBagConstraints.WEST;
    183         constraintsStretch.insets  = constraints.insets;
    184 
    185         GridBagConstraints constraintsLast = new GridBagConstraints();
    186         constraintsLast.gridwidth = GridBagConstraints.REMAINDER;
    187         constraintsLast.anchor    = GridBagConstraints.WEST;
    188         constraintsLast.insets    = constraints.insets;
    189 
    190         GridBagConstraints constraintsLastStretch = new GridBagConstraints();
    191         constraintsLastStretch.gridwidth = GridBagConstraints.REMAINDER;
    192         constraintsLastStretch.fill      = GridBagConstraints.HORIZONTAL;
    193         constraintsLastStretch.weightx   = 1.0;
    194         constraintsLastStretch.anchor    = GridBagConstraints.WEST;
    195         constraintsLastStretch.insets    = constraints.insets;
    196 
    197         GridBagConstraints splashPanelConstraints = new GridBagConstraints();
    198         splashPanelConstraints.gridwidth = GridBagConstraints.REMAINDER;
    199         splashPanelConstraints.fill      = GridBagConstraints.BOTH;
    200         splashPanelConstraints.weightx   = 1.0;
    201         splashPanelConstraints.weighty   = 0.02;
    202         splashPanelConstraints.anchor    = GridBagConstraints.NORTHWEST;
    203         //splashPanelConstraints.insets    = constraints.insets;
    204 
    205         GridBagConstraints welcomePaneConstraints = new GridBagConstraints();
    206         welcomePaneConstraints.gridwidth = GridBagConstraints.REMAINDER;
    207         welcomePaneConstraints.fill      = GridBagConstraints.NONE;
    208         welcomePaneConstraints.weightx   = 1.0;
    209         welcomePaneConstraints.weighty   = 0.01;
    210         welcomePaneConstraints.anchor    = GridBagConstraints.CENTER;//NORTHWEST;
    211         welcomePaneConstraints.insets    = new Insets(20, 40, 20, 40);
    212 
    213         GridBagConstraints panelConstraints = new GridBagConstraints();
    214         panelConstraints.gridwidth = GridBagConstraints.REMAINDER;
    215         panelConstraints.fill      = GridBagConstraints.HORIZONTAL;
    216         panelConstraints.weightx   = 1.0;
    217         panelConstraints.anchor    = GridBagConstraints.NORTHWEST;
    218         panelConstraints.insets    = constraints.insets;
    219 
    220         GridBagConstraints stretchPanelConstraints = new GridBagConstraints();
    221         stretchPanelConstraints.gridwidth = GridBagConstraints.REMAINDER;
    222         stretchPanelConstraints.fill      = GridBagConstraints.BOTH;
    223         stretchPanelConstraints.weightx   = 1.0;
    224         stretchPanelConstraints.weighty   = 1.0;
    225         stretchPanelConstraints.anchor    = GridBagConstraints.NORTHWEST;
    226         stretchPanelConstraints.insets    = constraints.insets;
    227 
    228         GridBagConstraints glueConstraints = new GridBagConstraints();
    229         glueConstraints.fill    = GridBagConstraints.BOTH;
    230         glueConstraints.weightx = 0.01;
    231         glueConstraints.weighty = 0.01;
    232         glueConstraints.anchor  = GridBagConstraints.NORTHWEST;
    233         glueConstraints.insets  = constraints.insets;
    234 
    235         GridBagConstraints bottomButtonConstraints = new GridBagConstraints();
    236         bottomButtonConstraints.anchor = GridBagConstraints.SOUTHEAST;
    237         bottomButtonConstraints.insets = new Insets(2, 2, 4, 6);
    238         bottomButtonConstraints.ipadx  = 10;
    239         bottomButtonConstraints.ipady  = 2;
    240 
    241         GridBagConstraints lastBottomButtonConstraints = new GridBagConstraints();
    242         lastBottomButtonConstraints.gridwidth = GridBagConstraints.REMAINDER;
    243         lastBottomButtonConstraints.anchor    = GridBagConstraints.SOUTHEAST;
    244         lastBottomButtonConstraints.insets    = bottomButtonConstraints.insets;
    245         lastBottomButtonConstraints.ipadx     = bottomButtonConstraints.ipadx;
    246         lastBottomButtonConstraints.ipady     = bottomButtonConstraints.ipady;
    247 
    248         // Leave room for a growBox on Mac OS X.
    249         if (System.getProperty("os.name").toLowerCase().startsWith("mac os x"))
    250         {
    251             lastBottomButtonConstraints.insets = new Insets(2, 2, 4, 6 + 16);
    252         }
    253 
    254         GridBagLayout layout = new GridBagLayout();
    255 
    256         configurationChooser.addChoosableFileFilter(
    257             new ExtensionFileFilter(msg("proExtension"), new String[] { ".pro" }));
    258 
    259         // Create the opening panel.
    260         Sprite splash =
    261             new CompositeSprite(new Sprite[]
    262         {
    263             new ColorSprite(new ConstantColor(Color.gray),
    264             new FontSprite(new ConstantFont(new Font("sansserif", Font.BOLD, 90)),
    265             new TextSprite(new ConstantString("ProGuard"),
    266                            new ConstantInt(160),
    267                            new LinearInt(-10, 120, new SmoothTiming(500, 1000))))),
    268 
    269             new ColorSprite(new ConstantColor(Color.white),
    270             new FontSprite(new ConstantFont(new Font("sansserif", Font.BOLD, 45)),
    271             new ShadowedSprite(new ConstantInt(3),
    272                                new ConstantInt(3),
    273                                new ConstantDouble(0.4),
    274                                new ConstantInt(1),
    275                                new CompositeSprite(new Sprite[]
    276             {
    277                 new TextSprite(new ConstantString(msg("shrinking")),
    278                                new LinearInt(1000, 60, new SmoothTiming(1000, 2000)),
    279                                new ConstantInt(70)),
    280                 new TextSprite(new ConstantString(msg("optimization")),
    281                                new LinearInt(1000, 400, new SmoothTiming(1500, 2500)),
    282                                new ConstantInt(60)),
    283                 new TextSprite(new ConstantString(msg("obfuscation")),
    284                                new LinearInt(1000, 10, new SmoothTiming(2000, 3000)),
    285                                new ConstantInt(145)),
    286                 new TextSprite(new ConstantString(msg("preverification")),
    287                                new LinearInt(1000, 350, new SmoothTiming(2500, 3500)),
    288                                new ConstantInt(140)),
    289                 new FontSprite(new ConstantFont(new Font("sansserif", Font.BOLD, 30)),
    290                 new TextSprite(new TypeWriterString(msg("developed"), new LinearTiming(3500, 5500)),
    291                                new ConstantInt(250),
    292                                new ConstantInt(200))),
    293             })))),
    294         });
    295         splashPanel = new SplashPanel(splash, 0.5, 5500L);
    296         splashPanel.setPreferredSize(new Dimension(0, 200));
    297 
    298         JEditorPane welcomePane = new JEditorPane("text/html", msg("proGuardInfo"));
    299         welcomePane.setPreferredSize(new Dimension(640, 350));
    300         // The constant HONOR_DISPLAY_PROPERTIES isn't present yet in JDK 1.4.
    301         //welcomePane.putClientProperty(JEditorPane.HONOR_DISPLAY_PROPERTIES, Boolean.TRUE);
    302         welcomePane.putClientProperty("JEditorPane.honorDisplayProperties", Boolean.TRUE);
    303         welcomePane.setOpaque(false);
    304         welcomePane.setEditable(false);
    305         welcomePane.setBorder(new EmptyBorder(20, 20, 20, 20));
    306         addBorder(welcomePane, "welcome");
    307 
    308         JPanel proGuardPanel = new JPanel(layout);
    309         proGuardPanel.add(splashPanel,      splashPanelConstraints);
    310         proGuardPanel.add(welcomePane,  welcomePaneConstraints);
    311 
    312         // Create the input panel.
    313         // TODO: properly clone the ClassPath objects.
    314         // This is awkward to implement in the generic ListPanel.addElements(...)
    315         // method, since the Object.clone() method is not public.
    316         programPanel.addCopyToPanelButton("moveToLibraries", "moveToLibrariesTip", libraryPanel);
    317         libraryPanel.addCopyToPanelButton("moveToProgram",   "moveToProgramTip",   programPanel);
    318 
    319         // Collect all buttons of these panels and make sure they are equally
    320         // sized.
    321         List panelButtons = new ArrayList();
    322         panelButtons.addAll(programPanel.getButtons());
    323         panelButtons.addAll(libraryPanel.getButtons());
    324         setCommonPreferredSize(panelButtons);
    325 
    326         addBorder(programPanel, "programJars" );
    327         addBorder(libraryPanel, "libraryJars" );
    328 
    329         JPanel inputOutputPanel = new JPanel(layout);
    330         inputOutputPanel.add(tip(programPanel, "programJarsTip"), stretchPanelConstraints);
    331         inputOutputPanel.add(tip(libraryPanel, "libraryJarsTip"), stretchPanelConstraints);
    332 
    333         // Load the boiler plate options.
    334         loadBoilerplateConfiguration();
    335 
    336         // Create the boiler plate keep panels.
    337         boilerplateKeepCheckBoxes = new JCheckBox[boilerplateKeep.length];
    338         boilerplateKeepTextFields = new JTextField[boilerplateKeep.length];
    339 
    340         JButton printUsageBrowseButton   = createBrowseButton(printUsageTextField,
    341                                                               msg("selectUsageFile"));
    342 
    343         JPanel shrinkingOptionsPanel = new JPanel(layout);
    344         addBorder(shrinkingOptionsPanel, "options");
    345 
    346         shrinkingOptionsPanel.add(tip(shrinkCheckBox,         "shrinkTip"),       constraintsLastStretch);
    347         shrinkingOptionsPanel.add(tip(printUsageCheckBox,     "printUsageTip"),   constraints);
    348         shrinkingOptionsPanel.add(tip(printUsageTextField,    "outputFileTip"),   constraintsStretch);
    349         shrinkingOptionsPanel.add(tip(printUsageBrowseButton, "selectUsageFile"), constraintsLast);
    350 
    351         JPanel shrinkingPanel = new JPanel(layout);
    352 
    353         shrinkingPanel.add(shrinkingOptionsPanel, panelConstraints);
    354         addClassSpecifications(extractClassSpecifications(boilerplateKeep),
    355                                shrinkingPanel,
    356                                boilerplateKeepCheckBoxes,
    357                                boilerplateKeepTextFields);
    358 
    359         addBorder(additionalKeepPanel, "keepAdditional");
    360         shrinkingPanel.add(tip(additionalKeepPanel, "keepAdditionalTip"), stretchPanelConstraints);
    361 
    362         // Create the boiler plate keep names panels.
    363         boilerplateKeepNamesCheckBoxes = new JCheckBox[boilerplateKeepNames.length];
    364         boilerplateKeepNamesTextFields = new JTextField[boilerplateKeepNames.length];
    365 
    366         JButton printMappingBrowseButton                = createBrowseButton(printMappingTextField,
    367                                                                              msg("selectPrintMappingFile"));
    368         JButton applyMappingBrowseButton                = createBrowseButton(applyMappingTextField,
    369                                                                              msg("selectApplyMappingFile"));
    370         JButton obfucationDictionaryBrowseButton        = createBrowseButton(obfuscationDictionaryTextField,
    371                                                                              msg("selectObfuscationDictionaryFile"));
    372         JButton classObfucationDictionaryBrowseButton   = createBrowseButton(classObfuscationDictionaryTextField,
    373                                                                              msg("selectObfuscationDictionaryFile"));
    374         JButton packageObfucationDictionaryBrowseButton = createBrowseButton(packageObfuscationDictionaryTextField,
    375                                                                              msg("selectObfuscationDictionaryFile"));
    376 
    377         JPanel obfuscationOptionsPanel = new JPanel(layout);
    378         addBorder(obfuscationOptionsPanel, "options");
    379 
    380         obfuscationOptionsPanel.add(tip(obfuscateCheckBox,                       "obfuscateTip"),                    constraintsLastStretch);
    381         obfuscationOptionsPanel.add(tip(printMappingCheckBox,                    "printMappingTip"),                 constraints);
    382         obfuscationOptionsPanel.add(tip(printMappingTextField,                   "outputFileTip"),                   constraintsStretch);
    383         obfuscationOptionsPanel.add(tip(printMappingBrowseButton,                "selectPrintMappingFile"),          constraintsLast);
    384         obfuscationOptionsPanel.add(tip(applyMappingCheckBox,                    "applyMappingTip"),                 constraints);
    385         obfuscationOptionsPanel.add(tip(applyMappingTextField,                   "inputFileTip"),                    constraintsStretch);
    386         obfuscationOptionsPanel.add(tip(applyMappingBrowseButton,                "selectApplyMappingFile"),          constraintsLast);
    387         obfuscationOptionsPanel.add(tip(obfuscationDictionaryCheckBox,           "obfuscationDictionaryTip"),        constraints);
    388         obfuscationOptionsPanel.add(tip(obfuscationDictionaryTextField,          "inputFileTip"),                    constraintsStretch);
    389         obfuscationOptionsPanel.add(tip(obfucationDictionaryBrowseButton,        "selectObfuscationDictionaryFile"), constraintsLast);
    390         obfuscationOptionsPanel.add(tip(classObfuscationDictionaryCheckBox,      "classObfuscationDictionaryTip"),   constraints);
    391         obfuscationOptionsPanel.add(tip(classObfuscationDictionaryTextField,     "inputFileTip"),                    constraintsStretch);
    392         obfuscationOptionsPanel.add(tip(classObfucationDictionaryBrowseButton,   "selectObfuscationDictionaryFile"), constraintsLast);
    393         obfuscationOptionsPanel.add(tip(packageObfuscationDictionaryCheckBox,    "packageObfuscationDictionaryTip"), constraints);
    394         obfuscationOptionsPanel.add(tip(packageObfuscationDictionaryTextField,   "inputFileTip"),                    constraintsStretch);
    395         obfuscationOptionsPanel.add(tip(packageObfucationDictionaryBrowseButton, "selectObfuscationDictionaryFile"), constraintsLast);
    396         obfuscationOptionsPanel.add(tip(overloadAggressivelyCheckBox,            "overloadAggressivelyTip"),         constraintsLastStretch);
    397         obfuscationOptionsPanel.add(tip(useUniqueClassMemberNamesCheckBox,       "useUniqueClassMemberNamesTip"),    constraintsLastStretch);
    398         obfuscationOptionsPanel.add(tip(useMixedCaseClassNamesCheckBox,          "useMixedCaseClassNamesTip"),       constraintsLastStretch);
    399         obfuscationOptionsPanel.add(tip(keepPackageNamesCheckBox,                "keepPackageNamesTip"),             constraints);
    400         obfuscationOptionsPanel.add(tip(keepPackageNamesTextField,               "packageNamesTip"),                 constraintsLastStretch);
    401         obfuscationOptionsPanel.add(tip(flattenPackageHierarchyCheckBox,         "flattenPackageHierarchyTip"),      constraints);
    402         obfuscationOptionsPanel.add(tip(flattenPackageHierarchyTextField,        "packageTip"),                      constraintsLastStretch);
    403         obfuscationOptionsPanel.add(tip(repackageClassesCheckBox,                "repackageClassesTip"),             constraints);
    404         obfuscationOptionsPanel.add(tip(repackageClassesTextField,               "packageTip"),                      constraintsLastStretch);
    405         obfuscationOptionsPanel.add(tip(keepAttributesCheckBox,                  "keepAttributesTip"),               constraints);
    406         obfuscationOptionsPanel.add(tip(keepAttributesTextField,                 "attributesTip"),                   constraintsLastStretch);
    407         obfuscationOptionsPanel.add(tip(keepParameterNamesCheckBox,              "keepParameterNamesTip"),           constraintsLastStretch);
    408         obfuscationOptionsPanel.add(tip(newSourceFileAttributeCheckBox,          "renameSourceFileAttributeTip"),    constraints);
    409         obfuscationOptionsPanel.add(tip(newSourceFileAttributeTextField,         "sourceFileAttributeTip"),          constraintsLastStretch);
    410         obfuscationOptionsPanel.add(tip(adaptClassStringsCheckBox,               "adaptClassStringsTip"),            constraints);
    411         obfuscationOptionsPanel.add(tip(adaptClassStringsTextField,              "classNamesTip"),                   constraintsLastStretch);
    412         obfuscationOptionsPanel.add(tip(adaptResourceFileNamesCheckBox,          "adaptResourceFileNamesTip"),       constraints);
    413         obfuscationOptionsPanel.add(tip(adaptResourceFileNamesTextField,         "fileNameFilterTip"),               constraintsLastStretch);
    414         obfuscationOptionsPanel.add(tip(adaptResourceFileContentsCheckBox,       "adaptResourceFileContentsTip"),    constraints);
    415         obfuscationOptionsPanel.add(tip(adaptResourceFileContentsTextField,      "fileNameFilterTip"),               constraintsLastStretch);
    416 
    417         JPanel obfuscationPanel = new JPanel(layout);
    418 
    419         obfuscationPanel.add(obfuscationOptionsPanel, panelConstraints);
    420         addClassSpecifications(extractClassSpecifications(boilerplateKeepNames),
    421                                obfuscationPanel,
    422                                boilerplateKeepNamesCheckBoxes,
    423                                boilerplateKeepNamesTextFields);
    424 
    425         addBorder(additionalKeepNamesPanel, "keepNamesAdditional");
    426         obfuscationPanel.add(tip(additionalKeepNamesPanel, "keepNamesAdditionalTip"), stretchPanelConstraints);
    427 
    428         // Create the boiler plate "no side effect methods" panels.
    429         boilerplateNoSideEffectMethodCheckBoxes = new JCheckBox[boilerplateNoSideEffectMethods.length];
    430 
    431         JPanel optimizationOptionsPanel = new JPanel(layout);
    432         addBorder(optimizationOptionsPanel, "options");
    433 
    434         JButton optimizationsButton =
    435             createOptimizationsButton(optimizationsTextField);
    436 
    437         optimizationOptionsPanel.add(tip(optimizeCheckBox,                    "optimizeTip"),                    constraintsLastStretch);
    438         optimizationOptionsPanel.add(tip(allowAccessModificationCheckBox,     "allowAccessModificationTip"),     constraintsLastStretch);
    439         optimizationOptionsPanel.add(tip(mergeInterfacesAggressivelyCheckBox, "mergeInterfacesAggressivelyTip"), constraintsLastStretch);
    440         optimizationOptionsPanel.add(tip(optimizationsLabel,                  "optimizationsTip"),               constraints);
    441         optimizationOptionsPanel.add(tip(optimizationsTextField,              "optimizationsFilterTip"),         constraintsStretch);
    442         optimizationOptionsPanel.add(tip(optimizationsButton,                 "optimizationsSelectTip"),         constraintsLast);
    443         optimizationOptionsPanel.add(tip(optimizationPassesLabel,             "optimizationPassesTip"),          constraints);
    444         optimizationOptionsPanel.add(tip(optimizationPassesSpinner,           "optimizationPassesTip"),          constraintsLast);
    445 
    446         JPanel optimizationPanel = new JPanel(layout);
    447 
    448         optimizationPanel.add(optimizationOptionsPanel, panelConstraints);
    449         addClassSpecifications(boilerplateNoSideEffectMethods,
    450                                optimizationPanel,
    451                                boilerplateNoSideEffectMethodCheckBoxes,
    452                                null);
    453 
    454         addBorder(additionalNoSideEffectsPanel, "assumeNoSideEffectsAdditional");
    455         optimizationPanel.add(tip(additionalNoSideEffectsPanel, "assumeNoSideEffectsAdditionalTip"), stretchPanelConstraints);
    456 
    457         // Create the options panel.
    458         JPanel preverificationOptionsPanel = new JPanel(layout);
    459         addBorder(preverificationOptionsPanel, "preverificationAndTargeting");
    460 
    461         preverificationOptionsPanel.add(tip(preverifyCheckBox,    "preverifyTip"),    constraintsLastStretch);
    462         preverificationOptionsPanel.add(tip(microEditionCheckBox, "microEditionTip"), constraintsLastStretch);
    463         preverificationOptionsPanel.add(tip(targetCheckBox,       "targetTip"),       constraints);
    464         preverificationOptionsPanel.add(tip(targetComboBox,       "targetTip"),       constraintsLast);
    465 
    466         JButton printSeedsBrowseButton =
    467             createBrowseButton(printSeedsTextField, msg("selectSeedsFile"));
    468 
    469         JButton printConfigurationBrowseButton =
    470             createBrowseButton(printConfigurationTextField, msg( "selectConfigurationFile"));
    471 
    472         JButton dumpBrowseButton =
    473             createBrowseButton(dumpTextField, msg("selectDumpFile"));
    474 
    475         // Select the most recent target by default.
    476         targetComboBox.setSelectedIndex(targetComboBox.getItemCount() - 1);
    477 
    478         JPanel consistencyPanel = new JPanel(layout);
    479         addBorder(consistencyPanel, "consistencyAndCorrectness");
    480 
    481         consistencyPanel.add(tip(verboseCheckBox,                          "verboseTip"),                          constraintsLastStretch);
    482         consistencyPanel.add(tip(noteCheckBox,                             "noteTip"),                             constraints);
    483         consistencyPanel.add(tip(noteTextField,                            "noteFilterTip"),                             constraintsLastStretch);
    484         consistencyPanel.add(tip(warnCheckBox,                             "warnTip"),                             constraints);
    485         consistencyPanel.add(tip(warnTextField,                            "warnFilterTip"),                             constraintsLastStretch);
    486         consistencyPanel.add(tip(ignoreWarningsCheckBox,                   "ignoreWarningsTip"),                   constraintsLastStretch);
    487         consistencyPanel.add(tip(skipNonPublicLibraryClassesCheckBox,      "skipNonPublicLibraryClassesTip"),      constraintsLastStretch);
    488         consistencyPanel.add(tip(skipNonPublicLibraryClassMembersCheckBox, "skipNonPublicLibraryClassMembersTip"), constraintsLastStretch);
    489         consistencyPanel.add(tip(keepDirectoriesCheckBox,                  "keepDirectoriesTip"),                  constraints);
    490         consistencyPanel.add(tip(keepDirectoriesTextField,                 "directoriesTip"),                      constraintsLastStretch);
    491         consistencyPanel.add(tip(forceProcessingCheckBox,                  "forceProcessingTip"),                  constraintsLastStretch);
    492         consistencyPanel.add(tip(printSeedsCheckBox,                       "printSeedsTip"),                       constraints);
    493         consistencyPanel.add(tip(printSeedsTextField,                      "outputFileTip"),                       constraintsStretch);
    494         consistencyPanel.add(tip(printSeedsBrowseButton,                   "selectSeedsFile"),                     constraintsLast);
    495         consistencyPanel.add(tip(printConfigurationCheckBox,               "printConfigurationTip"),               constraints);
    496         consistencyPanel.add(tip(printConfigurationTextField,              "outputFileTip"),                       constraintsStretch);
    497         consistencyPanel.add(tip(printConfigurationBrowseButton,           "selectConfigurationFile"),             constraintsLast);
    498         consistencyPanel.add(tip(dumpCheckBox,                             "dumpTip"),                             constraints);
    499         consistencyPanel.add(tip(dumpTextField,                            "outputFileTip"),                       constraintsStretch);
    500         consistencyPanel.add(tip(dumpBrowseButton,                         "selectDumpFile"),                      constraintsLast);
    501 
    502         // Collect all components that are followed by text fields and make
    503         // sure they are equally sized. That way the text fields start at the
    504         // same horizontal position.
    505         setCommonPreferredSize(Arrays.asList(new JComponent[] {
    506             printMappingCheckBox,
    507             applyMappingCheckBox,
    508             flattenPackageHierarchyCheckBox,
    509             repackageClassesCheckBox,
    510             newSourceFileAttributeCheckBox,
    511         }));
    512 
    513         JPanel optionsPanel = new JPanel(layout);
    514 
    515         optionsPanel.add(preverificationOptionsPanel, panelConstraints);
    516         optionsPanel.add(consistencyPanel,            panelConstraints);
    517 
    518         addBorder(whyAreYouKeepingPanel, "whyAreYouKeeping");
    519         optionsPanel.add(tip(whyAreYouKeepingPanel, "whyAreYouKeepingTip"), stretchPanelConstraints);
    520 
    521         // Create the process panel.
    522         consoleTextArea.setOpaque(false);
    523         consoleTextArea.setEditable(false);
    524         consoleTextArea.setLineWrap(false);
    525         consoleTextArea.setWrapStyleWord(false);
    526         JScrollPane consoleScrollPane = new JScrollPane(consoleTextArea);
    527         consoleScrollPane.setBorder(new EmptyBorder(1, 1, 1, 1));
    528         addBorder(consoleScrollPane, "processingConsole");
    529 
    530         JPanel processPanel = new JPanel(layout);
    531         processPanel.add(consoleScrollPane, stretchPanelConstraints);
    532 
    533         // Create the load, save, and process buttons.
    534         JButton loadButton = new JButton(msg("loadConfiguration"));
    535         loadButton.addActionListener(new MyLoadConfigurationActionListener());
    536 
    537         JButton viewButton = new JButton(msg("viewConfiguration"));
    538         viewButton.addActionListener(new MyViewConfigurationActionListener());
    539 
    540         JButton saveButton = new JButton(msg("saveConfiguration"));
    541         saveButton.addActionListener(new MySaveConfigurationActionListener());
    542 
    543         JButton processButton = new JButton(msg("process"));
    544         processButton.addActionListener(new MyProcessActionListener());
    545 
    546         // Create the ReTrace panel.
    547         JPanel reTraceSettingsPanel = new JPanel(layout);
    548         addBorder(reTraceSettingsPanel, "reTraceSettings");
    549 
    550         JButton reTraceMappingBrowseButton = createBrowseButton(reTraceMappingTextField,
    551                                                                 msg("selectApplyMappingFile"));
    552 
    553         JLabel reTraceMappingLabel = new JLabel(msg("mappingFile"));
    554         reTraceMappingLabel.setForeground(reTraceVerboseCheckBox.getForeground());
    555 
    556         reTraceSettingsPanel.add(tip(reTraceVerboseCheckBox,     "verboseTip"),             constraintsLastStretch);
    557         reTraceSettingsPanel.add(tip(reTraceMappingLabel,        "mappingFileTip"),         constraints);
    558         reTraceSettingsPanel.add(tip(reTraceMappingTextField,    "inputFileTip"),           constraintsStretch);
    559         reTraceSettingsPanel.add(tip(reTraceMappingBrowseButton, "selectApplyMappingFile"), constraintsLast);
    560 
    561         stackTraceTextArea.setOpaque(true);
    562         stackTraceTextArea.setEditable(true);
    563         stackTraceTextArea.setLineWrap(false);
    564         stackTraceTextArea.setWrapStyleWord(true);
    565         JScrollPane stackTraceScrollPane = new JScrollPane(stackTraceTextArea);
    566         addBorder(stackTraceScrollPane, "obfuscatedStackTrace");
    567 
    568         reTraceTextArea.setOpaque(false);
    569         reTraceTextArea.setEditable(false);
    570         reTraceTextArea.setLineWrap(true);
    571         reTraceTextArea.setWrapStyleWord(true);
    572         JScrollPane reTraceScrollPane = new JScrollPane(reTraceTextArea);
    573         reTraceScrollPane.setBorder(new EmptyBorder(1, 1, 1, 1));
    574         addBorder(reTraceScrollPane, "deobfuscatedStackTrace");
    575 
    576         JPanel reTracePanel = new JPanel(layout);
    577         reTracePanel.add(reTraceSettingsPanel,                                 panelConstraints);
    578         reTracePanel.add(tip(stackTraceScrollPane, "obfuscatedStackTraceTip"), panelConstraints);
    579         reTracePanel.add(reTraceScrollPane,                                    stretchPanelConstraints);
    580 
    581         // Create the load button.
    582         JButton loadStackTraceButton = new JButton(msg("loadStackTrace"));
    583         loadStackTraceButton.addActionListener(new MyLoadStackTraceActionListener());
    584 
    585         JButton reTraceButton = new JButton(msg("reTrace"));
    586         reTraceButton.addActionListener(new MyReTraceActionListener());
    587 
    588         // Create the main tabbed pane.
    589         TabbedPane tabs = new TabbedPane();
    590         tabs.add(msg("proGuardTab"),     proGuardPanel);
    591         tabs.add(msg("inputOutputTab"),  inputOutputPanel);
    592         tabs.add(msg("shrinkingTab"),    shrinkingPanel);
    593         tabs.add(msg("obfuscationTab"),  obfuscationPanel);
    594         tabs.add(msg("optimizationTab"), optimizationPanel);
    595         tabs.add(msg("informationTab"),  optionsPanel);
    596         tabs.add(msg("processTab"),      processPanel);
    597         tabs.add(msg("reTraceTab"),      reTracePanel);
    598         tabs.addImage(Toolkit.getDefaultToolkit().getImage(
    599             this.getClass().getResource(TITLE_IMAGE_FILE)));
    600 
    601         // Add the bottom buttons to each panel.
    602         proGuardPanel     .add(Box.createGlue(),                                      glueConstraints);
    603         proGuardPanel     .add(tip(loadButton,             "loadConfigurationTip"),   bottomButtonConstraints);
    604         proGuardPanel     .add(createNextButton(tabs),                                lastBottomButtonConstraints);
    605 
    606         inputOutputPanel  .add(Box.createGlue(),           glueConstraints);
    607         inputOutputPanel  .add(createPreviousButton(tabs), bottomButtonConstraints);
    608         inputOutputPanel  .add(createNextButton(tabs),     lastBottomButtonConstraints);
    609 
    610         shrinkingPanel    .add(Box.createGlue(),           glueConstraints);
    611         shrinkingPanel    .add(createPreviousButton(tabs), bottomButtonConstraints);
    612         shrinkingPanel    .add(createNextButton(tabs),     lastBottomButtonConstraints);
    613 
    614         obfuscationPanel  .add(Box.createGlue(),           glueConstraints);
    615         obfuscationPanel  .add(createPreviousButton(tabs), bottomButtonConstraints);
    616         obfuscationPanel  .add(createNextButton(tabs),     lastBottomButtonConstraints);
    617 
    618         optimizationPanel .add(Box.createGlue(),           glueConstraints);
    619         optimizationPanel .add(createPreviousButton(tabs), bottomButtonConstraints);
    620         optimizationPanel .add(createNextButton(tabs),     lastBottomButtonConstraints);
    621 
    622         optionsPanel      .add(Box.createGlue(),           glueConstraints);
    623         optionsPanel      .add(createPreviousButton(tabs), bottomButtonConstraints);
    624         optionsPanel      .add(createNextButton(tabs),     lastBottomButtonConstraints);
    625 
    626         processPanel      .add(Box.createGlue(),                                      glueConstraints);
    627         processPanel      .add(createPreviousButton(tabs),                            bottomButtonConstraints);
    628         processPanel      .add(tip(viewButton,             "viewConfigurationTip"),   bottomButtonConstraints);
    629         processPanel      .add(tip(saveButton,             "saveConfigurationTip"),   bottomButtonConstraints);
    630         processPanel      .add(tip(processButton,          "processTip"),             lastBottomButtonConstraints);
    631 
    632         reTracePanel      .add(Box.createGlue(),                                      glueConstraints);
    633         reTracePanel      .add(tip(loadStackTraceButton,   "loadStackTraceTip"),      bottomButtonConstraints);
    634         reTracePanel      .add(tip(reTraceButton,          "reTraceTip"),             lastBottomButtonConstraints);
    635 
    636         // Add the main tabs to the frame.
    637         getContentPane().add(tabs);
    638 
    639         // Pack the entire GUI before setting some default values.
    640         pack();
    641 
    642         // Initialize the GUI settings to reasonable defaults.
    643         loadConfiguration(this.getClass().getResource(DEFAULT_CONFIGURATION));
    644     }
    645 
    646 
    647     public void startSplash()
    648     {
    649         splashPanel.start();
    650     }
    651 
    652 
    653     public void skipSplash()
    654     {
    655         splashPanel.stop();
    656     }
    657 
    658 
    659     /**
    660      * Loads the boilerplate keep class options from the boilerplate file
    661      * into the boilerplate array.
    662      */
    663     private void loadBoilerplateConfiguration()
    664     {
    665         try
    666         {
    667             // Parse the boilerplate configuration file.
    668             ConfigurationParser parser = new ConfigurationParser(
    669                 this.getClass().getResource(BOILERPLATE_CONFIGURATION),
    670                 System.getProperties());
    671 
    672             Configuration configuration = new Configuration();
    673 
    674             try
    675             {
    676                 parser.parse(configuration);
    677 
    678                 // We're interested in the keep options.
    679                 boilerplateKeep =
    680                     extractKeepSpecifications(configuration.keep, false, false);
    681 
    682                 // We're interested in the keep options.
    683                 boilerplateKeepNames =
    684                     extractKeepSpecifications(configuration.keep, true, false);
    685 
    686                 // We're interested in the side effects options.
    687                 boilerplateNoSideEffectMethods = new ClassSpecification[configuration.assumeNoSideEffects.size()];
    688                 configuration.assumeNoSideEffects.toArray(boilerplateNoSideEffectMethods);
    689             }
    690             finally
    691             {
    692                 parser.close();
    693             }
    694         }
    695         catch (Exception ex)
    696         {
    697             ex.printStackTrace();
    698         }
    699     }
    700 
    701 
    702     /**
    703      * Returns an array containing the ClassSpecifications instances with
    704      * matching flags.
    705      */
    706     private KeepClassSpecification[] extractKeepSpecifications(List    keepSpecifications,
    707                                                                boolean allowShrinking,
    708                                                                boolean allowObfuscation)
    709     {
    710         List matches = new ArrayList();
    711 
    712         for (int index = 0; index < keepSpecifications.size(); index++)
    713         {
    714             KeepClassSpecification keepClassSpecification = (KeepClassSpecification)keepSpecifications.get(index);
    715             if (keepClassSpecification.allowShrinking   == allowShrinking &&
    716                 keepClassSpecification.allowObfuscation == allowObfuscation)
    717             {
    718                  matches.add(keepClassSpecification);
    719             }
    720         }
    721 
    722         KeepClassSpecification[] matchingKeepClassSpecifications = new KeepClassSpecification[matches.size()];
    723         matches.toArray(matchingKeepClassSpecifications);
    724 
    725         return matchingKeepClassSpecifications;
    726     }
    727 
    728 
    729     /**
    730      * Returns an array containing the ClassSpecification instances of the
    731      * given array of KeepClassSpecification instances.
    732      */
    733     private ClassSpecification[] extractClassSpecifications(KeepClassSpecification[] keepClassSpecifications)
    734     {
    735         ClassSpecification[] classSpecifications = new ClassSpecification[keepClassSpecifications.length];
    736 
    737         for (int index = 0; index < classSpecifications.length; index++)
    738         {
    739             classSpecifications[index] = keepClassSpecifications[index];
    740         }
    741 
    742         return classSpecifications;
    743     }
    744 
    745 
    746     /**
    747      * Creates a panel with the given boiler plate class specifications.
    748      */
    749     private void addClassSpecifications(ClassSpecification[] boilerplateClassSpecifications,
    750                                         JPanel               classSpecificationsPanel,
    751                                         JCheckBox[]          boilerplateCheckBoxes,
    752                                         JTextField[]         boilerplateTextFields)
    753     {
    754         // Create some constraints that can be reused.
    755         GridBagConstraints constraints = new GridBagConstraints();
    756         constraints.anchor = GridBagConstraints.WEST;
    757         constraints.insets = new Insets(0, 4, 0, 4);
    758 
    759         GridBagConstraints constraintsLastStretch = new GridBagConstraints();
    760         constraintsLastStretch.gridwidth = GridBagConstraints.REMAINDER;
    761         constraintsLastStretch.fill      = GridBagConstraints.HORIZONTAL;
    762         constraintsLastStretch.weightx   = 1.0;
    763         constraintsLastStretch.anchor    = GridBagConstraints.WEST;
    764         constraintsLastStretch.insets    = constraints.insets;
    765 
    766         GridBagConstraints panelConstraints = new GridBagConstraints();
    767         panelConstraints.gridwidth = GridBagConstraints.REMAINDER;
    768         panelConstraints.fill      = GridBagConstraints.HORIZONTAL;
    769         panelConstraints.weightx   = 1.0;
    770         panelConstraints.anchor    = GridBagConstraints.NORTHWEST;
    771         panelConstraints.insets    = constraints.insets;
    772 
    773         GridBagLayout layout = new GridBagLayout();
    774 
    775         String lastPanelName = null;
    776         JPanel keepSubpanel  = null;
    777         for (int index = 0; index < boilerplateClassSpecifications.length; index++)
    778         {
    779             // The panel structure is derived from the comments.
    780             String comments    = boilerplateClassSpecifications[index].comments;
    781             int    dashIndex   = comments.indexOf('-');
    782             int    periodIndex = comments.indexOf('.', dashIndex);
    783             String panelName   = comments.substring(0, dashIndex).trim();
    784             String optionName  = comments.substring(dashIndex + 1, periodIndex).replace('_', '.').trim();
    785             String toolTip     = comments.substring(periodIndex + 1);
    786             if (keepSubpanel == null || !panelName.equals(lastPanelName))
    787             {
    788                 // Create a new keep subpanel and add it.
    789                 keepSubpanel = new JPanel(layout);
    790                 keepSubpanel.setBorder(BorderFactory.createTitledBorder(BORDER, panelName));
    791                 classSpecificationsPanel.add(keepSubpanel, panelConstraints);
    792 
    793                 lastPanelName = panelName;
    794             }
    795 
    796             // Add the check box to the subpanel.
    797             JCheckBox boilerplateCheckBox = new JCheckBox(optionName);
    798             boilerplateCheckBox.setToolTipText(toolTip);
    799             boilerplateCheckBoxes[index] = boilerplateCheckBox;
    800             keepSubpanel.add(boilerplateCheckBox,
    801                              boilerplateTextFields != null ?
    802                                  constraints :
    803                                  constraintsLastStretch);
    804 
    805             if (boilerplateTextFields != null)
    806             {
    807                 // Add the text field to the subpanel.
    808                 boilerplateTextFields[index] = new JTextField(40);
    809                 keepSubpanel.add(tip(boilerplateTextFields[index], "classNamesTip"), constraintsLastStretch);
    810             }
    811         }
    812     }
    813 
    814 
    815     /**
    816      * Adds a standard border with the title that corresponds to the given key
    817      * in the GUI resources.
    818      */
    819     private void addBorder(JComponent component, String titleKey)
    820     {
    821         Border oldBorder = component.getBorder();
    822         Border newBorder = BorderFactory.createTitledBorder(BORDER, msg(titleKey));
    823 
    824         component.setBorder(oldBorder == null ?
    825             newBorder :
    826             new CompoundBorder(newBorder, oldBorder));
    827     }
    828 
    829 
    830     /**
    831      * Creates a Previous button for the given tabbed pane.
    832      */
    833     private JButton createPreviousButton(final TabbedPane tabbedPane)
    834     {
    835         JButton browseButton = new JButton(msg("previous"));
    836         browseButton.addActionListener(new ActionListener()
    837         {
    838             public void actionPerformed(ActionEvent e)
    839             {
    840                 tabbedPane.previous();
    841             }
    842         });
    843 
    844         return browseButton;
    845     }
    846 
    847 
    848     /**
    849      * Creates a Next button for the given tabbed pane.
    850      */
    851     private JButton createNextButton(final TabbedPane tabbedPane)
    852     {
    853         JButton browseButton = new JButton(msg("next"));
    854         browseButton.addActionListener(new ActionListener()
    855         {
    856             public void actionPerformed(ActionEvent e)
    857             {
    858                 tabbedPane.next();
    859             }
    860         });
    861 
    862         return browseButton;
    863     }
    864 
    865 
    866     /**
    867      * Creates a browse button that opens a file browser for the given text field.
    868      */
    869     private JButton createBrowseButton(final JTextField textField,
    870                                        final String     title)
    871     {
    872         JButton browseButton = new JButton(msg("browse"));
    873         browseButton.addActionListener(new ActionListener()
    874         {
    875             public void actionPerformed(ActionEvent e)
    876             {
    877                 // Update the file chooser.
    878                 fileChooser.setDialogTitle(title);
    879                 fileChooser.setSelectedFile(new File(textField.getText()));
    880 
    881                 int returnVal = fileChooser.showDialog(ProGuardGUI.this, msg("ok"));
    882                 if (returnVal == JFileChooser.APPROVE_OPTION)
    883                 {
    884                     // Update the text field.
    885                     textField.setText(fileChooser.getSelectedFile().getPath());
    886                 }
    887             }
    888         });
    889 
    890         return browseButton;
    891     }
    892 
    893 
    894     protected JButton createOptimizationsButton(final JTextField textField)
    895     {
    896         final OptimizationsDialog optimizationsDialog = new OptimizationsDialog(ProGuardGUI.this);
    897 
    898         JButton optimizationsButton = new JButton(msg("select"));
    899         optimizationsButton.addActionListener(new ActionListener()
    900         {
    901             public void actionPerformed(ActionEvent e)
    902             {
    903                 // Update the dialog.
    904                 optimizationsDialog.setFilter(textField.getText());
    905 
    906                 int returnValue = optimizationsDialog.showDialog();
    907                 if (returnValue == OptimizationsDialog.APPROVE_OPTION)
    908                 {
    909                     // Update the text field.
    910                     textField.setText(optimizationsDialog.getFilter());
    911                 }
    912             }
    913         });
    914 
    915         return optimizationsButton;
    916     }
    917 
    918 
    919     /**
    920      * Sets the preferred sizes of the given components to the maximum of their
    921      * current preferred sizes.
    922      */
    923     private void setCommonPreferredSize(List components)
    924     {
    925         // Find the maximum preferred size.
    926         Dimension maximumSize = null;
    927         for (int index = 0; index < components.size(); index++)
    928         {
    929             JComponent component = (JComponent)components.get(index);
    930             Dimension  size      = component.getPreferredSize();
    931             if (maximumSize == null ||
    932                 size.getWidth() > maximumSize.getWidth())
    933             {
    934                 maximumSize = size;
    935             }
    936         }
    937 
    938         // Set the size that we found as the preferred size for all components.
    939         for (int index = 0; index < components.size(); index++)
    940         {
    941             JComponent component = (JComponent)components.get(index);
    942             component.setPreferredSize(maximumSize);
    943         }
    944     }
    945 
    946 
    947     /**
    948      * Updates to GUI settings to reflect the given ProGuard configuration.
    949      */
    950     private void setProGuardConfiguration(Configuration configuration)
    951     {
    952         // Set up the input and output jars and directories.
    953         programPanel.setClassPath(configuration.programJars);
    954         libraryPanel.setClassPath(configuration.libraryJars);
    955 
    956         // Set up the boilerplate keep options.
    957         for (int index = 0; index < boilerplateKeep.length; index++)
    958         {
    959             String classNames =
    960                 findMatchingKeepSpecifications(boilerplateKeep[index],
    961                                                configuration.keep);
    962 
    963             boilerplateKeepCheckBoxes[index].setSelected(classNames != null);
    964             boilerplateKeepTextFields[index].setText(classNames == null ? "*" : classNames);
    965         }
    966 
    967 
    968         // Set up the boilerplate keep names options.
    969         for (int index = 0; index < boilerplateKeepNames.length; index++)
    970         {
    971             String classNames =
    972                 findMatchingKeepSpecifications(boilerplateKeepNames[index],
    973                                                configuration.keep);
    974 
    975             boilerplateKeepNamesCheckBoxes[index].setSelected(classNames != null);
    976             boilerplateKeepNamesTextFields[index].setText(classNames == null ? "*" : classNames);
    977         }
    978 
    979         // Set up the additional keep options. Note that the matched boilerplate
    980         // options have been removed from the list.
    981         additionalKeepPanel.setClassSpecifications(filteredKeepSpecifications(configuration.keep,
    982                                                                               false));
    983 
    984         // Set up the additional keep options. Note that the matched boilerplate
    985         // options have been removed from the list.
    986         additionalKeepNamesPanel.setClassSpecifications(filteredKeepSpecifications(configuration.keep,
    987                                                                                    true));
    988 
    989 
    990         // Set up the boilerplate "no side effect methods" options.
    991         for (int index = 0; index < boilerplateNoSideEffectMethods.length; index++)
    992         {
    993             boolean found =
    994                 findClassSpecification(boilerplateNoSideEffectMethods[index],
    995                                        configuration.assumeNoSideEffects);
    996 
    997             boilerplateNoSideEffectMethodCheckBoxes[index].setSelected(found);
    998         }
    999 
   1000         // Set up the additional keep options. Note that the matched boilerplate
   1001         // options have been removed from the list.
   1002         additionalNoSideEffectsPanel.setClassSpecifications(configuration.assumeNoSideEffects);
   1003 
   1004         // Set up the "why are you keeping" options.
   1005         whyAreYouKeepingPanel.setClassSpecifications(configuration.whyAreYouKeeping);
   1006 
   1007         // Set up the other options.
   1008         shrinkCheckBox                          .setSelected(configuration.shrink);
   1009         printUsageCheckBox                      .setSelected(configuration.printUsage != null);
   1010 
   1011         optimizeCheckBox                        .setSelected(configuration.optimize);
   1012         allowAccessModificationCheckBox         .setSelected(configuration.allowAccessModification);
   1013         mergeInterfacesAggressivelyCheckBox     .setSelected(configuration.mergeInterfacesAggressively);
   1014         optimizationPassesSpinner.getModel()    .setValue(new Integer(configuration.optimizationPasses));
   1015 
   1016         obfuscateCheckBox                       .setSelected(configuration.obfuscate);
   1017         printMappingCheckBox                    .setSelected(configuration.printMapping                 != null);
   1018         applyMappingCheckBox                    .setSelected(configuration.applyMapping                 != null);
   1019         obfuscationDictionaryCheckBox           .setSelected(configuration.obfuscationDictionary        != null);
   1020         classObfuscationDictionaryCheckBox      .setSelected(configuration.classObfuscationDictionary   != null);
   1021         packageObfuscationDictionaryCheckBox    .setSelected(configuration.packageObfuscationDictionary != null);
   1022         overloadAggressivelyCheckBox            .setSelected(configuration.overloadAggressively);
   1023         useUniqueClassMemberNamesCheckBox       .setSelected(configuration.useUniqueClassMemberNames);
   1024         useMixedCaseClassNamesCheckBox          .setSelected(configuration.useMixedCaseClassNames);
   1025         keepPackageNamesCheckBox                .setSelected(configuration.keepPackageNames             != null);
   1026         flattenPackageHierarchyCheckBox         .setSelected(configuration.flattenPackageHierarchy      != null);
   1027         repackageClassesCheckBox                .setSelected(configuration.repackageClasses             != null);
   1028         keepAttributesCheckBox                  .setSelected(configuration.keepAttributes               != null);
   1029         keepParameterNamesCheckBox              .setSelected(configuration.keepParameterNames);
   1030         newSourceFileAttributeCheckBox          .setSelected(configuration.newSourceFileAttribute       != null);
   1031         adaptClassStringsCheckBox               .setSelected(configuration.adaptClassStrings            != null);
   1032         adaptResourceFileNamesCheckBox          .setSelected(configuration.adaptResourceFileNames       != null);
   1033         adaptResourceFileContentsCheckBox       .setSelected(configuration.adaptResourceFileContents    != null);
   1034 
   1035         preverifyCheckBox                       .setSelected(configuration.preverify);
   1036         microEditionCheckBox                    .setSelected(configuration.microEdition);
   1037         targetCheckBox                          .setSelected(configuration.targetClassVersion != 0);
   1038 
   1039         verboseCheckBox                         .setSelected(configuration.verbose);
   1040         noteCheckBox                            .setSelected(configuration.note == null || !configuration.note.isEmpty());
   1041         warnCheckBox                            .setSelected(configuration.warn == null || !configuration.warn.isEmpty());
   1042         ignoreWarningsCheckBox                  .setSelected(configuration.ignoreWarnings);
   1043         skipNonPublicLibraryClassesCheckBox     .setSelected(configuration.skipNonPublicLibraryClasses);
   1044         skipNonPublicLibraryClassMembersCheckBox.setSelected(configuration.skipNonPublicLibraryClassMembers);
   1045         keepDirectoriesCheckBox                 .setSelected(configuration.keepDirectories    != null);
   1046         forceProcessingCheckBox                 .setSelected(configuration.lastModified == Long.MAX_VALUE);
   1047         printSeedsCheckBox                      .setSelected(configuration.printSeeds         != null);
   1048         printConfigurationCheckBox              .setSelected(configuration.printConfiguration != null);
   1049         dumpCheckBox                            .setSelected(configuration.dump               != null);
   1050 
   1051         printUsageTextField                     .setText(fileName(configuration.printUsage));
   1052         optimizationsTextField                  .setText(configuration.optimizations             == null ? OPTIMIZATIONS_DEFAULT                : ListUtil.commaSeparatedString(configuration.optimizations, true));
   1053         printMappingTextField                   .setText(fileName(configuration.printMapping));
   1054         applyMappingTextField                   .setText(fileName(configuration.applyMapping));
   1055         obfuscationDictionaryTextField          .setText(fileName(configuration.obfuscationDictionary));
   1056         classObfuscationDictionaryTextField     .setText(fileName(configuration.classObfuscationDictionary));
   1057         packageObfuscationDictionaryTextField   .setText(fileName(configuration.packageObfuscationDictionary));
   1058         keepPackageNamesTextField               .setText(configuration.keepPackageNames          == null ? ""                                   : ClassUtil.externalClassName(ListUtil.commaSeparatedString(configuration.keepPackageNames, true)));
   1059         flattenPackageHierarchyTextField        .setText(configuration.flattenPackageHierarchy);
   1060         repackageClassesTextField               .setText(configuration.repackageClasses);
   1061         keepAttributesTextField                 .setText(configuration.keepAttributes            == null ? KEEP_ATTRIBUTE_DEFAULT               : ListUtil.commaSeparatedString(configuration.keepAttributes, true));
   1062         newSourceFileAttributeTextField         .setText(configuration.newSourceFileAttribute    == null ? SOURCE_FILE_ATTRIBUTE_DEFAULT        : configuration.newSourceFileAttribute);
   1063         adaptClassStringsTextField              .setText(configuration.adaptClassStrings         == null ? ""                                   : ClassUtil.externalClassName(ListUtil.commaSeparatedString(configuration.adaptClassStrings, true)));
   1064         adaptResourceFileNamesTextField         .setText(configuration.adaptResourceFileNames    == null ? ADAPT_RESOURCE_FILE_NAMES_DEFAULT    : ListUtil.commaSeparatedString(configuration.adaptResourceFileNames, true));
   1065         adaptResourceFileContentsTextField      .setText(configuration.adaptResourceFileContents == null ? ADAPT_RESOURCE_FILE_CONTENTS_DEFAULT : ListUtil.commaSeparatedString(configuration.adaptResourceFileContents, true));
   1066         noteTextField                           .setText(ListUtil.commaSeparatedString(configuration.note, true));
   1067         warnTextField                           .setText(ListUtil.commaSeparatedString(configuration.warn, true));
   1068         keepDirectoriesTextField                .setText(ListUtil.commaSeparatedString(configuration.keepDirectories, true));
   1069         printSeedsTextField                     .setText(fileName(configuration.printSeeds));
   1070         printConfigurationTextField             .setText(fileName(configuration.printConfiguration));
   1071         dumpTextField                           .setText(fileName(configuration.dump));
   1072 
   1073         if (configuration.targetClassVersion != 0)
   1074         {
   1075             targetComboBox.setSelectedItem(ClassUtil.externalClassVersion(configuration.targetClassVersion));
   1076         }
   1077         else
   1078         {
   1079             targetComboBox.setSelectedIndex(targetComboBox.getItemCount() - 1);
   1080         }
   1081 
   1082         if (configuration.printMapping != null)
   1083         {
   1084             reTraceMappingTextField.setText(fileName(configuration.printMapping));
   1085         }
   1086     }
   1087 
   1088 
   1089     /**
   1090      * Returns the ProGuard configuration that reflects the current GUI settings.
   1091      */
   1092     private Configuration getProGuardConfiguration()
   1093     {
   1094         Configuration configuration = new Configuration();
   1095 
   1096         // Get the input and output jars and directories.
   1097         configuration.programJars = programPanel.getClassPath();
   1098         configuration.libraryJars = libraryPanel.getClassPath();
   1099 
   1100         List keep = new ArrayList();
   1101 
   1102         // Collect the additional keep options.
   1103         List additionalKeep = additionalKeepPanel.getClassSpecifications();
   1104         if (additionalKeep != null)
   1105         {
   1106             keep.addAll(additionalKeep);
   1107         }
   1108 
   1109         // Collect the additional keep names options.
   1110         List additionalKeepNames = additionalKeepNamesPanel.getClassSpecifications();
   1111         if (additionalKeepNames != null)
   1112         {
   1113             keep.addAll(additionalKeepNames);
   1114         }
   1115 
   1116         // Collect the boilerplate keep options.
   1117         for (int index = 0; index < boilerplateKeep.length; index++)
   1118         {
   1119             if (boilerplateKeepCheckBoxes[index].isSelected())
   1120             {
   1121                 keep.add(classSpecification(boilerplateKeep[index],
   1122                                             boilerplateKeepTextFields[index].getText()));
   1123             }
   1124         }
   1125 
   1126         // Collect the boilerplate keep names options.
   1127         for (int index = 0; index < boilerplateKeepNames.length; index++)
   1128         {
   1129             if (boilerplateKeepNamesCheckBoxes[index].isSelected())
   1130             {
   1131                 keep.add(classSpecification(boilerplateKeepNames[index],
   1132                                             boilerplateKeepNamesTextFields[index].getText()));
   1133             }
   1134         }
   1135 
   1136         // Put the list of keep specifications in the configuration.
   1137         if (keep.size() > 0)
   1138         {
   1139             configuration.keep = keep;
   1140         }
   1141 
   1142 
   1143         // Collect the boilerplate "no side effect methods" options.
   1144         List noSideEffectMethods = new ArrayList();
   1145 
   1146         for (int index = 0; index < boilerplateNoSideEffectMethods.length; index++)
   1147         {
   1148             if (boilerplateNoSideEffectMethodCheckBoxes[index].isSelected())
   1149             {
   1150                 noSideEffectMethods.add(boilerplateNoSideEffectMethods[index]);
   1151             }
   1152         }
   1153 
   1154         // Collect the additional "no side effect methods" options.
   1155         List additionalNoSideEffectOptions = additionalNoSideEffectsPanel.getClassSpecifications();
   1156         if (additionalNoSideEffectOptions != null)
   1157         {
   1158             noSideEffectMethods.addAll(additionalNoSideEffectOptions);
   1159         }
   1160 
   1161         // Put the list of "no side effect methods" options in the configuration.
   1162         if (noSideEffectMethods.size() > 0)
   1163         {
   1164             configuration.assumeNoSideEffects = noSideEffectMethods;
   1165         }
   1166 
   1167 
   1168         // Collect the "why are you keeping" options.
   1169         configuration.whyAreYouKeeping = whyAreYouKeepingPanel.getClassSpecifications();
   1170 
   1171 
   1172         // Get the other options.
   1173         configuration.shrink                           = shrinkCheckBox                          .isSelected();
   1174         configuration.printUsage                       = printUsageCheckBox                      .isSelected() ? new File(printUsageTextField                                         .getText()) : null;
   1175 
   1176         configuration.optimize                         = optimizeCheckBox                        .isSelected();
   1177         configuration.allowAccessModification          = allowAccessModificationCheckBox         .isSelected();
   1178         configuration.mergeInterfacesAggressively      = mergeInterfacesAggressivelyCheckBox     .isSelected();
   1179         configuration.optimizations                    = optimizationsTextField.getText().length() > 1 ?         ListUtil.commaSeparatedList(optimizationsTextField                   .getText()) : null;
   1180         configuration.optimizationPasses               = ((SpinnerNumberModel)optimizationPassesSpinner.getModel()).getNumber().intValue();
   1181 
   1182         configuration.obfuscate                        = obfuscateCheckBox                       .isSelected();
   1183         configuration.printMapping                     = printMappingCheckBox                    .isSelected() ? new File(printMappingTextField                                       .getText()) : null;
   1184         configuration.applyMapping                     = applyMappingCheckBox                    .isSelected() ? new File(applyMappingTextField                                       .getText()) : null;
   1185         configuration.obfuscationDictionary            = obfuscationDictionaryCheckBox           .isSelected() ? new File(obfuscationDictionaryTextField                              .getText()) : null;
   1186         configuration.classObfuscationDictionary       = classObfuscationDictionaryCheckBox      .isSelected() ? new File(classObfuscationDictionaryTextField                         .getText()) : null;
   1187         configuration.packageObfuscationDictionary     = packageObfuscationDictionaryCheckBox    .isSelected() ? new File(packageObfuscationDictionaryTextField                       .getText()) : null;
   1188         configuration.overloadAggressively             = overloadAggressivelyCheckBox            .isSelected();
   1189         configuration.useUniqueClassMemberNames        = useUniqueClassMemberNamesCheckBox       .isSelected();
   1190         configuration.useMixedCaseClassNames           = useMixedCaseClassNamesCheckBox          .isSelected();
   1191         configuration.keepPackageNames                 = keepPackageNamesCheckBox                .isSelected() ? keepPackageNamesTextField.getText().length()  > 0 ? ListUtil.commaSeparatedList(ClassUtil.internalClassName(keepPackageNamesTextField.getText()))  : new ArrayList() : null;
   1192         configuration.flattenPackageHierarchy          = flattenPackageHierarchyCheckBox         .isSelected() ? ClassUtil.internalClassName(flattenPackageHierarchyTextField         .getText()) : null;
   1193         configuration.repackageClasses                 = repackageClassesCheckBox                .isSelected() ? ClassUtil.internalClassName(repackageClassesTextField                .getText()) : null;
   1194         configuration.keepAttributes                   = keepAttributesCheckBox                  .isSelected() ? ListUtil.commaSeparatedList(keepAttributesTextField                  .getText()) : null;
   1195         configuration.keepParameterNames               = keepParameterNamesCheckBox              .isSelected();
   1196         configuration.newSourceFileAttribute           = newSourceFileAttributeCheckBox          .isSelected() ? newSourceFileAttributeTextField                                      .getText()  : null;
   1197         configuration.adaptClassStrings                = adaptClassStringsCheckBox               .isSelected() ? adaptClassStringsTextField.getText().length() > 0 ? ListUtil.commaSeparatedList(ClassUtil.internalClassName(adaptClassStringsTextField.getText())) : new ArrayList() : null;
   1198         configuration.adaptResourceFileNames           = adaptResourceFileNamesCheckBox          .isSelected() ? ListUtil.commaSeparatedList(adaptResourceFileNamesTextField          .getText()) : null;
   1199         configuration.adaptResourceFileContents        = adaptResourceFileContentsCheckBox       .isSelected() ? ListUtil.commaSeparatedList(adaptResourceFileContentsTextField       .getText()) : null;
   1200 
   1201         configuration.preverify                        = preverifyCheckBox                       .isSelected();
   1202         configuration.microEdition                     = microEditionCheckBox                    .isSelected();
   1203         configuration.targetClassVersion               = targetCheckBox                          .isSelected() ? ClassUtil.internalClassVersion(targetComboBox.getSelectedItem().toString()) : 0;
   1204 
   1205         configuration.verbose                          = verboseCheckBox                         .isSelected();
   1206         configuration.note                             = noteCheckBox                            .isSelected() ? noteTextField.getText().length() > 0 ? ListUtil.commaSeparatedList(ClassUtil.internalClassName(noteTextField.getText())) : null : new ArrayList();
   1207         configuration.warn                             = warnCheckBox                            .isSelected() ? warnTextField.getText().length() > 0 ? ListUtil.commaSeparatedList(ClassUtil.internalClassName(warnTextField.getText())) : null : new ArrayList();
   1208         configuration.ignoreWarnings                   = ignoreWarningsCheckBox                  .isSelected();
   1209         configuration.skipNonPublicLibraryClasses      = skipNonPublicLibraryClassesCheckBox     .isSelected();
   1210         configuration.skipNonPublicLibraryClassMembers = skipNonPublicLibraryClassMembersCheckBox.isSelected();
   1211         configuration.keepDirectories                  = keepDirectoriesCheckBox                 .isSelected() ? keepDirectoriesTextField.getText().length() > 0 ? ListUtil.commaSeparatedList(ClassUtil.internalClassName(keepDirectoriesTextField.getText())) : new ArrayList() : null;
   1212         configuration.lastModified                     = forceProcessingCheckBox                 .isSelected() ? Long.MAX_VALUE : System.currentTimeMillis();
   1213         configuration.printSeeds                       = printSeedsCheckBox                      .isSelected() ? new File(printSeedsTextField                                         .getText()) : null;
   1214         configuration.printConfiguration               = printConfigurationCheckBox              .isSelected() ? new File(printConfigurationTextField                                 .getText()) : null;
   1215         configuration.dump                             = dumpCheckBox                            .isSelected() ? new File(dumpTextField                                               .getText()) : null;
   1216 
   1217         return configuration;
   1218     }
   1219 
   1220 
   1221     /**
   1222      * Looks in the given list for a class specification that is identical to
   1223      * the given template. Returns true if it is found, and removes the matching
   1224      * class specification as a side effect.
   1225      */
   1226     private boolean findClassSpecification(ClassSpecification classSpecificationTemplate,
   1227                                            List                classSpecifications)
   1228     {
   1229         if (classSpecifications == null)
   1230         {
   1231             return false;
   1232         }
   1233 
   1234         for (int index = 0; index < classSpecifications.size(); index++)
   1235         {
   1236             if (classSpecificationTemplate.equals(classSpecifications.get(index)))
   1237             {
   1238                 // Remove the matching option as a side effect.
   1239                 classSpecifications.remove(index);
   1240 
   1241                 return true;
   1242             }
   1243         }
   1244 
   1245         return false;
   1246     }
   1247 
   1248 
   1249     /**
   1250      * Returns the subset of the given list of keep specifications, with
   1251      * matching shrinking flag.
   1252      */
   1253     private List filteredKeepSpecifications(List    keepSpecifications,
   1254                                             boolean allowShrinking)
   1255     {
   1256         List filteredKeepSpecifications = new ArrayList();
   1257 
   1258         for (int index = 0; index < keepSpecifications.size(); index++)
   1259         {
   1260             KeepClassSpecification keepClassSpecification =
   1261                 (KeepClassSpecification)keepSpecifications.get(index);
   1262 
   1263             if (keepClassSpecification.allowShrinking == allowShrinking)
   1264             {
   1265                 filteredKeepSpecifications.add(keepClassSpecification);
   1266             }
   1267         }
   1268 
   1269         return filteredKeepSpecifications;
   1270     }
   1271 
   1272 
   1273     /**
   1274      * Looks in the given list for keep specifications that match the given
   1275      * template. Returns a comma-separated string of class names from
   1276      * matching keep specifications, and removes the matching keep
   1277      * specifications as a side effect.
   1278      */
   1279     private String findMatchingKeepSpecifications(KeepClassSpecification keepClassSpecificationTemplate,
   1280                                                   List              keepSpecifications)
   1281     {
   1282         if (keepSpecifications == null)
   1283         {
   1284             return null;
   1285         }
   1286 
   1287         StringBuffer buffer = null;
   1288 
   1289         for (int index = 0; index < keepSpecifications.size(); index++)
   1290         {
   1291             KeepClassSpecification listedKeepClassSpecification =
   1292                 (KeepClassSpecification)keepSpecifications.get(index);
   1293             String className = listedKeepClassSpecification.className;
   1294             keepClassSpecificationTemplate.className = className;
   1295             if (keepClassSpecificationTemplate.equals(listedKeepClassSpecification))
   1296             {
   1297                 if (buffer == null)
   1298                 {
   1299                     buffer = new StringBuffer();
   1300                 }
   1301                 else
   1302                 {
   1303                     buffer.append(',');
   1304                 }
   1305                 buffer.append(className == null ? "*" : ClassUtil.externalClassName(className));
   1306 
   1307                 // Remove the matching option as a side effect.
   1308                 keepSpecifications.remove(index--);
   1309             }
   1310         }
   1311 
   1312         return buffer == null ? null : buffer.toString();
   1313     }
   1314 
   1315 
   1316     /**
   1317      * Returns a class specification or keep specification, based on the given
   1318      * template and the class name to be filled in.
   1319      */
   1320     private ClassSpecification classSpecification(ClassSpecification classSpecificationTemplate,
   1321                                                   String             className)
   1322     {
   1323         // Create a copy of the template.
   1324         ClassSpecification classSpecification =
   1325             (ClassSpecification)classSpecificationTemplate.clone();
   1326 
   1327         // Set the class name in the copy.
   1328         classSpecification.className =
   1329             className.equals("") ||
   1330             className.equals("*") ?
   1331                 null :
   1332                 ClassUtil.internalClassName(className);
   1333 
   1334         // Return the modified copy.
   1335         return classSpecification;
   1336     }
   1337 
   1338 
   1339     // Methods and internal classes related to actions.
   1340 
   1341     /**
   1342      * Loads the given ProGuard configuration into the GUI.
   1343      */
   1344     private void loadConfiguration(File file)
   1345     {
   1346         // Set the default directory and file in the file choosers.
   1347         configurationChooser.setSelectedFile(file.getAbsoluteFile());
   1348         fileChooser.setCurrentDirectory(file.getAbsoluteFile().getParentFile());
   1349 
   1350         try
   1351         {
   1352             // Parse the configuration file.
   1353             ConfigurationParser parser = new ConfigurationParser(file,
   1354                                                                  System.getProperties());
   1355 
   1356             Configuration configuration = new Configuration();
   1357 
   1358             try
   1359             {
   1360                 parser.parse(configuration);
   1361 
   1362                 // Let the GUI reflect the configuration.
   1363                 setProGuardConfiguration(configuration);
   1364             }
   1365             catch (ParseException ex)
   1366             {
   1367                 JOptionPane.showMessageDialog(getContentPane(),
   1368                                               msg("cantParseConfigurationFile", file.getPath()),
   1369                                               msg("warning"),
   1370                                               JOptionPane.ERROR_MESSAGE);
   1371             }
   1372             finally
   1373             {
   1374                 parser.close();
   1375             }
   1376         }
   1377         catch (IOException ex)
   1378         {
   1379             JOptionPane.showMessageDialog(getContentPane(),
   1380                                           msg("cantOpenConfigurationFile", file.getPath()),
   1381                                           msg("warning"),
   1382                                           JOptionPane.ERROR_MESSAGE);
   1383         }
   1384     }
   1385 
   1386 
   1387     /**
   1388      * Loads the given ProGuard configuration into the GUI.
   1389      */
   1390     private void loadConfiguration(URL url)
   1391     {
   1392         try
   1393         {
   1394             // Parse the configuration file.
   1395             ConfigurationParser parser = new ConfigurationParser(url,
   1396                                                                  System.getProperties());
   1397 
   1398             Configuration configuration = new Configuration();
   1399 
   1400             try
   1401             {
   1402                 parser.parse(configuration);
   1403 
   1404                 // Let the GUI reflect the configuration.
   1405                 setProGuardConfiguration(configuration);
   1406             }
   1407             catch (ParseException ex)
   1408             {
   1409                 JOptionPane.showMessageDialog(getContentPane(),
   1410                                               msg("cantParseConfigurationFile", url),
   1411                                               msg("warning"),
   1412                                               JOptionPane.ERROR_MESSAGE);
   1413             }
   1414             finally
   1415             {
   1416                 parser.close();
   1417             }
   1418         }
   1419         catch (IOException ex)
   1420         {
   1421             JOptionPane.showMessageDialog(getContentPane(),
   1422                                           msg("cantOpenConfigurationFile", url),
   1423                                           msg("warning"),
   1424                                           JOptionPane.ERROR_MESSAGE);
   1425         }
   1426     }
   1427 
   1428 
   1429     /**
   1430      * Saves the current ProGuard configuration to the given file.
   1431      */
   1432     private void saveConfiguration(File file)
   1433     {
   1434         try
   1435         {
   1436             // Save the configuration file.
   1437             ConfigurationWriter writer = new ConfigurationWriter(file);
   1438             writer.write(getProGuardConfiguration());
   1439             writer.close();
   1440         }
   1441         catch (Exception ex)
   1442         {
   1443             JOptionPane.showMessageDialog(getContentPane(),
   1444                                           msg("cantSaveConfigurationFile", file.getPath()),
   1445                                           msg("warning"),
   1446                                           JOptionPane.ERROR_MESSAGE);
   1447         }
   1448     }
   1449 
   1450 
   1451     /**
   1452      * Loads the given stack trace into the GUI.
   1453      */
   1454     private void loadStackTrace(File file)
   1455     {
   1456         try
   1457         {
   1458             StringBuffer buffer = new StringBuffer(1024);
   1459 
   1460             Reader reader = new BufferedReader(new FileReader(file));
   1461             try
   1462             {
   1463                 while (true)
   1464                 {
   1465                     int c = reader.read();
   1466                     if (c < 0)
   1467                     {
   1468                         break;
   1469                     }
   1470 
   1471                     buffer.append(c);
   1472                 }
   1473             }
   1474             finally
   1475             {
   1476                 reader.close();
   1477             }
   1478 
   1479             // Put the stack trace in the text area.
   1480             stackTraceTextArea.setText(buffer.toString());
   1481         }
   1482         catch (IOException ex)
   1483         {
   1484             JOptionPane.showMessageDialog(getContentPane(),
   1485                                           msg("cantOpenStackTraceFile", fileName(file)),
   1486                                           msg("warning"),
   1487                                           JOptionPane.ERROR_MESSAGE);
   1488         }
   1489     }
   1490 
   1491 
   1492     /**
   1493      * This ActionListener loads a ProGuard configuration file and initializes
   1494      * the GUI accordingly.
   1495      */
   1496     private class MyLoadConfigurationActionListener implements ActionListener
   1497     {
   1498         public void actionPerformed(ActionEvent e)
   1499         {
   1500             configurationChooser.setDialogTitle(msg("selectConfigurationFile"));
   1501 
   1502             int returnValue = configurationChooser.showOpenDialog(ProGuardGUI.this);
   1503             if (returnValue == JFileChooser.APPROVE_OPTION)
   1504             {
   1505                 loadConfiguration(configurationChooser.getSelectedFile());
   1506             }
   1507         }
   1508     }
   1509 
   1510 
   1511     /**
   1512      * This ActionListener saves a ProGuard configuration file based on the
   1513      * current GUI settings.
   1514      */
   1515     private class MySaveConfigurationActionListener implements ActionListener
   1516     {
   1517         public void actionPerformed(ActionEvent e)
   1518         {
   1519             configurationChooser.setDialogTitle(msg("saveConfigurationFile"));
   1520 
   1521             int returnVal = configurationChooser.showSaveDialog(ProGuardGUI.this);
   1522             if (returnVal == JFileChooser.APPROVE_OPTION)
   1523             {
   1524                 saveConfiguration(configurationChooser.getSelectedFile());
   1525             }
   1526         }
   1527     }
   1528 
   1529 
   1530     /**
   1531      * This ActionListener displays the ProGuard configuration specified by the
   1532      * current GUI settings.
   1533      */
   1534     private class MyViewConfigurationActionListener implements ActionListener
   1535     {
   1536         public void actionPerformed(ActionEvent e)
   1537         {
   1538             // Make sure System.out has not been redirected yet.
   1539             if (!systemOutRedirected)
   1540             {
   1541                 consoleTextArea.setText("");
   1542 
   1543                 TextAreaOutputStream outputStream =
   1544                     new TextAreaOutputStream(consoleTextArea);
   1545 
   1546                 try
   1547                 {
   1548                     // TODO: write out relative path names and path names with system properties.
   1549 
   1550                     // Write the configuration.
   1551                     ConfigurationWriter writer = new ConfigurationWriter(outputStream);
   1552                     try
   1553                     {
   1554                         writer.write(getProGuardConfiguration());
   1555                     }
   1556                     finally
   1557                     {
   1558                         writer.close();
   1559                     }
   1560                 }
   1561                 catch (IOException ex)
   1562                 {
   1563                     // This shouldn't happen.
   1564                 }
   1565 
   1566                 // Scroll to the top of the configuration.
   1567                 consoleTextArea.setCaretPosition(0);
   1568             }
   1569         }
   1570     }
   1571 
   1572 
   1573     /**
   1574      * This ActionListener executes ProGuard based on the current GUI settings.
   1575      */
   1576     private class MyProcessActionListener implements ActionListener
   1577     {
   1578         public void actionPerformed(ActionEvent e)
   1579         {
   1580             // Make sure System.out has not been redirected yet.
   1581             if (!systemOutRedirected)
   1582             {
   1583                 systemOutRedirected = true;
   1584 
   1585                 // Get the informational configuration file name.
   1586                 File configurationFile = configurationChooser.getSelectedFile();
   1587                 String configurationFileName = configurationFile != null ?
   1588                     configurationFile.getName() :
   1589                     msg("sampleConfigurationFileName");
   1590 
   1591                 // Create the ProGuard thread.
   1592                 Thread proGuardThread =
   1593                     new Thread(new ProGuardRunnable(consoleTextArea,
   1594                                                     getProGuardConfiguration(),
   1595                                                     configurationFileName));
   1596 
   1597                 // Run it.
   1598                 proGuardThread.start();
   1599             }
   1600         }
   1601     }
   1602 
   1603 
   1604     /**
   1605      * This ActionListener loads an obfuscated stack trace from a file and puts
   1606      * it in the proper text area.
   1607      */
   1608     private class MyLoadStackTraceActionListener implements ActionListener
   1609     {
   1610         public void actionPerformed(ActionEvent e)
   1611         {
   1612             fileChooser.setDialogTitle(msg("selectStackTraceFile"));
   1613             fileChooser.setSelectedFile(null);
   1614 
   1615             int returnValue = fileChooser.showOpenDialog(ProGuardGUI.this);
   1616             if (returnValue == JFileChooser.APPROVE_OPTION)
   1617             {
   1618 
   1619                 loadStackTrace(fileChooser.getSelectedFile());
   1620             }
   1621         }
   1622     }
   1623 
   1624 
   1625     /**
   1626      * This ActionListener executes ReTrace based on the current GUI settings.
   1627      */
   1628     private class MyReTraceActionListener implements ActionListener
   1629     {
   1630         public void actionPerformed(ActionEvent e)
   1631         {
   1632             // Make sure System.out has not been redirected yet.
   1633             if (!systemOutRedirected)
   1634             {
   1635                 systemOutRedirected = true;
   1636 
   1637                 boolean verbose            = reTraceVerboseCheckBox.isSelected();
   1638                 File    retraceMappingFile = new File(reTraceMappingTextField.getText());
   1639                 String  stackTrace         = stackTraceTextArea.getText();
   1640 
   1641                 // Create the ReTrace runnable.
   1642                 Runnable reTraceRunnable = new ReTraceRunnable(reTraceTextArea,
   1643                                                                verbose,
   1644                                                                retraceMappingFile,
   1645                                                                stackTrace);
   1646 
   1647                 // Run it in this thread, because it won't take long anyway.
   1648                 reTraceRunnable.run();
   1649             }
   1650         }
   1651     }
   1652 
   1653 
   1654     // Small utility methods.
   1655 
   1656     /**
   1657      * Returns the canonical file name for the given file, or the empty string
   1658      * if the file name is empty.
   1659      */
   1660     private String fileName(File file)
   1661     {
   1662         if (file == null)
   1663         {
   1664             return "";
   1665         }
   1666         else
   1667         {
   1668             try
   1669             {
   1670                 return file.getCanonicalPath();
   1671             }
   1672             catch (IOException ex)
   1673             {
   1674                 return file.getPath();
   1675             }
   1676         }
   1677     }
   1678 
   1679 
   1680     /**
   1681      * Attaches the tool tip from the GUI resources that corresponds to the
   1682      * given key, to the given component.
   1683      */
   1684     private static JComponent tip(JComponent component, String messageKey)
   1685     {
   1686         component.setToolTipText(msg(messageKey));
   1687 
   1688         return component;
   1689     }
   1690 
   1691 
   1692     /**
   1693      * Returns the message from the GUI resources that corresponds to the given
   1694      * key.
   1695      */
   1696     private static String msg(String messageKey)
   1697     {
   1698          return GUIResources.getMessage(messageKey);
   1699     }
   1700 
   1701 
   1702     /**
   1703      * Returns the message from the GUI resources that corresponds to the given
   1704      * key and argument.
   1705      */
   1706     private String msg(String messageKey,
   1707                        Object messageArgument)
   1708     {
   1709          return GUIResources.getMessage(messageKey, new Object[] {messageArgument});
   1710     }
   1711 
   1712 
   1713     /**
   1714      * The main method for the ProGuard GUI.
   1715      */
   1716     public static void main(final String[] args)
   1717     {
   1718         try
   1719         {
   1720             SwingUtil.invokeAndWait(new Runnable()
   1721             {
   1722                 public void run()
   1723                 {
   1724                     try
   1725                     {
   1726                         ProGuardGUI gui = new ProGuardGUI();
   1727 
   1728                         Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
   1729                         Dimension guiSize    = gui.getSize();
   1730                         gui.setLocation((screenSize.width - guiSize.width)   / 2,
   1731                                         (screenSize.height - guiSize.height) / 2);
   1732                         gui.show();
   1733 
   1734                         // Start the splash animation, unless specified otherwise.
   1735                         int argIndex = 0;
   1736                         if (argIndex < args.length &&
   1737                             NO_SPLASH_OPTION.startsWith(args[argIndex]))
   1738                         {
   1739                             gui.skipSplash();
   1740                             argIndex++;
   1741                         }
   1742                         else
   1743                         {
   1744                             gui.startSplash();
   1745                         }
   1746 
   1747                         // Load an initial configuration, if specified.
   1748                         if (argIndex < args.length)
   1749                         {
   1750                             gui.loadConfiguration(new File(args[argIndex]));
   1751                             argIndex++;
   1752                         }
   1753 
   1754                         if (argIndex < args.length)
   1755                         {
   1756                             System.out.println(gui.getClass().getName() + ": ignoring extra arguments [" + args[argIndex] + "...]");
   1757                         }
   1758                     }
   1759                     catch (Exception e)
   1760                     {
   1761                         System.out.println("Internal problem starting the ProGuard GUI (" + e.getMessage() + ")");
   1762                     }
   1763                 }
   1764             });
   1765         }
   1766         catch (Exception e)
   1767         {
   1768             System.out.println("Internal problem starting the ProGuard GUI (" + e.getMessage() + ")");
   1769         }
   1770     }
   1771 }
   1772