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