1/*
2 * ProGuard -- shrinking, optimization, obfuscation, and preverification
3 *             of Java bytecode.
4 *
5 * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu)
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21package proguard.gui;
22
23import javax.swing.*;
24
25/**
26 * This class builds filters corresponding to the selections and names of a
27 * given list of check boxes.
28 */
29public 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     */
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     */
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     */
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