• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright 2007 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.tonicsystems.jarjar;
18 
19 import java.util.regex.Matcher;
20 import java.util.regex.Pattern;
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 
24 class Wildcard
25 {
26     private static Pattern dstar = Pattern.compile("\\*\\*");
27     private static Pattern star  = Pattern.compile("\\*");
28     private static Pattern estar = Pattern.compile("\\+\\??\\)\\Z");
29     private static Pattern dollar = Pattern.compile("\\$");
30 
31     private final Pattern pattern;
32     private final int count;
33     private final ArrayList<Object> parts = new ArrayList<Object>(16); // kept for debugging
34     private final String[] strings;
35     private final int[] refs;
36 
Wildcard(String pattern, String result)37     public Wildcard(String pattern, String result) {
38         if (pattern.equals("**"))
39             throw new IllegalArgumentException("'**' is not a valid pattern");
40         if (!checkIdentifierChars(pattern, "/*"))
41             throw new IllegalArgumentException("Not a valid package pattern: " + pattern);
42         if (pattern.indexOf("***") >= 0)
43             throw new IllegalArgumentException("The sequence '***' is invalid in a package pattern");
44 
45         String regex = pattern;
46         regex = replaceAllLiteral(dstar, regex, "(.+?)");
47         regex = replaceAllLiteral(star, regex, "([^/]+)");
48         regex = replaceAllLiteral(estar, regex, "*)");
49         regex = replaceAllLiteral(dollar, regex, "\\$");
50         this.pattern = Pattern.compile("\\A" + regex + "\\Z");
51         this.count = this.pattern.matcher("foo").groupCount();
52 
53         // TODO: check for illegal characters
54         char[] chars = result.toCharArray();
55         int max = 0;
56         for (int i = 0, mark = 0, state = 0, len = chars.length; i < len + 1; i++) {
57             char ch = (i == len) ? '@' : chars[i];
58             if (state == 0) {
59                 if (ch == '@') {
60                     parts.add(new String(chars, mark, i - mark));
61                     mark = i + 1;
62                     state = 1;
63                 }
64             } else {
65                 switch (ch) {
66                 case '0': case '1': case '2': case '3': case '4':
67                 case '5': case '6': case '7': case '8': case '9':
68                     break;
69                 default:
70                     if (i == mark)
71                         throw new IllegalArgumentException("Backslash not followed by a digit");
72                     int n = Integer.parseInt(new String(chars, mark, i - mark));
73                     if (n > max)
74                         max = n;
75                     parts.add(new Integer(n));
76                     mark = i--;
77                     state = 0;
78                 }
79             }
80         }
81         int size = parts.size();
82         strings = new String[size];
83         refs = new int[size];
84         Arrays.fill(refs, -1);
85         for (int i = 0; i < size; i++) {
86             Object v = parts.get(i);
87             if (v instanceof String) {
88                 strings[i] = ((String)v).replace('.', '/');
89             } else {
90                 refs[i] = ((Integer)v).intValue();
91             }
92         }
93         if (count < max)
94             throw new IllegalArgumentException("Result includes impossible placeholder \"@" + max + "\": " + result);
95         // System.err.println(this);
96     }
97 
matches(String value)98     public boolean matches(String value) {
99         return getMatcher(value) != null;
100     }
101 
replace(String value)102     public String replace(String value) {
103         Matcher matcher = getMatcher(value);
104         if (matcher != null) {
105             StringBuilder sb = new StringBuilder();
106             for (int i = 0; i < strings.length; i++)
107                 sb.append((refs[i] >= 0) ? matcher.group(refs[i]) : strings[i]);
108             return sb.toString();
109         }
110         return null;
111     }
112 
getMatcher(String value)113     private Matcher getMatcher(String value) {
114         Matcher matcher = pattern.matcher(value);
115         if (matcher.matches() && checkIdentifierChars(value, "/"))
116             return matcher;
117         return null;
118     }
119 
checkIdentifierChars(String expr, String extra)120     private static boolean checkIdentifierChars(String expr, String extra) {
121       // package-info violates the spec for Java Identifiers.
122       // Nevertheless, expressions that end with this string are still legal.
123       // See 7.4.1.1 of the Java language spec for discussion.
124       if (expr.endsWith("package-info")) {
125           expr = expr.substring(0, expr.length() - "package-info".length());
126       }
127       for (int i = 0, len = expr.length(); i < len; i++) {
128           char c = expr.charAt(i);
129           if (extra.indexOf(c) >= 0)
130               continue;
131           if (!Character.isJavaIdentifierPart(c))
132               return false;
133       }
134       return true;
135     }
136 
replaceAllLiteral(Pattern pattern, String value, String replace)137     private static String replaceAllLiteral(Pattern pattern, String value, String replace) {
138         replace = replace.replaceAll("([$\\\\])", "\\\\$0");
139         return pattern.matcher(value).replaceAll(replace);
140     }
141 
toString()142     public String toString() {
143         return "Wildcard{pattern=" + pattern + ",parts=" + parts + "}";
144     }
145 }
146