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.MemberSpecification;
24 import proguard.classfile.*;
25 import proguard.classfile.util.ClassUtil;
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 
33 /**
34  * This <code>JDialog</code> allows the user to enter a String.
35  *
36  * @author Eric Lafortune
37  */
38 final class MemberSpecificationDialog extends JDialog
39 {
40     /**
41      * Return value if the dialog is canceled (with the Cancel button or by
42      * closing the dialog window).
43      */
44     public static final int CANCEL_OPTION = 1;
45 
46     /**
47      * Return value if the dialog is approved (with the Ok button).
48      */
49     public static final int APPROVE_OPTION = 0;
50 
51 
52     private final boolean isField;
53 
54     private final JRadioButton[] publicRadioButtons;
55     private final JRadioButton[] privateRadioButtons;
56     private final JRadioButton[] protectedRadioButtons;
57     private final JRadioButton[] staticRadioButtons;
58     private final JRadioButton[] finalRadioButtons;
59     private final JRadioButton[] syntheticRadioButtons;
60 
61     private JRadioButton[] volatileRadioButtons;
62     private JRadioButton[] transientRadioButtons;
63 
64     private JRadioButton[] synchronizedRadioButtons;
65     private JRadioButton[] nativeRadioButtons;
66     private JRadioButton[] abstractRadioButtons;
67     private JRadioButton[] strictRadioButtons;
68     private JRadioButton[] bridgeRadioButtons;
69     private JRadioButton[] varargsRadioButtons;
70 
71     private final JTextField annotationTypeTextField = new JTextField(20);
72     private final JTextField nameTextField           = new JTextField(20);
73     private final JTextField typeTextField           = new JTextField(20);
74     private final JTextField argumentTypesTextField  = new JTextField(20);
75 
76     private int returnValue;
77 
78 
MemberSpecificationDialog(JDialog owner, boolean isField)79     public MemberSpecificationDialog(JDialog owner, boolean isField)
80     {
81         super(owner, msg(isField ? "specifyFields" : "specifyMethods"), true);
82         setResizable(true);
83 
84         // Create some constraints that can be reused.
85         GridBagConstraints constraints = new GridBagConstraints();
86         constraints.anchor = GridBagConstraints.WEST;
87         constraints.insets = new Insets(1, 2, 1, 2);
88 
89         GridBagConstraints constraintsStretch = new GridBagConstraints();
90         constraintsStretch.fill    = GridBagConstraints.HORIZONTAL;
91         constraintsStretch.weightx = 1.0;
92         constraintsStretch.anchor  = GridBagConstraints.WEST;
93         constraintsStretch.insets  = constraints.insets;
94 
95         GridBagConstraints constraintsLast = new GridBagConstraints();
96         constraintsLast.gridwidth = GridBagConstraints.REMAINDER;
97         constraintsLast.anchor    = GridBagConstraints.WEST;
98         constraintsLast.insets    = constraints.insets;
99 
100         GridBagConstraints constraintsLastStretch = new GridBagConstraints();
101         constraintsLastStretch.gridwidth = GridBagConstraints.REMAINDER;
102         constraintsLastStretch.fill      = GridBagConstraints.HORIZONTAL;
103         constraintsLastStretch.weightx   = 1.0;
104         constraintsLastStretch.anchor    = GridBagConstraints.WEST;
105         constraintsLastStretch.insets    = constraints.insets;
106 
107         GridBagConstraints panelConstraints = new GridBagConstraints();
108         panelConstraints.gridwidth = GridBagConstraints.REMAINDER;
109         panelConstraints.fill      = GridBagConstraints.HORIZONTAL;
110         panelConstraints.weightx   = 1.0;
111         panelConstraints.weighty   = 0.0;
112         panelConstraints.anchor    = GridBagConstraints.NORTHWEST;
113         panelConstraints.insets    = constraints.insets;
114 
115         GridBagConstraints stretchPanelConstraints = new GridBagConstraints();
116         stretchPanelConstraints.gridwidth = GridBagConstraints.REMAINDER;
117         stretchPanelConstraints.fill      = GridBagConstraints.BOTH;
118         stretchPanelConstraints.weightx   = 1.0;
119         stretchPanelConstraints.weighty   = 1.0;
120         stretchPanelConstraints.anchor    = GridBagConstraints.NORTHWEST;
121         stretchPanelConstraints.insets    = constraints.insets;
122 
123         GridBagConstraints labelConstraints = new GridBagConstraints();
124         labelConstraints.anchor = GridBagConstraints.CENTER;
125         labelConstraints.insets = new Insets(2, 10, 2, 10);
126 
127         GridBagConstraints lastLabelConstraints = new GridBagConstraints();
128         lastLabelConstraints.gridwidth = GridBagConstraints.REMAINDER;
129         lastLabelConstraints.anchor    = GridBagConstraints.CENTER;
130         lastLabelConstraints.insets    = labelConstraints.insets;
131 
132         GridBagConstraints advancedButtonConstraints = new GridBagConstraints();
133         advancedButtonConstraints.weightx = 1.0;
134         advancedButtonConstraints.weighty = 1.0;
135         advancedButtonConstraints.anchor  = GridBagConstraints.SOUTHWEST;
136         advancedButtonConstraints.insets  = new Insets(4, 4, 8, 4);
137 
138         GridBagConstraints okButtonConstraints = new GridBagConstraints();
139         okButtonConstraints.weightx = 1.0;
140         okButtonConstraints.weighty = 1.0;
141         okButtonConstraints.anchor  = GridBagConstraints.SOUTHEAST;
142         okButtonConstraints.insets  = advancedButtonConstraints.insets;
143 
144         GridBagConstraints cancelButtonConstraints = new GridBagConstraints();
145         cancelButtonConstraints.gridwidth = GridBagConstraints.REMAINDER;
146         cancelButtonConstraints.weighty   = 1.0;
147         cancelButtonConstraints.anchor    = GridBagConstraints.SOUTHEAST;
148         cancelButtonConstraints.insets    = okButtonConstraints.insets;
149 
150         GridBagLayout layout = new GridBagLayout();
151 
152         Border etchedBorder = BorderFactory.createEtchedBorder(EtchedBorder.RAISED);
153 
154         this.isField = isField;
155 
156         // Create the access panel.
157         JPanel accessPanel = new JPanel(layout);
158         accessPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
159                                                                msg("access")));
160 
161         accessPanel.add(Box.createGlue(),                                labelConstraints);
162         accessPanel.add(tip(new JLabel(msg("required")), "requiredTip"), labelConstraints);
163         accessPanel.add(tip(new JLabel(msg("not")),      "notTip"),      labelConstraints);
164         accessPanel.add(tip(new JLabel(msg("dontCare")), "dontCareTip"), labelConstraints);
165         accessPanel.add(Box.createGlue(),                                constraintsLastStretch);
166 
167         publicRadioButtons    = addRadioButtonTriplet("Public",    accessPanel);
168         privateRadioButtons   = addRadioButtonTriplet("Private",   accessPanel);
169         protectedRadioButtons = addRadioButtonTriplet("Protected", accessPanel);
170         staticRadioButtons    = addRadioButtonTriplet("Static",    accessPanel);
171         finalRadioButtons     = addRadioButtonTriplet("Final",     accessPanel);
172         syntheticRadioButtons = addRadioButtonTriplet("Synthetic", accessPanel);
173 
174         if (isField)
175         {
176             volatileRadioButtons  = addRadioButtonTriplet("Volatile",  accessPanel);
177             transientRadioButtons = addRadioButtonTriplet("Transient", accessPanel);
178         }
179         else
180         {
181             synchronizedRadioButtons = addRadioButtonTriplet("Synchronized", accessPanel);
182             nativeRadioButtons       = addRadioButtonTriplet("Native",       accessPanel);
183             abstractRadioButtons     = addRadioButtonTriplet("Abstract",     accessPanel);
184             strictRadioButtons       = addRadioButtonTriplet("Strict",       accessPanel);
185             bridgeRadioButtons       = addRadioButtonTriplet("Bridge",       accessPanel);
186             varargsRadioButtons      = addRadioButtonTriplet("Varargs",      accessPanel);
187         }
188 
189         // Create the type panel.
190         JPanel typePanel = new JPanel(layout);
191         typePanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
192                                                              msg(isField ? "fieldType" :
193                                                                            "returnType")));
194 
195         typePanel.add(tip(typeTextField, "typeTip"), constraintsLastStretch);
196 
197         // Create the annotation type panel.
198         final JPanel annotationTypePanel = new JPanel(layout);
199         annotationTypePanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
200                                                                        msg("annotation")));
201 
202         annotationTypePanel.add(tip(annotationTypeTextField, "classNameTip"), constraintsLastStretch);
203 
204         // Create the name panel.
205         JPanel namePanel = new JPanel(layout);
206         namePanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
207                                                              msg("name")));
208 
209         namePanel.add(tip(nameTextField, isField ? "fieldNameTip" :
210                                                    "methodNameTip"), constraintsLastStretch);
211 
212         // Create the arguments panel.
213         JPanel argumentsPanel = new JPanel(layout);
214         argumentsPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
215                                                                   msg("argumentTypes")));
216 
217         argumentsPanel.add(tip(argumentTypesTextField, "argumentTypes2Tip"), constraintsLastStretch);
218 
219         // Create the Advanced button.
220         final JButton advancedButton = new JButton(msg("basic"));
221         advancedButton.addActionListener(new ActionListener()
222         {
223             public void actionPerformed(ActionEvent e)
224             {
225                 boolean visible = !annotationTypePanel.isVisible();
226 
227                 annotationTypePanel.setVisible(visible);
228 
229                 advancedButton.setText(msg(visible ? "basic" : "advanced"));
230 
231                 pack();
232             }
233         });
234         advancedButton.doClick();
235 
236         // Create the Ok button.
237         JButton okButton = new JButton(msg("ok"));
238         okButton.addActionListener(new ActionListener()
239         {
240             public void actionPerformed(ActionEvent e)
241             {
242                 returnValue = APPROVE_OPTION;
243                 hide();
244             }
245         });
246 
247         // Create the Cancel button.
248         JButton cancelButton = new JButton(msg("cancel"));
249         cancelButton.addActionListener(new ActionListener()
250         {
251             public void actionPerformed(ActionEvent e)
252             {
253                 hide();
254             }
255         });
256 
257         // Add all panels to the main panel.
258         JPanel mainPanel = new JPanel(layout);
259         mainPanel.add(tip(accessPanel,         "accessTip"),       panelConstraints);
260         mainPanel.add(tip(annotationTypePanel, "annotationTip"),   panelConstraints);
261         mainPanel.add(tip(typePanel, isField ? "fieldTypeTip" :
262                                                "returnTypeTip"),   panelConstraints);
263         mainPanel.add(tip(namePanel,           "nameTip"),         panelConstraints);
264 
265         if (!isField)
266         {
267             mainPanel.add(tip(argumentsPanel, "argumentTypesTip"), panelConstraints);
268         }
269 
270         mainPanel.add(tip(advancedButton, "advancedTip"), advancedButtonConstraints);
271         mainPanel.add(okButton,                           okButtonConstraints);
272         mainPanel.add(cancelButton,                       cancelButtonConstraints);
273 
274         getContentPane().add(new JScrollPane(mainPanel));
275     }
276 
277 
278     /**
279      * Adds a JLabel and three JRadioButton instances in a ButtonGroup to the
280      * given panel with a GridBagLayout, and returns the buttons in an array.
281      */
addRadioButtonTriplet(String labelText, JPanel panel)282     private JRadioButton[] addRadioButtonTriplet(String labelText,
283                                                  JPanel panel)
284     {
285         GridBagConstraints labelConstraints = new GridBagConstraints();
286         labelConstraints.anchor = GridBagConstraints.WEST;
287         labelConstraints.insets = new Insets(2, 10, 2, 10);
288 
289         GridBagConstraints buttonConstraints = new GridBagConstraints();
290         buttonConstraints.insets = labelConstraints.insets;
291 
292         GridBagConstraints lastGlueConstraints = new GridBagConstraints();
293         lastGlueConstraints.gridwidth = GridBagConstraints.REMAINDER;
294         lastGlueConstraints.weightx   = 1.0;
295 
296         // Create the radio buttons.
297         JRadioButton radioButton0 = new JRadioButton();
298         JRadioButton radioButton1 = new JRadioButton();
299         JRadioButton radioButton2 = new JRadioButton();
300 
301         // Put them in a button group.
302         ButtonGroup buttonGroup = new ButtonGroup();
303         buttonGroup.add(radioButton0);
304         buttonGroup.add(radioButton1);
305         buttonGroup.add(radioButton2);
306 
307         // Add the label and the buttons to the panel.
308         panel.add(new JLabel(labelText), labelConstraints);
309         panel.add(radioButton0,          buttonConstraints);
310         panel.add(radioButton1,          buttonConstraints);
311         panel.add(radioButton2,          buttonConstraints);
312         panel.add(Box.createGlue(),      lastGlueConstraints);
313 
314         return new JRadioButton[]
315         {
316              radioButton0,
317              radioButton1,
318              radioButton2
319         };
320     }
321 
322 
323     /**
324      * Sets the MemberSpecification to be represented in this dialog.
325      */
setMemberSpecification(MemberSpecification memberSpecification)326     public void setMemberSpecification(MemberSpecification memberSpecification)
327     {
328         String annotationType = memberSpecification.annotationType;
329         String name           = memberSpecification.name;
330         String descriptor     = memberSpecification.descriptor;
331 
332         // Set the class name text fields.
333         annotationTypeTextField.setText(annotationType == null ? "" : ClassUtil.externalType(annotationType));
334 
335         // Set the access radio buttons.
336         setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_PUBLIC,       publicRadioButtons);
337         setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_PRIVATE,      privateRadioButtons);
338         setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_PROTECTED,    protectedRadioButtons);
339         setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_STATIC,       staticRadioButtons);
340         setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_FINAL,        finalRadioButtons);
341         setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_SYNTHETIC,    syntheticRadioButtons);
342         setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_VOLATILE,     volatileRadioButtons);
343         setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_TRANSIENT,    transientRadioButtons);
344         setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_SYNCHRONIZED, synchronizedRadioButtons);
345         setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_NATIVE,       nativeRadioButtons);
346         setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_ABSTRACT,     abstractRadioButtons);
347         setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_STRICT,       strictRadioButtons);
348         setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_BRIDGE,       bridgeRadioButtons);
349         setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_VARARGS,      varargsRadioButtons);
350 
351         // Set the class name text fields.
352         nameTextField.setText(name == null ? "*" : name);
353 
354         if (isField)
355         {
356             typeTextField         .setText(descriptor == null ? "***" : ClassUtil.externalType(descriptor));
357         }
358         else
359         {
360             typeTextField         .setText(descriptor == null ? "***" : ClassUtil.externalMethodReturnType(descriptor));
361             argumentTypesTextField.setText(descriptor == null ? "..." : ClassUtil.externalMethodArguments(descriptor));
362         }
363     }
364 
365 
366     /**
367      * Returns the MemberSpecification currently represented in this dialog.
368      */
getMemberSpecification()369     public MemberSpecification getMemberSpecification()
370     {
371         String annotationType = annotationTypeTextField.getText();
372         String name           = nameTextField.getText();
373         String type           = typeTextField.getText();
374         String arguments      = argumentTypesTextField.getText();
375 
376         // Convert all class member specifications into the internal format.
377         annotationType =
378             annotationType.equals("") ||
379             annotationType.equals("***") ? null : ClassUtil.internalType(annotationType);
380 
381         if (name.equals("") ||
382             name.equals("*"))
383         {
384             name = null;
385         }
386 
387         if (isField)
388         {
389             type =
390                 type.equals("") ||
391                 type.equals("***") ? null : ClassUtil.internalType(type);
392         }
393         else
394         {
395             if (type.equals(""))
396             {
397                 type = JavaConstants.TYPE_VOID;
398             }
399 
400             type =
401                 type     .equals("***") &&
402                 arguments.equals("...") ? null :
403                     ClassUtil.internalMethodDescriptor(type, ListUtil.commaSeparatedList(arguments));
404         }
405 
406         MemberSpecification memberSpecification =
407             new MemberSpecification(0, 0, annotationType, name, type);
408 
409         // Also get the access radio button settings.
410         getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_PUBLIC,       publicRadioButtons);
411         getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_PRIVATE,      privateRadioButtons);
412         getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_PROTECTED,    protectedRadioButtons);
413         getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_STATIC,       staticRadioButtons);
414         getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_FINAL,        finalRadioButtons);
415         getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_SYNTHETIC,    syntheticRadioButtons);
416         getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_VOLATILE,     volatileRadioButtons);
417         getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_TRANSIENT,    transientRadioButtons);
418         getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_SYNCHRONIZED, synchronizedRadioButtons);
419         getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_NATIVE,       nativeRadioButtons);
420         getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_ABSTRACT,     abstractRadioButtons);
421         getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_STRICT,       strictRadioButtons);
422         getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_BRIDGE,       bridgeRadioButtons);
423         getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.ACC_VARARGS,      varargsRadioButtons);
424 
425         return memberSpecification;
426     }
427 
428 
429     /**
430      * Shows this dialog. This method only returns when the dialog is closed.
431      *
432      * @return <code>CANCEL_OPTION</code> or <code>APPROVE_OPTION</code>,
433      *         depending on the choice of the user.
434      */
showDialog()435     public int showDialog()
436     {
437         returnValue = CANCEL_OPTION;
438 
439         // Open the dialog in the right place, then wait for it to be closed,
440         // one way or another.
441         pack();
442         setLocationRelativeTo(getOwner());
443         show();
444 
445         return returnValue;
446     }
447 
448 
449     /**
450      * Sets the appropriate radio button of a given triplet, based on the access
451      * flags of the given keep option.
452      */
setMemberSpecificationRadioButtons(MemberSpecification memberSpecification, int flag, JRadioButton[] radioButtons)453     private void setMemberSpecificationRadioButtons(MemberSpecification memberSpecification,
454                                                     int                 flag,
455                                                     JRadioButton[]      radioButtons)
456     {
457         if (radioButtons != null)
458         {
459             int index = (memberSpecification.requiredSetAccessFlags   & flag) != 0 ? 0 :
460                         (memberSpecification.requiredUnsetAccessFlags & flag) != 0 ? 1 :
461                                                                                        2;
462             radioButtons[index].setSelected(true);
463         }
464     }
465 
466 
467     /**
468      * Updates the access flag of the given keep option, based on the given radio
469      * button triplet.
470      */
getMemberSpecificationRadioButtons(MemberSpecification memberSpecification, int flag, JRadioButton[] radioButtons)471     private void getMemberSpecificationRadioButtons(MemberSpecification memberSpecification,
472                                                     int                 flag,
473                                                     JRadioButton[]      radioButtons)
474     {
475         if (radioButtons != null)
476         {
477             if      (radioButtons[0].isSelected())
478             {
479                 memberSpecification.requiredSetAccessFlags   |= flag;
480             }
481             else if (radioButtons[1].isSelected())
482             {
483                 memberSpecification.requiredUnsetAccessFlags |= flag;
484             }
485         }
486     }
487 
488 
489     /**
490      * Attaches the tool tip from the GUI resources that corresponds to the
491      * given key, to the given component.
492      */
tip(JComponent component, String messageKey)493     private static JComponent tip(JComponent component, String messageKey)
494     {
495         component.setToolTipText(msg(messageKey));
496 
497         return component;
498     }
499 
500 
501     /**
502      * Returns the message from the GUI resources that corresponds to the given
503      * key.
504      */
msg(String messageKey)505     private static String msg(String messageKey)
506     {
507          return GUIResources.getMessage(messageKey);
508     }
509 }
510