diff options
Diffstat (limited to 'src/main/com/tonicsystems/jarjar/Wildcard.java')
-rw-r--r-- | src/main/com/tonicsystems/jarjar/Wildcard.java | 143 |
1 files changed, 143 insertions, 0 deletions
diff --git a/src/main/com/tonicsystems/jarjar/Wildcard.java b/src/main/com/tonicsystems/jarjar/Wildcard.java new file mode 100644 index 0000000..723c6f5 --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/Wildcard.java @@ -0,0 +1,143 @@ +/** + * Copyright 2007 Google Inc. + * + * 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 com.tonicsystems.jarjar; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.ArrayList; +import java.util.Arrays; + +class Wildcard +{ + private static Pattern dstar = Pattern.compile("\\*\\*"); + private static Pattern star = Pattern.compile("\\*"); + private static Pattern estar = Pattern.compile("\\+\\??\\)\\Z"); + + private final Pattern pattern; + private final int count; + private final ArrayList<Object> parts = new ArrayList<Object>(16); // kept for debugging + private final String[] strings; + private final int[] refs; + + public Wildcard(String pattern, String result) { + if (pattern.equals("**")) + throw new IllegalArgumentException("'**' is not a valid pattern"); + if (!checkIdentifierChars(pattern, "/*")) + throw new IllegalArgumentException("Not a valid package pattern: " + pattern); + if (pattern.indexOf("***") >= 0) + throw new IllegalArgumentException("The sequence '***' is invalid in a package pattern"); + + String regex = pattern; + regex = replaceAllLiteral(dstar, regex, "(.+?)"); + regex = replaceAllLiteral(star, regex, "([^/]+)"); + regex = replaceAllLiteral(estar, regex, "*)"); + this.pattern = Pattern.compile("\\A" + regex + "\\Z"); + this.count = this.pattern.matcher("foo").groupCount(); + + // TODO: check for illegal characters + char[] chars = result.toCharArray(); + int max = 0; + for (int i = 0, mark = 0, state = 0, len = chars.length; i < len + 1; i++) { + char ch = (i == len) ? '@' : chars[i]; + if (state == 0) { + if (ch == '@') { + parts.add(new String(chars, mark, i - mark)); + mark = i + 1; + state = 1; + } + } else { + switch (ch) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + break; + default: + if (i == mark) + throw new IllegalArgumentException("Backslash not followed by a digit"); + int n = Integer.parseInt(new String(chars, mark, i - mark)); + if (n > max) + max = n; + parts.add(new Integer(n)); + mark = i--; + state = 0; + } + } + } + int size = parts.size(); + strings = new String[size]; + refs = new int[size]; + Arrays.fill(refs, -1); + for (int i = 0; i < size; i++) { + Object v = parts.get(i); + if (v instanceof String) { + strings[i] = ((String)v).replace('.', '/'); + } else { + refs[i] = ((Integer)v).intValue(); + } + } + if (count < max) + throw new IllegalArgumentException("Result includes impossible placeholder \"@" + max + "\": " + result); + // System.err.println(this); + } + + public boolean matches(String value) { + return getMatcher(value) != null; + } + + public String replace(String value) { + Matcher matcher = getMatcher(value); + if (matcher != null) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < strings.length; i++) + sb.append((refs[i] >= 0) ? matcher.group(refs[i]) : strings[i]); + return sb.toString(); + } + return null; + } + + private Matcher getMatcher(String value) { + Matcher matcher = pattern.matcher(value); + if (matcher.matches() && checkIdentifierChars(value, "/")) + return matcher; + return null; + } + + private static boolean checkIdentifierChars(String expr, String extra) { + // package-info violates the spec for Java Identifiers. + // Nevertheless, expressions that end with this string are still legal. + // See 7.4.1.1 of the Java language spec for discussion. + if (expr.endsWith("package-info")) { + expr = expr.substring(0, expr.length() - "package-info".length()); + } + for (int i = 0, len = expr.length(); i < len; i++) { + char c = expr.charAt(i); + if (extra.indexOf(c) >= 0) + continue; + if (!Character.isJavaIdentifierPart(c)) + return false; + } + return true; + } + + private static String replaceAllLiteral(Pattern pattern, String value, String replace) { + replace = replace.replaceAll("([$\\\\])", "\\\\$0"); + return pattern.matcher(value).replaceAll(replace); + } + + public String toString() { + return "Wildcard{pattern=" + pattern + ",parts=" + parts + "}"; + } +} |