1 //
2 // ========================================================================
3 // Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4 // ------------------------------------------------------------------------
5 // All rights reserved. This program and the accompanying materials
6 // are made available under the terms of the Eclipse Public License v1.0
7 // and Apache License v2.0 which accompanies this distribution.
8 //
9 // The Eclipse Public License is available at
10 // http://www.eclipse.org/legal/epl-v10.html
11 //
12 // The Apache License v2.0 is available at
13 // http://www.opensource.org/licenses/apache2.0.php
14 //
15 // You may elect to redistribute this code under either of these licenses.
16 // ========================================================================
17 //
18
19
20 package org.eclipse.jetty.webapp;
21
22 import java.util.ArrayList;
23 import java.util.List;
24 import java.util.StringTokenizer;
25
26
27 /* ------------------------------------------------------------ */
28 /**
29 * ClasspathPattern performs sequential pattern matching of a class name
30 * against an internal array of classpath pattern entries.
31 *
32 * When an entry starts with '-' (minus), reverse matching is performed.
33 * When an entry ends with '.' (period), prefix matching is performed.
34 *
35 * When class is initialized from a classpath pattern string, entries
36 * in this string should be separated by ':' (semicolon) or ',' (comma).
37 */
38
39 public class ClasspathPattern
40 {
41 private static class Entry
42 {
43 public String classpath = null;
44 public boolean result = false;
45 public boolean partial = false;
46 }
47
48 final private List<String> _patterns = new ArrayList<String>();
49 final private List<Entry> _entries = new ArrayList<Entry>();
50
51 /* ------------------------------------------------------------ */
52 public ClasspathPattern()
53 {
54 }
55
56 /* ------------------------------------------------------------ */
57 public ClasspathPattern(String[] patterns)
58 {
59 setPatterns(patterns);
60 }
61
62 /* ------------------------------------------------------------ */
63 public ClasspathPattern(String pattern)
64 {
65 setPattern(pattern);
66 }
67
68
69 /* ------------------------------------------------------------ */
70 /**
71 * Initialize the matcher by parsing each classpath pattern in an array
72 *
73 * @param patterns array of classpath patterns
74 */
75 private void setPatterns(String[] patterns)
76 {
77 _patterns.clear();
78 _entries.clear();
79 addPatterns(patterns);
80 }
81
82 /* ------------------------------------------------------------ */
83 /**
84 * @param patterns array of classpath patterns
85 */
86 private void addPatterns(String[] patterns)
87 {
88 if (patterns != null)
89 {
90 Entry entry = null;
91 for (String pattern : patterns)
92 {
93 entry = createEntry(pattern);
94 if (entry != null)
95 {
96 _patterns.add(pattern);
97 _entries.add(entry);
98 }
99 }
100 }
101 }
102
103 /* ------------------------------------------------------------ */
104 /**
105 * @param patterns array of classpath patterns
106 */
107 private void prependPatterns(String[] patterns)
108 {
109 if (patterns != null)
110 {
111 Entry entry = null;
112 int i=0;
113 for (String pattern : patterns)
114 {
115 entry = createEntry(pattern);
116 if (entry != null)
117 {
118 _patterns.add(i,pattern);
119 _entries.add(i,entry);
120 i++;
121 }
122 }
123 }
124 }
125
126 /* ------------------------------------------------------------ */
127 /**
128 * Create an entry object containing information about
129 * a single classpath pattern
130 *
131 * @param pattern single classpath pattern
132 * @return corresponding Entry object
133 */
134 private Entry createEntry(String pattern)
135 {
136 Entry entry = null;
137
138 if (pattern != null)
139 {
140 String item = pattern.trim();
141 if (item.length() > 0)
142 {
143 entry = new Entry();
144 entry.result = !item.startsWith("-");
145 entry.partial = item.endsWith(".");
146 entry.classpath = entry.result ? item : item.substring(1).trim();
147 }
148 }
149 return entry;
150 }
151
152 /* ------------------------------------------------------------ */
153 /**
154 * Initialize the matcher by parsing a classpath pattern string
155 *
156 * @param pattern classpath pattern string
157 */
158 public void setPattern(String pattern)
159 {
160 _patterns.clear();
161 _entries.clear();
162 addPattern(pattern);
163 }
164
165 /* ------------------------------------------------------------ */
166 /**
167 * Parse a classpath pattern string and appending the result
168 * to the existing configuration.
169 *
170 * @param pattern classpath pattern string
171 */
172 public void addPattern(String pattern)
173 {
174 ArrayList<String> patterns = new ArrayList<String>();
175 StringTokenizer entries = new StringTokenizer(pattern, ":,");
176 while (entries.hasMoreTokens())
177 {
178 patterns.add(entries.nextToken());
179 }
180
181 addPatterns(patterns.toArray(new String[patterns.size()]));
182 }
183
184
185 /* ------------------------------------------------------------ */
186 public void prependPattern(String classOrPackage)
187 {
188 ArrayList<String> patterns = new ArrayList<String>();
189 StringTokenizer entries = new StringTokenizer(classOrPackage, ":,");
190 while (entries.hasMoreTokens())
191 {
192 patterns.add(entries.nextToken());
193 }
194
195 prependPatterns(patterns.toArray(new String[patterns.size()]));
196 }
197
198
199 /* ------------------------------------------------------------ */
200 /**
201 * @return array of classpath patterns
202 */
203 public String[] getPatterns()
204 {
205 String[] patterns = null;
206
207 if (_patterns!=null && _patterns.size() > 0)
208 {
209 patterns = _patterns.toArray(new String[_patterns.size()]);
210 }
211
212 return patterns;
213 }
214
215 /* ------------------------------------------------------------ */
216 /**
217 * Match the class name against the pattern
218 *
219 * @param name name of the class to match
220 * @return true if class matches the pattern
221 */
222 public boolean match(String name)
223 {
224 boolean result=false;
225
226 if (_entries != null)
227 {
228 name = name.replace('/','.');
229
230 int startIndex = 0;
231
232 while(startIndex < name.length() && name.charAt(startIndex) == '.') {
233 startIndex++;
234 }
235
236 int dollar = name.indexOf("$");
237
238 int endIndex = dollar != -1 ? dollar : name.length();
239
240 for (Entry entry : _entries)
241 {
242 if (entry != null)
243 {
244 if (entry.partial)
245 {
246 if (name.regionMatches(startIndex, entry.classpath, 0, entry.classpath.length()))
247 {
248 result = entry.result;
249 break;
250 }
251 }
252 else
253 {
254 int regionLength = endIndex-startIndex;
255 if (regionLength == entry.classpath.length()
256 && name.regionMatches(startIndex, entry.classpath, 0, regionLength))
257 {
258 result = entry.result;
259 break;
260 }
261 }
262 }
263 }
264 }
265 return result;
266 }
267
268 }