Merge tag 'v2.3.20' into testing
[gpl/argeo-jcr.git] / org.argeo.slc.repo / src / org / argeo / slc / repo / internal / springutil / AntPathMatcher.java
diff --git a/org.argeo.slc.repo/src/org/argeo/slc/repo/internal/springutil/AntPathMatcher.java b/org.argeo.slc.repo/src/org/argeo/slc/repo/internal/springutil/AntPathMatcher.java
new file mode 100644 (file)
index 0000000..20becbc
--- /dev/null
@@ -0,0 +1,424 @@
+/*\r
+ * Copyright 2002-2007 the original author or authors.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+package org.argeo.slc.repo.internal.springutil;\r
+\r
+/**\r
+ * PathMatcher implementation for Ant-style path patterns. Examples are provided\r
+ * below.\r
+ *\r
+ * <p>\r
+ * Part of this mapping code has been kindly borrowed from\r
+ * <a href="http://ant.apache.org">Apache Ant</a>.\r
+ *\r
+ * <p>\r
+ * The mapping matches URLs using the following rules:<br>\r
+ * <ul>\r
+ * <li>? matches one character</li>\r
+ * <li>* matches zero or more characters</li>\r
+ * <li>** matches zero or more 'directories' in a path</li>\r
+ * </ul>\r
+ *\r
+ * <p>\r
+ * Some examples:<br>\r
+ * <ul>\r
+ * <li><code>com/t?st.jsp</code> - matches <code>com/test.jsp</code> but also\r
+ * <code>com/tast.jsp</code> or <code>com/txst.jsp</code></li>\r
+ * <li><code>com/*.jsp</code> - matches all <code>.jsp</code> files in the\r
+ * <code>com</code> directory</li>\r
+ * <li><code>com/&#42;&#42;/test.jsp</code> - matches all <code>test.jsp</code>\r
+ * files underneath the <code>com</code> path</li>\r
+ * <li><code>org/springframework/&#42;&#42;/*.jsp</code> - matches all\r
+ * <code>.jsp</code> files underneath the <code>org/springframework</code>\r
+ * path</li>\r
+ * <li><code>org/&#42;&#42;/servlet/bla.jsp</code> - matches\r
+ * <code>org/springframework/servlet/bla.jsp</code> but also\r
+ * <code>org/springframework/testing/servlet/bla.jsp</code> and\r
+ * <code>org/servlet/bla.jsp</code></li>\r
+ * </ul>\r
+ *\r
+ * @author Alef Arendsen\r
+ * @author Juergen Hoeller\r
+ * @author Rob Harrop\r
+ * @since 16.07.2003\r
+ */\r
+public class AntPathMatcher implements PathMatcher {\r
+\r
+       /** Default path separator: "/" */\r
+       public static final String DEFAULT_PATH_SEPARATOR = "/";\r
+\r
+       private String pathSeparator = DEFAULT_PATH_SEPARATOR;\r
+\r
+       /**\r
+        * Set the path separator to use for pattern parsing. Default is "/", as in Ant.\r
+        */\r
+       public void setPathSeparator(String pathSeparator) {\r
+               this.pathSeparator = (pathSeparator != null ? pathSeparator : DEFAULT_PATH_SEPARATOR);\r
+       }\r
+\r
+       public boolean isPattern(String path) {\r
+               return (path.indexOf('*') != -1 || path.indexOf('?') != -1);\r
+       }\r
+\r
+       public boolean match(String pattern, String path) {\r
+               return doMatch(pattern, path, true);\r
+       }\r
+\r
+       public boolean matchStart(String pattern, String path) {\r
+               return doMatch(pattern, path, false);\r
+       }\r
+\r
+       /**\r
+        * Actually match the given <code>path</code> against the given\r
+        * <code>pattern</code>.\r
+        * \r
+        * @param pattern   the pattern to match against\r
+        * @param path      the path String to test\r
+        * @param fullMatch whether a full pattern match is required (else a pattern\r
+        *                  match as far as the given base path goes is sufficient)\r
+        * @return <code>true</code> if the supplied <code>path</code> matched,\r
+        *         <code>false</code> if it didn't\r
+        */\r
+       protected boolean doMatch(String pattern, String path, boolean fullMatch) {\r
+               if (path.startsWith(this.pathSeparator) != pattern.startsWith(this.pathSeparator)) {\r
+                       return false;\r
+               }\r
+\r
+//             String[] pattDirs = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator);\r
+//             String[] pathDirs = StringUtils.tokenizeToStringArray(path, this.pathSeparator);\r
+               // mbaudier - 2020-03-13 : Use standard Java call:\r
+               String[] pattDirs = pattern.split(this.pathSeparator);\r
+               String[] pathDirs = path.split(this.pathSeparator);\r
+\r
+               int pattIdxStart = 0;\r
+               int pattIdxEnd = pattDirs.length - 1;\r
+               int pathIdxStart = 0;\r
+               int pathIdxEnd = pathDirs.length - 1;\r
+\r
+               // Match all elements up to the first **\r
+               while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {\r
+                       String patDir = pattDirs[pattIdxStart];\r
+                       if ("**".equals(patDir)) {\r
+                               break;\r
+                       }\r
+                       if (!matchStrings(patDir, pathDirs[pathIdxStart])) {\r
+                               return false;\r
+                       }\r
+                       pattIdxStart++;\r
+                       pathIdxStart++;\r
+               }\r
+\r
+               if (pathIdxStart > pathIdxEnd) {\r
+                       // Path is exhausted, only match if rest of pattern is * or **'s\r
+                       if (pattIdxStart > pattIdxEnd) {\r
+                               return (pattern.endsWith(this.pathSeparator) ? path.endsWith(this.pathSeparator)\r
+                                               : !path.endsWith(this.pathSeparator));\r
+                       }\r
+                       if (!fullMatch) {\r
+                               return true;\r
+                       }\r
+                       if (pattIdxStart == pattIdxEnd && pattDirs[pattIdxStart].equals("*") && path.endsWith(this.pathSeparator)) {\r
+                               return true;\r
+                       }\r
+                       for (int i = pattIdxStart; i <= pattIdxEnd; i++) {\r
+                               if (!pattDirs[i].equals("**")) {\r
+                                       return false;\r
+                               }\r
+                       }\r
+                       return true;\r
+               } else if (pattIdxStart > pattIdxEnd) {\r
+                       // String not exhausted, but pattern is. Failure.\r
+                       return false;\r
+               } else if (!fullMatch && "**".equals(pattDirs[pattIdxStart])) {\r
+                       // Path start definitely matches due to "**" part in pattern.\r
+                       return true;\r
+               }\r
+\r
+               // up to last '**'\r
+               while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {\r
+                       String patDir = pattDirs[pattIdxEnd];\r
+                       if (patDir.equals("**")) {\r
+                               break;\r
+                       }\r
+                       if (!matchStrings(patDir, pathDirs[pathIdxEnd])) {\r
+                               return false;\r
+                       }\r
+                       pattIdxEnd--;\r
+                       pathIdxEnd--;\r
+               }\r
+               if (pathIdxStart > pathIdxEnd) {\r
+                       // String is exhausted\r
+                       for (int i = pattIdxStart; i <= pattIdxEnd; i++) {\r
+                               if (!pattDirs[i].equals("**")) {\r
+                                       return false;\r
+                               }\r
+                       }\r
+                       return true;\r
+               }\r
+\r
+               while (pattIdxStart != pattIdxEnd && pathIdxStart <= pathIdxEnd) {\r
+                       int patIdxTmp = -1;\r
+                       for (int i = pattIdxStart + 1; i <= pattIdxEnd; i++) {\r
+                               if (pattDirs[i].equals("**")) {\r
+                                       patIdxTmp = i;\r
+                                       break;\r
+                               }\r
+                       }\r
+                       if (patIdxTmp == pattIdxStart + 1) {\r
+                               // '**/**' situation, so skip one\r
+                               pattIdxStart++;\r
+                               continue;\r
+                       }\r
+                       // Find the pattern between padIdxStart & padIdxTmp in str between\r
+                       // strIdxStart & strIdxEnd\r
+                       int patLength = (patIdxTmp - pattIdxStart - 1);\r
+                       int strLength = (pathIdxEnd - pathIdxStart + 1);\r
+                       int foundIdx = -1;\r
+\r
+                       strLoop: for (int i = 0; i <= strLength - patLength; i++) {\r
+                               for (int j = 0; j < patLength; j++) {\r
+                                       String subPat = (String) pattDirs[pattIdxStart + j + 1];\r
+                                       String subStr = (String) pathDirs[pathIdxStart + i + j];\r
+                                       if (!matchStrings(subPat, subStr)) {\r
+                                               continue strLoop;\r
+                                       }\r
+                               }\r
+                               foundIdx = pathIdxStart + i;\r
+                               break;\r
+                       }\r
+\r
+                       if (foundIdx == -1) {\r
+                               return false;\r
+                       }\r
+\r
+                       pattIdxStart = patIdxTmp;\r
+                       pathIdxStart = foundIdx + patLength;\r
+               }\r
+\r
+               for (int i = pattIdxStart; i <= pattIdxEnd; i++) {\r
+                       if (!pattDirs[i].equals("**")) {\r
+                               return false;\r
+                       }\r
+               }\r
+\r
+               return true;\r
+       }\r
+\r
+       /**\r
+        * Tests whether or not a string matches against a pattern. The pattern may\r
+        * contain two special characters:<br>\r
+        * '*' means zero or more characters<br>\r
+        * '?' means one and only one character\r
+        * \r
+        * @param pattern pattern to match against. Must not be <code>null</code>.\r
+        * @param str     string which must be matched against the pattern. Must not be\r
+        *                <code>null</code>.\r
+        * @return <code>true</code> if the string matches against the pattern, or\r
+        *         <code>false</code> otherwise.\r
+        */\r
+       private boolean matchStrings(String pattern, String str) {\r
+               char[] patArr = pattern.toCharArray();\r
+               char[] strArr = str.toCharArray();\r
+               int patIdxStart = 0;\r
+               int patIdxEnd = patArr.length - 1;\r
+               int strIdxStart = 0;\r
+               int strIdxEnd = strArr.length - 1;\r
+               char ch;\r
+\r
+               boolean containsStar = false;\r
+               for (int i = 0; i < patArr.length; i++) {\r
+                       if (patArr[i] == '*') {\r
+                               containsStar = true;\r
+                               break;\r
+                       }\r
+               }\r
+\r
+               if (!containsStar) {\r
+                       // No '*'s, so we make a shortcut\r
+                       if (patIdxEnd != strIdxEnd) {\r
+                               return false; // Pattern and string do not have the same size\r
+                       }\r
+                       for (int i = 0; i <= patIdxEnd; i++) {\r
+                               ch = patArr[i];\r
+                               if (ch != '?') {\r
+                                       if (ch != strArr[i]) {\r
+                                               return false;// Character mismatch\r
+                                       }\r
+                               }\r
+                       }\r
+                       return true; // String matches against pattern\r
+               }\r
+\r
+               if (patIdxEnd == 0) {\r
+                       return true; // Pattern contains only '*', which matches anything\r
+               }\r
+\r
+               // Process characters before first star\r
+               while ((ch = patArr[patIdxStart]) != '*' && strIdxStart <= strIdxEnd) {\r
+                       if (ch != '?') {\r
+                               if (ch != strArr[strIdxStart]) {\r
+                                       return false;// Character mismatch\r
+                               }\r
+                       }\r
+                       patIdxStart++;\r
+                       strIdxStart++;\r
+               }\r
+               if (strIdxStart > strIdxEnd) {\r
+                       // All characters in the string are used. Check if only '*'s are\r
+                       // left in the pattern. If so, we succeeded. Otherwise failure.\r
+                       for (int i = patIdxStart; i <= patIdxEnd; i++) {\r
+                               if (patArr[i] != '*') {\r
+                                       return false;\r
+                               }\r
+                       }\r
+                       return true;\r
+               }\r
+\r
+               // Process characters after last star\r
+               while ((ch = patArr[patIdxEnd]) != '*' && strIdxStart <= strIdxEnd) {\r
+                       if (ch != '?') {\r
+                               if (ch != strArr[strIdxEnd]) {\r
+                                       return false;// Character mismatch\r
+                               }\r
+                       }\r
+                       patIdxEnd--;\r
+                       strIdxEnd--;\r
+               }\r
+               if (strIdxStart > strIdxEnd) {\r
+                       // All characters in the string are used. Check if only '*'s are\r
+                       // left in the pattern. If so, we succeeded. Otherwise failure.\r
+                       for (int i = patIdxStart; i <= patIdxEnd; i++) {\r
+                               if (patArr[i] != '*') {\r
+                                       return false;\r
+                               }\r
+                       }\r
+                       return true;\r
+               }\r
+\r
+               // process pattern between stars. padIdxStart and patIdxEnd point\r
+               // always to a '*'.\r
+               while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {\r
+                       int patIdxTmp = -1;\r
+                       for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {\r
+                               if (patArr[i] == '*') {\r
+                                       patIdxTmp = i;\r
+                                       break;\r
+                               }\r
+                       }\r
+                       if (patIdxTmp == patIdxStart + 1) {\r
+                               // Two stars next to each other, skip the first one.\r
+                               patIdxStart++;\r
+                               continue;\r
+                       }\r
+                       // Find the pattern between padIdxStart & padIdxTmp in str between\r
+                       // strIdxStart & strIdxEnd\r
+                       int patLength = (patIdxTmp - patIdxStart - 1);\r
+                       int strLength = (strIdxEnd - strIdxStart + 1);\r
+                       int foundIdx = -1;\r
+                       strLoop: for (int i = 0; i <= strLength - patLength; i++) {\r
+                               for (int j = 0; j < patLength; j++) {\r
+                                       ch = patArr[patIdxStart + j + 1];\r
+                                       if (ch != '?') {\r
+                                               if (ch != strArr[strIdxStart + i + j]) {\r
+                                                       continue strLoop;\r
+                                               }\r
+                                       }\r
+                               }\r
+\r
+                               foundIdx = strIdxStart + i;\r
+                               break;\r
+                       }\r
+\r
+                       if (foundIdx == -1) {\r
+                               return false;\r
+                       }\r
+\r
+                       patIdxStart = patIdxTmp;\r
+                       strIdxStart = foundIdx + patLength;\r
+               }\r
+\r
+               // All characters in the string are used. Check if only '*'s are left\r
+               // in the pattern. If so, we succeeded. Otherwise failure.\r
+               for (int i = patIdxStart; i <= patIdxEnd; i++) {\r
+                       if (patArr[i] != '*') {\r
+                               return false;\r
+                       }\r
+               }\r
+\r
+               return true;\r
+       }\r
+\r
+       /**\r
+        * Given a pattern and a full path, determine the pattern-mapped part.\r
+        * <p>\r
+        * For example:\r
+        * <ul>\r
+        * <li>'<code>/docs/cvs/commit.html</code>' and\r
+        * '<code>/docs/cvs/commit.html</code> to ''</li>\r
+        * <li>'<code>/docs/*</code>' and '<code>/docs/cvs/commit</code> to\r
+        * '<code>cvs/commit</code>'</li>\r
+        * <li>'<code>/docs/cvs/*.html</code>' and '<code>/docs/cvs/commit.html</code>\r
+        * to '<code>commit.html</code>'</li>\r
+        * <li>'<code>/docs/**</code>' and '<code>/docs/cvs/commit</code> to\r
+        * '<code>cvs/commit</code>'</li>\r
+        * <li>'<code>/docs/**\/*.html</code>' and '<code>/docs/cvs/commit.html</code>\r
+        * to '<code>cvs/commit.html</code>'</li>\r
+        * <li>'<code>/*.html</code>' and '<code>/docs/cvs/commit.html</code> to\r
+        * '<code>docs/cvs/commit.html</code>'</li>\r
+        * <li>'<code>*.html</code>' and '<code>/docs/cvs/commit.html</code> to\r
+        * '<code>/docs/cvs/commit.html</code>'</li>\r
+        * <li>'<code>*</code>' and '<code>/docs/cvs/commit.html</code> to\r
+        * '<code>/docs/cvs/commit.html</code>'</li>\r
+        * </ul>\r
+        * <p>\r
+        * Assumes that {@link #match} returns <code>true</code> for\r
+        * '<code>pattern</code>' and '<code>path</code>', but does <strong>not</strong>\r
+        * enforce this.\r
+        */\r
+       public String extractPathWithinPattern(String pattern, String path) {\r
+//             String[] patternParts = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator);\r
+//             String[] pathParts = StringUtils.tokenizeToStringArray(path, this.pathSeparator);\r
+               // mbaudier - 2020-03-13 : Use standard Java call:\r
+               String[] patternParts = pattern.split(this.pathSeparator);\r
+               String[] pathParts = path.split(this.pathSeparator);\r
+\r
+               StringBuffer buffer = new StringBuffer();\r
+\r
+               // Add any path parts that have a wildcarded pattern part.\r
+               int puts = 0;\r
+               for (int i = 0; i < patternParts.length; i++) {\r
+                       String patternPart = patternParts[i];\r
+                       if ((patternPart.indexOf('*') > -1 || patternPart.indexOf('?') > -1) && pathParts.length >= i + 1) {\r
+                               if (puts > 0 || (i == 0 && !pattern.startsWith(this.pathSeparator))) {\r
+                                       buffer.append(this.pathSeparator);\r
+                               }\r
+                               buffer.append(pathParts[i]);\r
+                               puts++;\r
+                       }\r
+               }\r
+\r
+               // Append any trailing path parts.\r
+               for (int i = patternParts.length; i < pathParts.length; i++) {\r
+                       if (puts > 0 || i > 0) {\r
+                               buffer.append(this.pathSeparator);\r
+                       }\r
+                       buffer.append(pathParts[i]);\r
+               }\r
+\r
+               return buffer.toString();\r
+       }\r
+\r
+}\r