Home | History | Annotate | Download | only in util
      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