PathMatcher.java
Upload User: jiancairen
Upload Date: 2007-08-27
Package Size: 26458k
Code Size: 10k
Category:

Java Develop

Development Platform:

Java

  1. /*
  2.  * Copyright 2002-2004 the original author or authors.
  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. package org.springframework.util;
  17. import java.util.ArrayList;
  18. import java.util.List;
  19. import java.util.StringTokenizer;
  20. /**
  21.  * Utility for matching paths with patterns in an Ant-like way.
  22.  * Examples are provided below.
  23.  *
  24.  * <p>Part of this mapping code has been kindly borrowed from
  25.  * <a href="http://ant.apache.org">Apache Ant</a>.
  26.  *
  27.  * <p>The mapping matches URLs using the following rules:<br>
  28.  * <ul>
  29.  * <li>? matches one character</li>
  30.  * <li>* matches zero or more characters</li>
  31.  * <li>** matches zero or more 'directories' in a path</li>
  32.  * </ul>
  33.  *
  34.  * <p>Some examples:<br>
  35.  * <ul>
  36.  * <li>com/t?st.jsp - matches test.jsp but also tast.jsp or txst.jsp</li>
  37.  * <li>com/*.jsp - matches all .jsp files in the com directory</li>
  38.  * <li>com/&#42;&#42;/test.jsp - matches all test.jsp path underneath the com path</li>
  39.  * <li>org/springframework/&#42;&#42;/*.jsp - matches all .jsp files underneath the
  40.  * org/springframework path</li>
  41.  * <li>com/&#42;&#42;/servlet/bla.jsp - matches org/springframework/servlet/bla.jsp
  42.  * but also org/springframework/testing/servlet/bla.jsp and com/servlet/bla.jsp</li>
  43.  * </ul>
  44.  *
  45.  * @author Alef Arendsen
  46.  * @author Juergen Hoeller
  47.  * @since 16.07.2003
  48.  */
  49. public abstract class PathMatcher {
  50. /**
  51.  * Return if the given string represents a pattern to be matched
  52.  * via this class: If not, the "match" method does not have to be
  53.  * used because direct equality comparisons are sufficient.
  54.  * @param str the string to check
  55.  * @return whether the given string represents a pattern
  56.  * @see #match
  57.  */
  58. public static boolean isPattern(String str) {
  59. return (str.indexOf('*') != -1 || str.indexOf('?') != -1);
  60. }
  61. /**
  62.  * Match a string against the given pattern.
  63.  * @param pattern the pattern to match against
  64.  * @param str the string to test
  65.  * @return whether the arguments matched
  66.  */
  67. public static boolean match(String pattern, String str) {
  68. if (str.startsWith("/") != pattern.startsWith("/")) {
  69. return false;
  70. }
  71. List patDirs = tokenizePath(pattern);
  72. List strDirs = tokenizePath(str);
  73. int patIdxStart = 0;
  74. int patIdxEnd = patDirs.size() - 1;
  75. int strIdxStart = 0;
  76. int strIdxEnd = strDirs.size() - 1;
  77. // match all elements up to the first **
  78. while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
  79. String patDir = (String) patDirs.get(patIdxStart);
  80. if (patDir.equals("**")) {
  81. break;
  82. }
  83. if (!matchStrings(patDir, (String) strDirs.get(strIdxStart))) {
  84. return false;
  85. }
  86. patIdxStart++;
  87. strIdxStart++;
  88. }
  89. if (strIdxStart > strIdxEnd) {
  90. // String is exhausted, only match if rest of pattern is **'s
  91. for (int i = patIdxStart; i <= patIdxEnd; i++) {
  92. if (!patDirs.get(i).equals("**")) {
  93. return false;
  94. }
  95. }
  96. return true;
  97. }
  98. else {
  99. if (patIdxStart > patIdxEnd) {
  100. // String not exhausted, but pattern is. Failure.
  101. return false;
  102. }
  103. }
  104. // up to last '**'
  105. while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
  106. String patDir = (String) patDirs.get(patIdxEnd);
  107. if (patDir.equals("**")) {
  108. break;
  109. }
  110. if (!matchStrings(patDir, (String) strDirs.get(strIdxEnd))) {
  111. return false;
  112. }
  113. patIdxEnd--;
  114. strIdxEnd--;
  115. }
  116. if (strIdxStart > strIdxEnd) {
  117. // String is exhausted
  118. for (int i = patIdxStart; i <= patIdxEnd; i++) {
  119. if (!patDirs.get(i).equals("**")) {
  120. return false;
  121. }
  122. }
  123. return true;
  124. }
  125. while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
  126. int patIdxTmp = -1;
  127. for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
  128. if (patDirs.get(i).equals("**")) {
  129. patIdxTmp = i;
  130. break;
  131. }
  132. }
  133. if (patIdxTmp == patIdxStart + 1) {
  134. // '**/**' situation, so skip one
  135. patIdxStart++;
  136. continue;
  137. }
  138. // Find the pattern between padIdxStart & padIdxTmp in str between
  139. // strIdxStart & strIdxEnd
  140. int patLength = (patIdxTmp - patIdxStart - 1);
  141. int strLength = (strIdxEnd - strIdxStart + 1);
  142. int foundIdx = -1;
  143. strLoop:
  144.     for (int i = 0; i <= strLength - patLength; i++) {
  145.     for (int j = 0; j < patLength; j++) {
  146.     String subPat = (String) patDirs.get(patIdxStart + j + 1);
  147.     String subStr = (String) strDirs.get(strIdxStart + i + j);
  148.     if (!matchStrings(subPat, subStr)) {
  149.     continue strLoop;
  150.     }
  151.     }
  152.     foundIdx = strIdxStart + i;
  153.     break;
  154.     }
  155. if (foundIdx == -1) {
  156. return false;
  157. }
  158. patIdxStart = patIdxTmp;
  159. strIdxStart = foundIdx + patLength;
  160. }
  161. for (int i = patIdxStart; i <= patIdxEnd; i++) {
  162. if (!patDirs.get(i).equals("**")) {
  163. return false;
  164. }
  165. }
  166. return true;
  167. }
  168. /**
  169.  * Tests whether or not a string matches against a pattern.
  170.  * The pattern may contain two special characters:<br>
  171.  * '*' means zero or more characters<br>
  172.  * '?' means one and only one character
  173.  * @param pattern pattern to match against.
  174.  * Must not be <code>null</code>.
  175.  * @param str string which must be matched against the pattern.
  176.  * Must not be <code>null</code>.
  177.  * @return <code>true</code> if the string matches against the
  178.  * pattern, or <code>false</code> otherwise.
  179.  */
  180. private static boolean matchStrings(String pattern, String str) {
  181. char[] patArr = pattern.toCharArray();
  182. char[] strArr = str.toCharArray();
  183. int patIdxStart = 0;
  184. int patIdxEnd = patArr.length - 1;
  185. int strIdxStart = 0;
  186. int strIdxEnd = strArr.length - 1;
  187. char ch;
  188. boolean containsStar = false;
  189. for (int i = 0; i < patArr.length; i++) {
  190. if (patArr[i] == '*') {
  191. containsStar = true;
  192. break;
  193. }
  194. }
  195. if (!containsStar) {
  196. // No '*'s, so we make a shortcut
  197. if (patIdxEnd != strIdxEnd) {
  198. return false; // Pattern and string do not have the same size
  199. }
  200. for (int i = 0; i <= patIdxEnd; i++) {
  201. ch = patArr[i];
  202. if (ch != '?') {
  203. if (ch != strArr[i]) {
  204. return false;// Character mismatch
  205. }
  206. }
  207. }
  208. return true; // String matches against pattern
  209. }
  210. if (patIdxEnd == 0) {
  211. return true; // Pattern contains only '*', which matches anything
  212. }
  213. // Process characters before first star
  214. while ((ch = patArr[patIdxStart]) != '*' && strIdxStart <= strIdxEnd) {
  215. if (ch != '?') {
  216. if (ch != strArr[strIdxStart]) {
  217. return false;// Character mismatch
  218. }
  219. }
  220. patIdxStart++;
  221. strIdxStart++;
  222. }
  223. if (strIdxStart > strIdxEnd) {
  224. // All characters in the string are used. Check if only '*'s are
  225. // left in the pattern. If so, we succeeded. Otherwise failure.
  226. for (int i = patIdxStart; i <= patIdxEnd; i++) {
  227. if (patArr[i] != '*') {
  228. return false;
  229. }
  230. }
  231. return true;
  232. }
  233. // Process characters after last star
  234. while ((ch = patArr[patIdxEnd]) != '*' && strIdxStart <= strIdxEnd) {
  235. if (ch != '?') {
  236. if (ch != strArr[strIdxEnd]) {
  237. return false;// Character mismatch
  238. }
  239. }
  240. patIdxEnd--;
  241. strIdxEnd--;
  242. }
  243. if (strIdxStart > strIdxEnd) {
  244. // All characters in the string are used. Check if only '*'s are
  245. // left in the pattern. If so, we succeeded. Otherwise failure.
  246. for (int i = patIdxStart; i <= patIdxEnd; i++) {
  247. if (patArr[i] != '*') {
  248. return false;
  249. }
  250. }
  251. return true;
  252. }
  253. // process pattern between stars. padIdxStart and patIdxEnd point
  254. // always to a '*'.
  255. while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
  256. int patIdxTmp = -1;
  257. for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
  258. if (patArr[i] == '*') {
  259. patIdxTmp = i;
  260. break;
  261. }
  262. }
  263. if (patIdxTmp == patIdxStart + 1) {
  264. // Two stars next to each other, skip the first one.
  265. patIdxStart++;
  266. continue;
  267. }
  268. // Find the pattern between padIdxStart & padIdxTmp in str between
  269. // strIdxStart & strIdxEnd
  270. int patLength = (patIdxTmp - patIdxStart - 1);
  271. int strLength = (strIdxEnd - strIdxStart + 1);
  272. int foundIdx = -1;
  273. strLoop:
  274. for (int i = 0; i <= strLength - patLength; i++) {
  275. for (int j = 0; j < patLength; j++) {
  276. ch = patArr[patIdxStart + j + 1];
  277. if (ch != '?') {
  278. if (ch != strArr[strIdxStart + i + j]) {
  279. continue strLoop;
  280. }
  281. }
  282. }
  283. foundIdx = strIdxStart + i;
  284. break;
  285. }
  286. if (foundIdx == -1) {
  287. return false;
  288. }
  289. patIdxStart = patIdxTmp;
  290. strIdxStart = foundIdx + patLength;
  291. }
  292. // All characters in the string are used. Check if only '*'s are left
  293. // in the pattern. If so, we succeeded. Otherwise failure.
  294. for (int i = patIdxStart; i <= patIdxEnd; i++) {
  295. if (patArr[i] != '*') {
  296. return false;
  297. }
  298. }
  299. return true;
  300. }
  301. /**
  302.  * Break up a given path in a List of elements.
  303.  * @param path Path to tokenize. Must not be <code>null</code>.
  304.  * @return a List of path elements from the tokenized path
  305.  */
  306. private static List tokenizePath(String path) {
  307. List ret = new ArrayList();
  308. StringTokenizer st = new StringTokenizer(path, "/");
  309. while (st.hasMoreTokens()) {
  310. ret.add(st.nextToken());
  311. }
  312. return ret;
  313. }
  314. }