| /** |
| * 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 static Pattern dollar = Pattern.compile("\\$"); |
| |
| 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, "*)"); |
| regex = replaceAllLiteral(dollar, 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 + "}"; |
| } |
| } |