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