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 javax.swing.*;
24 
25 /**
26  * This class builds filters corresponding to the selections and names of a
27  * given list of check boxes.
28  */
29 public class FilterBuilder
30 {
31     private JCheckBox[] checkBoxes;
32     private char        separator;
33 
34 
35     /**
36      * Creates a new FilterBuilder.
37      * @param checkBoxes the check boxes with names and selections that should
38      *                   be reflected in the output filter.
39      * @param separator  the separator for the names in the check boxes.
40      */
FilterBuilder(JCheckBox[] checkBoxes, char separator)41     public FilterBuilder(JCheckBox[] checkBoxes, char separator)
42     {
43         this.checkBoxes = checkBoxes;
44         this.separator  = separator;
45     }
46 
47 
48     /**
49      * Builds a filter for the current names and selections of the check boxes.
50      */
buildFilter()51     public String buildFilter()
52     {
53         StringBuffer positive = new StringBuffer();
54         StringBuffer negative = new StringBuffer();
55 
56         buildFilter("", positive, negative);
57 
58         return positive.length() <= negative.length() ?
59             positive.toString() :
60             negative.toString();
61     }
62 
63 
64     /**
65      * Builds two versions of the filter for the given prefix.
66      * @param prefix   the prefix.
67      * @param positive the filter to be extended, assuming the matching
68      *                 strings are accepted.
69      * @param negative the filter to be extended, assuming the matching
70      *                 strings are rejected.
71      */
buildFilter(String prefix, StringBuffer positive, StringBuffer negative)72     private void buildFilter(String       prefix,
73                              StringBuffer positive,
74                              StringBuffer negative)
75     {
76         int positiveCount = 0;
77         int negativeCount = 0;
78 
79         // Count all selected and unselected check boxes with the prefix.
80         for (int index = 0; index < checkBoxes.length; index++)
81         {
82             JCheckBox checkBox = checkBoxes[index];
83             String    name     = checkBox.getText();
84 
85             if (name.startsWith(prefix))
86             {
87                 if (checkBox.isSelected())
88                 {
89                     positiveCount++;
90                 }
91                 else
92                 {
93                     negativeCount++;
94                 }
95             }
96         }
97 
98         // Are there only unselected check boxes?
99         if (positiveCount == 0)
100         {
101             // Extend the positive filter with exceptions and return.
102             if (positive.length() > 0)
103             {
104                 positive.append(',');
105             }
106             positive.append('!').append(prefix);
107             if (prefix.length() == 0 ||
108                 prefix.charAt(prefix.length()-1) == separator)
109             {
110                 positive.append('*');
111             }
112 
113             return;
114         }
115 
116         // Are there only selected check boxes?
117         if (negativeCount == 0)
118         {
119             // Extend the negative filter with exceptions and return.
120             if (negative.length() > 0)
121             {
122                 negative.append(',');
123             }
124             negative.append(prefix);
125             if (prefix.length() == 0 ||
126                 prefix.charAt(prefix.length()-1) == separator)
127             {
128                 negative.append('*');
129             }
130 
131             return;
132         }
133 
134         // Create new positive and negative filters for names starting with the
135         // prefix only.
136         StringBuffer positiveFilter = new StringBuffer();
137         StringBuffer negativeFilter = new StringBuffer();
138 
139         String newPrefix = null;
140 
141         for (int index = 0; index < checkBoxes.length; index++)
142         {
143             String name = checkBoxes[index].getText();
144 
145             if (name.startsWith(prefix))
146             {
147                 if (newPrefix == null ||
148                     !name.startsWith(newPrefix))
149                 {
150                     int prefixIndex =
151                         name.indexOf(separator, prefix.length()+1);
152 
153                     newPrefix = prefixIndex >= 0 ?
154                         name.substring(0, prefixIndex+1) :
155                         name;
156 
157                     buildFilter(newPrefix,
158                                 positiveFilter,
159                                 negativeFilter);
160                 }
161             }
162         }
163 
164         // Extend the positive filter.
165         if (positiveFilter.length() <= negativeFilter.length() + prefix.length() + 3)
166         {
167             if (positive.length() > 0 &&
168                 positiveFilter.length() > 0)
169             {
170                 positive.append(',');
171             }
172 
173             positive.append(positiveFilter);
174         }
175         else
176         {
177             if (positive.length() > 0 &&
178                 negativeFilter.length() > 0)
179             {
180                 positive.append(',');
181             }
182 
183             positive.append(negativeFilter).append(",!").append(prefix).append('*');
184         }
185 
186         // Extend the negative filter.
187         if (negativeFilter.length() <= positiveFilter.length() + prefix.length() + 4)
188         {
189             if (negative.length() > 0 &&
190                 negativeFilter.length() > 0)
191             {
192                 negative.append(',');
193             }
194 
195             negative.append(negativeFilter);
196         }
197         else
198         {
199             if (negative.length() > 0 &&
200                 positiveFilter.length() > 0)
201             {
202                 negative.append(',');
203             }
204 
205             negative.append(positiveFilter).append(',').append(prefix).append('*');
206         }
207     }
208 }
209