All Downloads are FREE. Search and download functionalities are using the official Maven repository.

proguard.util.ClassNameParser Maven / Gradle / Ivy

Go to download

ProGuardCORE is a free library to read, analyze, modify, and write Java class files.

There is a newer version: 9.1.6
Show newest version
/*
 * ProGuardCORE -- library to process Java bytecode.
 *
 * Copyright (c) 2002-2020 Guardsquare NV
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package proguard.util;

import proguard.classfile.TypeConstants;

/**
 * This {@link StringParser} can create {@link StringMatcher} instances for regular expressions
 * matching internal class names (or descriptors containing class names). The regular expressions
 * can contain the following wildcards: '%' for a single internal primitive type character (V, Z, B,
 * C, S, I, F, J, or D), '?' for a single regular class name character, '*' for any number of
 * regular class name characters, '**' for any number of regular class name characters or package
 * separator characters ('/'), 'L***;' for a single internal type (class name or primitive type,
 * array or non-array), 'L///;' for any number of internal types (class names and primitive types),
 * and '' for a reference to an earlier wildcard (n = 1, 2, ...)
 *
 * @author Eric Lafortune
 */
public class ClassNameParser implements StringParser {
  private static final char[] PRIMITIVE_TYPES =
      new char[] {
        TypeConstants.VOID,
        TypeConstants.BOOLEAN,
        TypeConstants.BYTE,
        TypeConstants.CHAR,
        TypeConstants.SHORT,
        TypeConstants.INT,
        TypeConstants.LONG,
        TypeConstants.FLOAT,
        TypeConstants.DOUBLE,
      };

  private final WildcardManager wildcardManager;

  /** Creates a new ClassNameParser. */
  public ClassNameParser() {
    this(null);
  }

  /**
   * Creates a new ClassNameParser that supports references to earlier wildcards.
   *
   * @param wildcardManager an optional scope for StringMatcher instances that match wildcards.
   */
  public ClassNameParser(WildcardManager wildcardManager) {
    this.wildcardManager = wildcardManager;
  }

  // Implementations for StringParser.

