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