1 /* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2014 Eric Lafortune (eric (at) 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 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 */ 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 */ 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