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.util;
22 
23 import proguard.classfile.ClassConstants;
24 
25 /**
26  * This StringParser can create StringMatcher instances for regular expressions
27  * matching internal class names (or descriptors containing class names).
28  * The regular expressions can contain the following wildcards:
29  * '%'     for a single internal primitive type character (V, Z, B, C, S, I, F,
30  *         J, or D),
31  * '?'     for a single regular class name character,
32  * '*'     for any number of regular class name characters,
33  * '**'    for any number of regular class name characters or package separator
34  *         characters ('/'),
35  * 'L***;' for a single internal type (class name or primitive type,
36  *         array or non-array), and
37  * 'L///;' for any number of internal types (class names and primitive
38  *         types).
39  *
40  * @author Eric Lafortune
41  */
42 public class ClassNameParser implements StringParser
43 {
44     private static final char[] INTERNAL_PRIMITIVE_TYPES = new char[]
45     {
46         ClassConstants.TYPE_VOID,
47         ClassConstants.TYPE_BOOLEAN,
48         ClassConstants.TYPE_BYTE,
49         ClassConstants.TYPE_CHAR,
50         ClassConstants.TYPE_SHORT,
51         ClassConstants.TYPE_INT,
52         ClassConstants.TYPE_LONG,
53         ClassConstants.TYPE_FLOAT,
54         ClassConstants.TYPE_DOUBLE,
55     };
56 
57 
58     // Implementations for StringParser.
59 
parse(String regularExpression)60     public StringMatcher parse(String regularExpression)
61     {
62         int           index;
63         StringMatcher nextMatcher = new EmptyStringMatcher();
64 
65         // Look for wildcards.
66         for (index = 0; index < regularExpression.length(); index++)
67         {
68             // Is there an 'L///;' wildcard?
69             if (regularExpression.regionMatches(index, "L///;", 0, 5))
70             {
71                 SettableMatcher settableMatcher = new SettableMatcher();
72 
73                 // Create a matcher, recursively, for the remainder of the
74                 // string, optionally preceded by any type.
75                 nextMatcher =
76                     new OrMatcher(parse(regularExpression.substring(index + 5)),
77                                   createAnyTypeMatcher(settableMatcher));
78 
79                 settableMatcher.setMatcher(nextMatcher);
80 
81                 break;
82             }
83 
84             // Is there an 'L***;' wildcard?
85             if (regularExpression.regionMatches(index, "L***;", 0, 5))
86             {
87                 // Create a matcher for the wildcard and, recursively, for the
88                 // remainder of the string.
89                 nextMatcher =
90                     createAnyTypeMatcher(parse(regularExpression.substring(index + 5)));
91                 break;
92             }
93 
94             // Is there a '**' wildcard?
95             if (regularExpression.regionMatches(index, "**", 0, 2))
96             {
97                 // Create a matcher for the wildcard and, recursively, for the
98                 // remainder of the string.
99                 nextMatcher =
100                     new VariableStringMatcher(null,
101                                               new char[] { ClassConstants.TYPE_CLASS_END },
102                                               0,
103                                               Integer.MAX_VALUE,
104                                               parse(regularExpression.substring(index + 2)));
105                 break;
106             }
107 
108             // Is there a '*' wildcard?
109             else if (regularExpression.charAt(index) == '*')
110             {
111                 // Create a matcher for the wildcard and, recursively, for the
112                 // remainder of the string.
113                 nextMatcher =
114                     new VariableStringMatcher(null,
115                                               new char[] { ClassConstants.TYPE_CLASS_END, ClassConstants.PACKAGE_SEPARATOR },
116                                               0,
117                                               Integer.MAX_VALUE,
118                                               parse(regularExpression.substring(index + 1)));
119                 break;
120             }
121 
122             // Is there a '?' wildcard?
123             else if (regularExpression.charAt(index) == '?')
124             {
125                 // Create a matcher for the wildcard and, recursively, for the
126                 // remainder of the string.
127                 nextMatcher =
128                     new VariableStringMatcher(null,
129                                               new char[] { ClassConstants.TYPE_CLASS_END, ClassConstants.PACKAGE_SEPARATOR },
130                                               1,
131                                               1,
132                                               parse(regularExpression.substring(index + 1)));
133                 break;
134             }
135 
136             // Is there a '%' wildcard?
137             else if (regularExpression.charAt(index) == '%')
138             {
139                 // Create a matcher for the wildcard and, recursively, for the
140                 // remainder of the string.
141                 nextMatcher =
142                     new VariableStringMatcher(INTERNAL_PRIMITIVE_TYPES,
143                                               null,
144                                               1,
145                                               1,
146                                               parse(regularExpression.substring(index + 1)));
147                 break;
148             }
149         }
150 
151         // Return a matcher for the fixed first part of the regular expression,
152         // if any, and the remainder.
153         return index != 0 ?
154             (StringMatcher)new FixedStringMatcher(regularExpression.substring(0, index), nextMatcher) :
155             (StringMatcher)nextMatcher;
156     }
157 
158 
159     // Small utility methods.
160 
161     /**
162      * Creates a StringMatcher that matches any type (class or primitive type,
163      * array or non-array) and then the given matcher.
164      */
createAnyTypeMatcher(StringMatcher nextMatcher)165     private VariableStringMatcher createAnyTypeMatcher(StringMatcher nextMatcher)
166     {
167         return new VariableStringMatcher(new char[] { ClassConstants.TYPE_ARRAY },
168                                   null,
169                                   0,
170                                   255,
171         new OrMatcher(
172         new VariableStringMatcher(INTERNAL_PRIMITIVE_TYPES,
173                                   null,
174                                   1,
175                                   1,
176                                   nextMatcher),
177         new VariableStringMatcher(new char[] { ClassConstants.TYPE_CLASS_START },
178                                   null,
179                                   1,
180                                   1,
181         new VariableStringMatcher(null,
182                                   new char[] { ClassConstants.TYPE_CLASS_END },
183                                   0,
184                                   Integer.MAX_VALUE,
185         new VariableStringMatcher(new char[] { ClassConstants.TYPE_CLASS_END },
186                                   null,
187                                   1,
188                                   1,
189                                   nextMatcher)))));
190     }
191 
192 
193     /**
194      * A main method for testing class name matching.
195      */
main(String[] args)196     public static void main(String[] args)
197     {
198         try
199         {
200             System.out.println("Regular expression ["+args[0]+"]");
201             ClassNameParser parser  = new ClassNameParser();
202             StringMatcher  matcher = parser.parse(args[0]);
203             for (int index = 1; index < args.length; index++)
204             {
205                 String string = args[index];
206                 System.out.print("String             ["+string+"]");
207                 System.out.println(" -> match = "+matcher.matches(args[index]));
208             }
209         }
210         catch (Exception ex)
211         {
212             ex.printStackTrace();
213         }
214     }
215 }
216