blob: 5cc486f202c0baae518663fb0ed307f9f47835ea [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.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 + "}";
}
}