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