  public StringMatcher parse(String regularExpression) {
    int index;
    StringMatcher nextMatcher = new EmptyStringMatcher();

    // Look for wildcards.
    for (index = 0; index < regularExpression.length(); index++) {
      // Is there an 'L///;' wildcard?
      if (regularExpression.regionMatches(index, "L///;", 0, 5)) {
        // Create a settable matcher so we can parse wildcards from
        // left to right.
        SettableMatcher settableMatcher = new SettableMatcher();

        // Create a matcher for the wildcard.
        VariableStringMatcher variableStringMatcher =
            new VariableStringMatcher(
                null,
                new char[] {TypeConstants.METHOD_ARGUMENTS_CLOSE},
                0,
                Integer.MAX_VALUE,
                settableMatcher);

        // Remember it so it can be referenced back.
        if (wildcardManager != null) {
          wildcardManager.rememberVariableStringMatcher(variableStringMatcher);
        }

        // Recursively create a matcher for the rest of the string.
        settableMatcher.setMatcher(parse(regularExpression.substring(index + 5)));

        nextMatcher = variableStringMatcher;
        break;
      }

      // Is there an 'L***;' wildcard?
      else if (regularExpression.regionMatches(index, "L***;", 0, 5)) {
        // Create a settable matcher so we can parse wildcards from
        // left to right.
        SettableMatcher settableMatcher = new SettableMatcher();

        // Create a matcher for the wildcard.
        // TODO: The returned variable matcher is actually a composite that doesn't return the
        // entire matched string.
        VariableStringMatcher variableStringMatcher = createAnyTypeMatcher(settableMatcher);

        // Remember it so it can be referenced back.
        if (wildcardManager != null) {
          wildcardManager.rememberVariableStringMatcher(variableStringMatcher);
        }

        // Recursively create a matcher for the rest of the string.
        settableMatcher.setMatcher(parse(regularExpression.substring(index + 5)));

        nextMatcher = variableStringMatcher;
        break;
      }

      // Is there a '**' wildcard?
      else if (regularExpression.regionMatches(index, "**", 0, 2)) {
        // Handle the end of the regular expression more efficiently,
        // without any next matcher for the variable string matcher.
        SettableMatcher settableMatcher =
            index + 2 == regularExpression.length() ? null : new SettableMatcher();

        // Create a matcher for the wildcard.
        VariableStringMatcher variableStringMatcher =
            new VariableStringMatcher(
                null, new char[] {TypeConstants.CLASS_END}, 0, Integer.MAX_VALUE, settableMatcher);

        // Remember it so it can be referenced back.
        if (wildcardManager != null) {
          wildcardManager.rememberVariableStringMatcher(variableStringMatcher);
        }

        // Recursively create a matcher for the rest of the string.
        if (settableMatcher != null) {
          settableMatcher.setMatcher(parse(regularExpression.substring(index + 2)));
        }

        nextMatcher = variableStringMatcher;
        break;
      }

      // Is there a '*' wildcard?
      else if (regularExpression.charAt(index) == '*') {
        // Create a settable matcher so we can parse wildcards from
        // left to right.
        SettableMatcher settableMatcher = new SettableMatcher();

        // Create a matcher for the wildcard.
        VariableStringMatcher variableStringMatcher =
            new VariableStringMatcher(
                null,
                new char[] {TypeConstants.CLASS_END, TypeConstants.PACKAGE_SEPARATOR},
                0,
                Integer.MAX_VALUE,
                settableMatcher);

        // Remember it so it can be referenced back.
        if (wildcardManager != null) {
          wildcardManager.rememberVariableStringMatcher(variableStringMatcher);
        }

        // Recursively create a matcher for the rest of the string.
        settableMatcher.setMatcher(parse(regularExpression.substring(index + 1)));

        nextMatcher = variableStringMatcher;
        break;
      }

      // Is there a '?' wildcard?
      else if (regularExpression.charAt(index) == '?') {
        // Create a settable matcher so we can parse wildcards from
        // left to right.
        SettableMatcher settableMatcher = new SettableMatcher();

        // Create a matcher for the wildcard.
        VariableStringMatcher variableStringMatcher =
            new VariableStringMatcher(
                null,
                new char[] {TypeConstants.CLASS_END, TypeConstants.PACKAGE_SEPARATOR},
                1,
                1,
                settableMatcher);

        // Remember it so it can be referenced back.
        if (wildcardManager != null) {
          wildcardManager.rememberVariableStringMatcher(variableStringMatcher);
        }

        // Recursively create a matcher for the rest of the string.
        settableMatcher.setMatcher(parse(regularExpression.substring(index + 1)));

        nextMatcher = variableStringMatcher;
        break;
      }

      // Is there a '%' wildcard?
      else if (regularExpression.charAt(index) == '%') {
        // Create a settable matcher so we can parse wildcards from
        // left to right.
        SettableMatcher settableMatcher = new SettableMatcher();

        // Create a matcher for the wildcard.
        VariableStringMatcher variableStringMatcher =
            new VariableStringMatcher(PRIMITIVE_TYPES, null, 1, 1, settableMatcher);

        // Remember it so it can be referenced back.
        if (wildcardManager != null) {
          wildcardManager.rememberVariableStringMatcher(variableStringMatcher);
        }

        // Recursively create a matcher for the rest of the string.
        settableMatcher.setMatcher(parse(regularExpression.substring(index + 1)));

        nextMatcher = variableStringMatcher;
        break;
      }

      // Is there a '' wildcard?
      else if (wildcardManager != null) {
        int wildCardIndex = wildcardManager.wildCardIndex(regularExpression, index);
        if (wildCardIndex >= 0) {
          // Find the index of the closing bracket again.
          int closingIndex = regularExpression.indexOf('>', index + 1);

          // Retrieve the specified variable string matcher and
          // recursively create a matcher for the rest of the string.
          nextMatcher =
              wildcardManager.createMatchedStringMatcher(
                  wildCardIndex, parse(regularExpression.substring(closingIndex + 1)));

          break;
        }
      }
    }

    // Return a matcher for the fixed first part of the regular expression,
    // if any, and the remainder.
    return index != 0
        ? new FixedStringMatcher(regularExpression.substring(0, index), nextMatcher)
        : nextMatcher;
  }

  // Small utility methods.

  /**
   * Creates a StringMatcher that matches any type (class or primitive type, array or non-array) and
   * then the given matcher.
   */
  private VariableStringMatcher createAnyTypeMatcher(StringMatcher nextMatcher) {
    return
    // Any number of '['.
    new VariableStringMatcher(
        new char[] {TypeConstants.ARRAY},
        null,
        0,
        255,
        // Followed by:
        new OrMatcher(
            // A primitive type.
            new VariableStringMatcher(PRIMITIVE_TYPES, null, 1, 1, nextMatcher),

            // Or a class type.
            new VariableStringMatcher(
                new char[] {TypeConstants.CLASS_START},
                null,
                1,
                1,
                new VariableStringMatcher(
                    null,
                    new char[] {TypeConstants.CLASS_END},
                    0,
                    Integer.MAX_VALUE,
                    new VariableStringMatcher(
                        new char[] {TypeConstants.CLASS_END}, null, 1, 1, nextMatcher)))));
  }

  /** A main method for testing class name matching. */
  public static void main(String[] args) {
    try {
      System.out.println("Regular expression [" + args[0] + "]");
      ClassNameParser parser = new ClassNameParser();
      StringMatcher matcher = parser.parse(args[0]);
      for (int index = 1; index < args.length; index++) {
        String string = args[index];
        System.out.print("String             [" + string + "]");
        System.out.println(" -> match = " + matcher.matches(args[index]));
      }
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy