blob: 8deb3518d02e3e834bb9b344d8f9a83638ca5607 [file] [log] [blame]
/*
* 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.ArrayList;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
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 + "}";
}
}