Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
GAEUtils |
|
| 4.611111111111111;4.611 | ||||
GAEUtils$IOCase |
|
| 4.611111111111111;4.611 |
1 | /* | |
2 | * Licensed to the Apache Software Foundation (ASF) under one | |
3 | * or more contributor license agreements. See the NOTICE file | |
4 | * distributed with this work for additional information | |
5 | * regarding copyright ownership. The ASF licenses this file | |
6 | * to you under the Apache License, Version 2.0 (the | |
7 | * "License"); you may not use this file except in compliance | |
8 | * with the License. You may obtain a copy of the License at | |
9 | * | |
10 | * http://www.apache.org/licenses/LICENSE-2.0 | |
11 | * | |
12 | * Unless required by applicable law or agreed to in writing, | |
13 | * software distributed under the License is distributed on an | |
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
15 | * KIND, either express or implied. See the License for the | |
16 | * specific language governing permissions and limitations | |
17 | * under the License. | |
18 | */ | |
19 | package org.apache.myfaces.config.util; | |
20 | ||
21 | import java.io.IOException; | |
22 | import java.io.Serializable; | |
23 | import java.net.URL; | |
24 | import java.util.ArrayList; | |
25 | import java.util.Collection; | |
26 | import java.util.Enumeration; | |
27 | import java.util.Set; | |
28 | import java.util.Stack; | |
29 | import java.util.TreeSet; | |
30 | import java.util.jar.JarEntry; | |
31 | import java.util.jar.JarFile; | |
32 | import javax.faces.context.ExternalContext; | |
33 | import org.apache.myfaces.shared.util.StringUtils; | |
34 | ||
35 | /** | |
36 | * Utility methods to use in Google Application Engine (GAE) | |
37 | * | |
38 | * @author Leonardo Uribe | |
39 | */ | |
40 | 0 | public class GAEUtils |
41 | { | |
42 | ||
43 | public static final String WEB_LIB_PREFIX = "/WEB-INF/lib/"; | |
44 | ||
45 | ||
46 | /** | |
47 | * Look in all jars located inside /WEB-INF/lib/ folder for files that has | |
48 | * some specified prefix and suffix. It is a simplification that can be done | |
49 | * in GAE, because no JSF libraries are outside /WEB-INF/lib | |
50 | * | |
51 | * @param context | |
52 | * @param classloader | |
53 | * @param prefix | |
54 | * @param suffix | |
55 | * @return | |
56 | * @throws IOException | |
57 | */ | |
58 | public static Collection<URL> searchInWebLib( | |
59 | ExternalContext context, ClassLoader classloader, String filter, | |
60 | String prefix, String suffix) throws IOException | |
61 | { | |
62 | 0 | if (!filter.equals("none")) |
63 | { | |
64 | 0 | String[] jarFilesToScan = StringUtils.trim(StringUtils.splitLongString(filter, ',')); |
65 | 0 | Set<URL> urlSet = null; |
66 | 0 | Set<String> paths = context.getResourcePaths(WEB_LIB_PREFIX); |
67 | 0 | if (paths != null) |
68 | { | |
69 | 0 | for (Object pathObject : paths) |
70 | { | |
71 | 0 | String path = (String) pathObject; |
72 | 0 | if (path.endsWith(".jar") && wildcardMatch(path, jarFilesToScan, WEB_LIB_PREFIX)) |
73 | { | |
74 | // GAE does not use WAR format, so the app is just uncompressed in a directory | |
75 | // What we need here is just take the path of the file, and open the file as a | |
76 | // jar file. Then, if the jar should be scanned, try to find the required file. | |
77 | 0 | URL jarUrl = new URL("jar:" + context.getResource(path).toExternalForm() + "!/"); |
78 | 0 | JarFile jarFile = JarUtils.getJarFile(jarUrl); |
79 | ||
80 | 0 | Enumeration<JarEntry> entries = jarFile.entries(); |
81 | 0 | while (entries.hasMoreElements()) |
82 | { | |
83 | 0 | JarEntry entry = entries.nextElement(); |
84 | 0 | if (entry.isDirectory()) |
85 | { | |
86 | 0 | continue; // This is a directory |
87 | } | |
88 | 0 | String name = entry.getName(); |
89 | 0 | if (!name.startsWith(prefix)) |
90 | { | |
91 | 0 | continue; // Attribute files |
92 | } | |
93 | 0 | if (name.endsWith(suffix)) |
94 | { | |
95 | // Get it from classloader, because no URL can be | |
96 | // derived from JarEntry | |
97 | 0 | Enumeration<URL> alternateFacesConfigs = classloader.getResources(name); |
98 | 0 | while (alternateFacesConfigs.hasMoreElements()) |
99 | { | |
100 | 0 | if (urlSet == null) |
101 | { | |
102 | 0 | urlSet = new TreeSet<URL>(); |
103 | } | |
104 | 0 | urlSet.add(alternateFacesConfigs.nextElement()); |
105 | } | |
106 | } | |
107 | 0 | } |
108 | } | |
109 | 0 | } |
110 | } | |
111 | 0 | return urlSet; |
112 | } | |
113 | 0 | return null; |
114 | } | |
115 | ||
116 | public static boolean wildcardMatch(String filename, String[] wildcardMatchers, String prefix) | |
117 | { | |
118 | 0 | for (String matcher : wildcardMatchers) |
119 | { | |
120 | 0 | if (wildcardMatch(filename, prefix + matcher)) |
121 | { | |
122 | 0 | return true; |
123 | } | |
124 | } | |
125 | 0 | return false; |
126 | } | |
127 | ||
128 | // NOTE: CODE TAKEN FROM COMMONS-IO AND REFACTORED TO USE INSIDE GAE | |
129 | //----------------------------------------------------------------------- | |
130 | /** | |
131 | * Checks a filename to see if it matches the specified wildcard matcher, | |
132 | * always testing case-sensitive. <p> The wildcard matcher uses the | |
133 | * characters '?' and '*' to represent a single or multiple (zero or more) | |
134 | * wildcard characters. This is the same as often found on Dos/Unix command | |
135 | * lines. The check is case-sensitive always. | |
136 | * <pre> | |
137 | * wildcardMatch("c.txt", "*.txt") --> true | |
138 | * wildcardMatch("c.txt", "*.jpg") --> false | |
139 | * wildcardMatch("a/b/c.txt", "a/b/*") --> true | |
140 | * wildcardMatch("c.txt", "*.???") --> true | |
141 | * wildcardMatch("c.txt", "*.????") --> false | |
142 | * </pre> N.B. the sequence "*?" does not work properly at present in match | |
143 | * strings. | |
144 | * | |
145 | * @param filename the filename to match on | |
146 | * @param wildcardMatcher the wildcard string to match against | |
147 | * @return true if the filename matches the wilcard string | |
148 | * @see IOCase#SENSITIVE | |
149 | */ | |
150 | static boolean wildcardMatch(String filename, String wildcardMatcher) | |
151 | { | |
152 | 0 | return wildcardMatch(filename, wildcardMatcher, IOCase.SENSITIVE); |
153 | } | |
154 | ||
155 | /** | |
156 | * Checks a filename to see if it matches the specified wildcard matcher | |
157 | * using the case rules of the system. <p> The wildcard matcher uses the | |
158 | * characters '?' and '*' to represent a single or multiple (zero or more) | |
159 | * wildcard characters. This is the same as often found on Dos/Unix command | |
160 | * lines. The check is case-sensitive on Unix and case-insensitive on | |
161 | * Windows. | |
162 | * <pre> | |
163 | * wildcardMatch("c.txt", "*.txt") --> true | |
164 | * wildcardMatch("c.txt", "*.jpg") --> false | |
165 | * wildcardMatch("a/b/c.txt", "a/b/*") --> true | |
166 | * wildcardMatch("c.txt", "*.???") --> true | |
167 | * wildcardMatch("c.txt", "*.????") --> false | |
168 | * </pre> N.B. the sequence "*?" does not work properly at present in match | |
169 | * strings. | |
170 | * | |
171 | * @param filename the filename to match on | |
172 | * @param wildcardMatcher the wildcard string to match against | |
173 | * @return true if the filename matches the wilcard string | |
174 | * @see IOCase#SYSTEM | |
175 | */ | |
176 | static boolean wildcardMatchOnSystem(String filename, String wildcardMatcher) | |
177 | { | |
178 | //return wildcardMatch(filename, wildcardMatcher, IOCase.SYSTEM); | |
179 | 0 | return wildcardMatch(filename, wildcardMatcher, IOCase.SENSITIVE); |
180 | } | |
181 | ||
182 | /** | |
183 | * Checks a filename to see if it matches the specified wildcard matcher | |
184 | * allowing control over case-sensitivity. <p> The wildcard matcher uses the | |
185 | * characters '?' and '*' to represent a single or multiple (zero or more) | |
186 | * wildcard characters. N.B. the sequence "*?" does not work properly at | |
187 | * present in match strings. | |
188 | * | |
189 | * @param filename the filename to match on | |
190 | * @param wildcardMatcher the wildcard string to match against | |
191 | * @param caseSensitivity what case sensitivity rule to use, null means | |
192 | * case-sensitive | |
193 | * @return true if the filename matches the wilcard string | |
194 | * @since 1.3 | |
195 | */ | |
196 | static boolean wildcardMatch(String filename, String wildcardMatcher, IOCase caseSensitivity) | |
197 | { | |
198 | 0 | if (filename == null && wildcardMatcher == null) |
199 | { | |
200 | 0 | return true; |
201 | } | |
202 | 0 | if (filename == null || wildcardMatcher == null) |
203 | { | |
204 | 0 | return false; |
205 | } | |
206 | 0 | if (caseSensitivity == null) |
207 | { | |
208 | 0 | caseSensitivity = IOCase.SENSITIVE; |
209 | } | |
210 | 0 | String[] wcs = splitOnTokens(wildcardMatcher); |
211 | 0 | boolean anyChars = false; |
212 | 0 | int textIdx = 0; |
213 | 0 | int wcsIdx = 0; |
214 | 0 | Stack<int[]> backtrack = new Stack<int[]>(); |
215 | ||
216 | // loop around a backtrack stack, to handle complex * matching | |
217 | do | |
218 | { | |
219 | 0 | if (backtrack.size() > 0) |
220 | { | |
221 | 0 | int[] array = backtrack.pop(); |
222 | 0 | wcsIdx = array[0]; |
223 | 0 | textIdx = array[1]; |
224 | 0 | anyChars = true; |
225 | } | |
226 | ||
227 | // loop whilst tokens and text left to process | |
228 | 0 | while (wcsIdx < wcs.length) |
229 | { | |
230 | ||
231 | 0 | if (wcs[wcsIdx].equals("?")) |
232 | { | |
233 | // ? so move to next text char | |
234 | 0 | textIdx++; |
235 | 0 | if (textIdx > filename.length()) |
236 | { | |
237 | 0 | break; |
238 | } | |
239 | 0 | anyChars = false; |
240 | ||
241 | } | |
242 | 0 | else if (wcs[wcsIdx].equals("*")) |
243 | { | |
244 | // set any chars status | |
245 | 0 | anyChars = true; |
246 | 0 | if (wcsIdx == wcs.length - 1) |
247 | { | |
248 | 0 | textIdx = filename.length(); |
249 | } | |
250 | ||
251 | } | |
252 | else | |
253 | { | |
254 | // matching text token | |
255 | 0 | if (anyChars) |
256 | { | |
257 | // any chars then try to locate text token | |
258 | 0 | textIdx = caseSensitivity.checkIndexOf(filename, textIdx, wcs[wcsIdx]); |
259 | 0 | if (textIdx == -1) |
260 | { | |
261 | // token not found | |
262 | 0 | break; |
263 | } | |
264 | 0 | int repeat = caseSensitivity.checkIndexOf(filename, textIdx + 1, wcs[wcsIdx]); |
265 | 0 | if (repeat >= 0) |
266 | { | |
267 | 0 | backtrack.push(new int[] |
268 | { | |
269 | wcsIdx, repeat | |
270 | }); | |
271 | } | |
272 | 0 | } |
273 | else | |
274 | { | |
275 | // matching from current position | |
276 | 0 | if (!caseSensitivity.checkRegionMatches(filename, textIdx, wcs[wcsIdx])) |
277 | { | |
278 | // couldnt match token | |
279 | 0 | break; |
280 | } | |
281 | } | |
282 | ||
283 | // matched text token, move text index to end of matched token | |
284 | 0 | textIdx += wcs[wcsIdx].length(); |
285 | 0 | anyChars = false; |
286 | } | |
287 | ||
288 | 0 | wcsIdx++; |
289 | } | |
290 | ||
291 | // full match | |
292 | 0 | if (wcsIdx == wcs.length && textIdx == filename.length()) |
293 | { | |
294 | 0 | return true; |
295 | } | |
296 | ||
297 | 0 | } while (backtrack.size() > 0); |
298 | ||
299 | 0 | return false; |
300 | } | |
301 | ||
302 | /** | |
303 | * Splits a string into a number of tokens. The text is split by '?' and | |
304 | * '*'. Where multiple '*' occur consecutively they are collapsed into a | |
305 | * single '*'. | |
306 | * | |
307 | * @param text the text to split | |
308 | * @return the array of tokens, never null | |
309 | */ | |
310 | static String[] splitOnTokens(String text) | |
311 | { | |
312 | // used by wildcardMatch | |
313 | // package level so a unit test may run on this | |
314 | ||
315 | 0 | if (text.indexOf('?') == -1 && text.indexOf('*') == -1) |
316 | { | |
317 | 0 | return new String[] |
318 | { | |
319 | text | |
320 | }; | |
321 | } | |
322 | ||
323 | 0 | char[] array = text.toCharArray(); |
324 | 0 | ArrayList<String> list = new ArrayList<String>(); |
325 | 0 | StringBuilder buffer = new StringBuilder(); |
326 | 0 | for (int i = 0; i < array.length; i++) |
327 | { | |
328 | 0 | if (array[i] == '?' || array[i] == '*') |
329 | { | |
330 | 0 | if (buffer.length() != 0) |
331 | { | |
332 | 0 | list.add(buffer.toString()); |
333 | 0 | buffer.setLength(0); |
334 | } | |
335 | 0 | if (array[i] == '?') |
336 | { | |
337 | 0 | list.add("?"); |
338 | } | |
339 | 0 | else if (list.isEmpty() |
340 | || i > 0 && list.get(list.size() - 1).equals("*") == false) | |
341 | { | |
342 | 0 | list.add("*"); |
343 | } | |
344 | } | |
345 | else | |
346 | { | |
347 | 0 | buffer.append(array[i]); |
348 | } | |
349 | } | |
350 | 0 | if (buffer.length() != 0) |
351 | { | |
352 | 0 | list.add(buffer.toString()); |
353 | } | |
354 | ||
355 | 0 | return list.toArray(new String[list.size()]); |
356 | } | |
357 | ||
358 | 0 | final static class IOCase implements Serializable |
359 | { | |
360 | ||
361 | /** | |
362 | * The constant for case sensitive regardless of operating system. | |
363 | */ | |
364 | 0 | public static final IOCase SENSITIVE = new IOCase("Sensitive", true); |
365 | /** | |
366 | * The constant for case insensitive regardless of operating system. | |
367 | */ | |
368 | 0 | public static final IOCase INSENSITIVE = new IOCase("Insensitive", false); |
369 | /** | |
370 | * The constant for case sensitivity determined by the current operating | |
371 | * system. Windows is case-insensitive when comparing filenames, Unix is | |
372 | * case-sensitive. <p> <strong>Note:</strong> This only caters for | |
373 | * Windows and Unix. Other operating systems (e.g. OSX and OpenVMS) are | |
374 | * treated as case sensitive if they use the Unix file separator and | |
375 | * case-insensitive if they use the Windows file separator (see {@link java.io.File#separatorChar}). | |
376 | * <p> If you derialize this constant of Windows, and deserialize on | |
377 | * Unix, or vice versa, then the value of the case-sensitivity flag will | |
378 | * change. | |
379 | */ | |
380 | //public static final IOCase SYSTEM = new IOCase("System", !FilenameUtils.isSystemWindows()); | |
381 | /** | |
382 | * Serialization version. | |
383 | */ | |
384 | private static final long serialVersionUID = -6343169151696340687L; | |
385 | /** | |
386 | * The enumeration name. | |
387 | */ | |
388 | private final String name; | |
389 | /** | |
390 | * The sensitivity flag. | |
391 | */ | |
392 | private final transient boolean sensitive; | |
393 | ||
394 | //----------------------------------------------------------------------- | |
395 | /** | |
396 | * Factory method to create an IOCase from a name. | |
397 | * | |
398 | * @param name the name to find | |
399 | * @return the IOCase object | |
400 | * @throws IllegalArgumentException if the name is invalid | |
401 | */ | |
402 | public static IOCase forName(String name) | |
403 | { | |
404 | 0 | if (IOCase.SENSITIVE.name.equals(name)) |
405 | { | |
406 | 0 | return IOCase.SENSITIVE; |
407 | } | |
408 | 0 | if (IOCase.INSENSITIVE.name.equals(name)) |
409 | { | |
410 | 0 | return IOCase.INSENSITIVE; |
411 | } | |
412 | //if (IOCase.SYSTEM.name.equals(name)){ | |
413 | // return IOCase.SYSTEM; | |
414 | //} | |
415 | 0 | throw new IllegalArgumentException("Invalid IOCase name: " + name); |
416 | } | |
417 | ||
418 | //----------------------------------------------------------------------- | |
419 | /** | |
420 | * Private constructor. | |
421 | * | |
422 | * @param name the name | |
423 | * @param sensitive the sensitivity | |
424 | */ | |
425 | private IOCase(String name, boolean sensitive) | |
426 | 0 | { |
427 | 0 | this.name = name; |
428 | 0 | this.sensitive = sensitive; |
429 | 0 | } |
430 | ||
431 | /** | |
432 | * Replaces the enumeration from the stream with a real one. This | |
433 | * ensures that the correct flag is set for SYSTEM. | |
434 | * | |
435 | * @return the resolved object | |
436 | */ | |
437 | private Object readResolve() | |
438 | { | |
439 | 0 | return forName(name); |
440 | } | |
441 | ||
442 | //----------------------------------------------------------------------- | |
443 | /** | |
444 | * Gets the name of the constant. | |
445 | * | |
446 | * @return the name of the constant | |
447 | */ | |
448 | public String getName() | |
449 | { | |
450 | 0 | return name; |
451 | } | |
452 | ||
453 | /** | |
454 | * Does the object represent case sensitive comparison. | |
455 | * | |
456 | * @return true if case sensitive | |
457 | */ | |
458 | public boolean isCaseSensitive() | |
459 | { | |
460 | 0 | return sensitive; |
461 | } | |
462 | ||
463 | //----------------------------------------------------------------------- | |
464 | /** | |
465 | * Compares two strings using the case-sensitivity rule. <p> This method | |
466 | * mimics {@link String#compareTo} but takes case-sensitivity into | |
467 | * account. | |
468 | * | |
469 | * @param str1 the first string to compare, not null | |
470 | * @param str2 the second string to compare, not null | |
471 | * @return true if equal using the case rules | |
472 | * @throws NullPointerException if either string is null | |
473 | */ | |
474 | public int checkCompareTo(String str1, String str2) | |
475 | { | |
476 | 0 | if (str1 == null || str2 == null) |
477 | { | |
478 | 0 | throw new NullPointerException("The strings must not be null"); |
479 | } | |
480 | 0 | return sensitive ? str1.compareTo(str2) : str1.compareToIgnoreCase(str2); |
481 | } | |
482 | ||
483 | /** | |
484 | * Compares two strings using the case-sensitivity rule. <p> This method | |
485 | * mimics {@link String#equals} but takes case-sensitivity into account. | |
486 | * | |
487 | * @param str1 the first string to compare, not null | |
488 | * @param str2 the second string to compare, not null | |
489 | * @return true if equal using the case rules | |
490 | * @throws NullPointerException if either string is null | |
491 | */ | |
492 | public boolean checkEquals(String str1, String str2) | |
493 | { | |
494 | 0 | if (str1 == null || str2 == null) |
495 | { | |
496 | 0 | throw new NullPointerException("The strings must not be null"); |
497 | } | |
498 | 0 | return sensitive ? str1.equals(str2) : str1.equalsIgnoreCase(str2); |
499 | } | |
500 | ||
501 | /** | |
502 | * Checks if one string starts with another using the case-sensitivity | |
503 | * rule. <p> This method mimics {@link String#startsWith(String)} but | |
504 | * takes case-sensitivity into account. | |
505 | * | |
506 | * @param str the string to check, not null | |
507 | * @param start the start to compare against, not null | |
508 | * @return true if equal using the case rules | |
509 | * @throws NullPointerException if either string is null | |
510 | */ | |
511 | public boolean checkStartsWith(String str, String start) | |
512 | { | |
513 | 0 | return str.regionMatches(!sensitive, 0, start, 0, start.length()); |
514 | } | |
515 | ||
516 | /** | |
517 | * Checks if one string ends with another using the case-sensitivity | |
518 | * rule. <p> This method mimics {@link String#endsWith} but takes | |
519 | * case-sensitivity into account. | |
520 | * | |
521 | * @param str the string to check, not null | |
522 | * @param end the end to compare against, not null | |
523 | * @return true if equal using the case rules | |
524 | * @throws NullPointerException if either string is null | |
525 | */ | |
526 | public boolean checkEndsWith(String str, String end) | |
527 | { | |
528 | 0 | int endLen = end.length(); |
529 | 0 | return str.regionMatches(!sensitive, str.length() - endLen, end, 0, endLen); |
530 | } | |
531 | ||
532 | /** | |
533 | * Checks if one string contains another starting at a specific index | |
534 | * using the case-sensitivity rule. <p> This method mimics parts of {@link String#indexOf(String, int)} | |
535 | * but takes case-sensitivity into account. | |
536 | * | |
537 | * @param str the string to check, not null | |
538 | * @param strStartIndex the index to start at in str | |
539 | * @param search the start to search for, not null | |
540 | * @return the first index of the search String, -1 if no match or {@code null} | |
541 | * string input | |
542 | * @throws NullPointerException if either string is null | |
543 | * @since 2.0 | |
544 | */ | |
545 | public int checkIndexOf(String str, int strStartIndex, String search) | |
546 | { | |
547 | 0 | int endIndex = str.length() - search.length(); |
548 | 0 | if (endIndex >= strStartIndex) |
549 | { | |
550 | 0 | for (int i = strStartIndex; i <= endIndex; i++) |
551 | { | |
552 | 0 | if (checkRegionMatches(str, i, search)) |
553 | { | |
554 | 0 | return i; |
555 | } | |
556 | } | |
557 | } | |
558 | 0 | return -1; |
559 | } | |
560 | ||
561 | /** | |
562 | * Checks if one string contains another at a specific index using the | |
563 | * case-sensitivity rule. <p> This method mimics parts of {@link | |
564 | * String#regionMatches(boolean, int, String, int, int)} | |
565 | * but takes case-sensitivity into account. | |
566 | * | |
567 | * @param str the string to check, not null | |
568 | * @param strStartIndex the index to start at in str | |
569 | * @param search the start to search for, not null | |
570 | * @return true if equal using the case rules | |
571 | * @throws NullPointerException if either string is null | |
572 | */ | |
573 | public boolean checkRegionMatches(String str, int strStartIndex, String search) | |
574 | { | |
575 | 0 | return str.regionMatches(!sensitive, strStartIndex, search, 0, search.length()); |
576 | } | |
577 | ||
578 | //----------------------------------------------------------------------- | |
579 | /** | |
580 | * Gets a string describing the sensitivity. | |
581 | * | |
582 | * @return a string describing the sensitivity | |
583 | */ | |
584 | @Override | |
585 | public String toString() | |
586 | { | |
587 | 0 | return name; |
588 | } | |
589 | } | |
590 | } |