You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by gn...@apache.org on 2017/05/04 11:26:26 UTC
svn commit: r1793780 - in /felix/trunk/gogo/runtime/src:
main/java/org/apache/felix/gogo/runtime/
test/java/org/apache/felix/gogo/runtime/
Author: gnodet
Date: Thu May 4 11:26:26 2017
New Revision: 1793780
URL: http://svn.apache.org/viewvc?rev=1793780&view=rev
Log:
[FELIX-5634][gogo][runtime] The file name generation may loop into subtrees for nothing
Added:
felix/trunk/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/GlobPathMatcher.java
felix/trunk/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/ExpanderTest.java
felix/trunk/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/GlobPathMatcherTest.java
Modified:
felix/trunk/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Expander.java
Modified: felix/trunk/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Expander.java
URL: http://svn.apache.org/viewvc/felix/trunk/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Expander.java?rev=1793780&r1=1793779&r2=1793780&view=diff
==============================================================================
--- felix/trunk/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Expander.java (original)
+++ felix/trunk/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Expander.java Thu May 4 11:26:26 2017
@@ -24,7 +24,6 @@ import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.nio.file.PathMatcher;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Arrays;
@@ -601,7 +600,7 @@ public class Expander extends BaseTokeni
dir = currentDir;
prefix = "";
}
- final PathMatcher matcher = dir.getFileSystem().getPathMatcher("glob:" + arg);
+ final GlobPathMatcher matcher = new GlobPathMatcher(arg.toString());
Files.walkFileTree(dir,
EnumSet.of(FileVisitOption.FOLLOW_LINKS),
Integer.MAX_VALUE,
@@ -619,11 +618,18 @@ public class Expander extends BaseTokeni
return FileVisitResult.SKIP_SUBTREE;
}
Path r = dir.relativize(file);
- if (matcher.matches(r))
+ if (matcher.matches(r.toString(), true))
{
expanded.add(prefix + r.toString());
}
- return FileVisitResult.CONTINUE;
+ if (matcher.matches(r.toString(), false))
+ {
+ return FileVisitResult.CONTINUE;
+ }
+ else
+ {
+ return FileVisitResult.SKIP_SUBTREE;
+ }
}
@Override
@@ -632,7 +638,7 @@ public class Expander extends BaseTokeni
if (!Files.isHidden(file))
{
Path r = dir.relativize(file);
- if (matcher.matches(r))
+ if (matcher.matches(r.toString(), true))
{
expanded.add(prefix + r.toString());
}
Added: felix/trunk/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/GlobPathMatcher.java
URL: http://svn.apache.org/viewvc/felix/trunk/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/GlobPathMatcher.java?rev=1793780&view=auto
==============================================================================
--- felix/trunk/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/GlobPathMatcher.java (added)
+++ felix/trunk/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/GlobPathMatcher.java Thu May 4 11:26:26 2017
@@ -0,0 +1,301 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.gogo.runtime;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.StringTokenizer;
+import java.util.regex.Pattern;
+
+/**
+ * Freely adapted from Spring's AntPathMatcher.
+ * We don't use the file system's glob PathMatcher
+ * because it can't detect directories which can't be
+ * a start of a match.
+ */
+public class GlobPathMatcher {
+
+ /** Default path separator: "/" */
+ public static final String DEFAULT_PATH_SEPARATOR = "/";
+
+ private String pattern;
+ private String pathSeparator;
+ private boolean caseSensitive;
+
+ private String[] pattDirs;
+ private Pattern[] pattPats;
+
+ /**
+ * Create a new instance with the {@link #DEFAULT_PATH_SEPARATOR}.
+ */
+ public GlobPathMatcher(String pattern) {
+ this(pattern, DEFAULT_PATH_SEPARATOR, true);
+ }
+
+ /**
+ * A convenient, alternative constructor to use with a custom path separator.
+ * @param pathSeparator the path separator to use, must not be {@code null}.
+ */
+ public GlobPathMatcher(String pattern, String pathSeparator, boolean caseSensitive) {
+ Objects.requireNonNull(pathSeparator, "'pathSeparator' is required");
+ this.pattern = pattern;
+ this.pathSeparator = pathSeparator;
+ this.caseSensitive = caseSensitive;
+ this.pattDirs = tokenizePath(pattern);
+ this.pattPats = new Pattern[pattDirs.length];
+ for (int i = 0; i < pattDirs.length; i++) {
+ pattPats[i] = createMatcherPattern(pattDirs[i]);
+ }
+ }
+
+
+ /**
+ * Actually match the given {@code path} against the given {@code pattern}.
+ * @param path the path String to test
+ * @param fullMatch whether a full pattern match is required (else a pattern match
+ * as far as the given base path goes is sufficient)
+ * @return {@code true} if the supplied {@code path} matched, {@code false} if it didn't
+ */
+ public boolean matches(String path, boolean fullMatch) {
+ if (path.startsWith(this.pathSeparator) != pattern.startsWith(this.pathSeparator)) {
+ return false;
+ }
+
+ String[] pathDirs = tokenizePath(path);
+
+ int pattIdxStart = 0;
+ int pattIdxEnd = pattDirs.length - 1;
+ int pathIdxStart = 0;
+ int pathIdxEnd = pathDirs.length - 1;
+
+ // Match all elements up to the first **
+ while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {
+ String pattDir = pattDirs[pattIdxStart];
+ if ("**".equals(pattDir)) {
+ break;
+ }
+ if (!matchStrings(pattIdxStart, pathDirs[pathIdxStart])) {
+ return false;
+ }
+ pattIdxStart++;
+ pathIdxStart++;
+ }
+
+ if (pathIdxStart > pathIdxEnd) {
+ // Path is exhausted, only match if rest of pattern is * or **'s
+ if (pattIdxStart > pattIdxEnd) {
+ return (pattern.endsWith(this.pathSeparator) == path.endsWith(this.pathSeparator));
+ }
+ if (!fullMatch) {
+ return true;
+ }
+ if (pattIdxStart == pattIdxEnd && pattDirs[pattIdxStart].equals("*") && path.endsWith(this.pathSeparator)) {
+ return true;
+ }
+ for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
+ if (!pattDirs[i].equals("**")) {
+ return false;
+ }
+ }
+ return true;
+ }
+ else if (pattIdxStart > pattIdxEnd) {
+ // String not exhausted, but pattern is. Failure.
+ return false;
+ }
+ else if (!fullMatch && "**".equals(pattDirs[pattIdxStart])) {
+ // Path start definitely matches due to "**" part in pattern.
+ return true;
+ }
+
+ // up to last '**'
+ while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {
+ String pattDir = pattDirs[pattIdxEnd];
+ if (pattDir.equals("**")) {
+ break;
+ }
+ if (!matchStrings(pattIdxEnd, pathDirs[pathIdxEnd])) {
+ return false;
+ }
+ pattIdxEnd--;
+ pathIdxEnd--;
+ }
+ if (pathIdxStart > pathIdxEnd) {
+ // String is exhausted
+ for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
+ if (!pattDirs[i].equals("**")) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ while (pattIdxStart != pattIdxEnd && pathIdxStart <= pathIdxEnd) {
+ int patIdxTmp = -1;
+ for (int i = pattIdxStart + 1; i <= pattIdxEnd; i++) {
+ if (pattDirs[i].equals("**")) {
+ patIdxTmp = i;
+ break;
+ }
+ }
+ if (patIdxTmp == pattIdxStart + 1) {
+ // '**/**' situation, so skip one
+ pattIdxStart++;
+ continue;
+ }
+ // Find the pattern between padIdxStart & padIdxTmp in str between
+ // strIdxStart & strIdxEnd
+ int patLength = (patIdxTmp - pattIdxStart - 1);
+ int strLength = (pathIdxEnd - pathIdxStart + 1);
+ int foundIdx = -1;
+
+ strLoop:
+ for (int i = 0; i <= strLength - patLength; i++) {
+ for (int j = 0; j < patLength; j++) {
+ String subStr = pathDirs[pathIdxStart + i + j];
+ if (!matchStrings(pattIdxStart + j + 1, subStr)) {
+ continue strLoop;
+ }
+ }
+ foundIdx = pathIdxStart + i;
+ break;
+ }
+
+ if (foundIdx == -1) {
+ return false;
+ }
+
+ pattIdxStart = patIdxTmp;
+ pathIdxStart = foundIdx + patLength;
+ }
+
+ for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
+ if (!pattDirs[i].equals("**")) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Tokenize the given path String into parts, based on this matcher's settings.
+ * @param path the path to tokenize
+ * @return the tokenized path parts
+ */
+ private String[] tokenizePath(String path) {
+ StringTokenizer st = new StringTokenizer(path, pathSeparator);
+ List<String> tokens = new ArrayList<>();
+ while (st.hasMoreTokens()) {
+ String token = st.nextToken();
+ if (token.length() > 0) {
+ tokens.add(token);
+ }
+ }
+ return tokens.toArray(new String[tokens.size()]);
+ }
+
+ private boolean matchStrings(int pattIdx, String str) {
+ return pattPats[pattIdx].matcher(str).matches();
+ }
+
+ private Pattern createMatcherPattern(String pattern) {
+ StringBuilder sb = new StringBuilder(pattern.length());
+ int inGroup = 0;
+ int inClass = 0;
+ int firstIndexInClass = -1;
+ char[] arr = pattern.toCharArray();
+ for (int i = 0; i < arr.length; i++) {
+ char ch = arr[i];
+ switch (ch) {
+ case '\\':
+ if (++i >= arr.length) {
+ sb.append('\\');
+ } else {
+ char next = arr[i];
+ switch (next) {
+ case ',':
+ // escape not needed
+ break;
+ case 'Q':
+ case 'E':
+ // extra escape needed
+ sb.append("\\\\");
+ break;
+ default:
+ sb.append('\\');
+ break;
+ }
+ sb.append(next);
+ }
+ break;
+ case '*':
+ sb.append(inClass == 0 ? ".*" : "*");
+ break;
+ case '?':
+ sb.append(inClass == 0 ? '.' : '?');
+ break;
+ case '[':
+ inClass++;
+ firstIndexInClass = i + 1;
+ sb.append('[');
+ break;
+ case ']':
+ inClass--;
+ sb.append(']');
+ break;
+ case '.':
+ case '(':
+ case ')':
+ case '+':
+ case '|':
+ case '^':
+ case '$':
+ case '@':
+ case '%':
+ if (inClass == 0 || (firstIndexInClass == i && ch == '^')) {
+ sb.append('\\');
+ }
+ sb.append(ch);
+ break;
+ case '!':
+ sb.append(firstIndexInClass == i ? '^' : '!');
+ break;
+ case '{':
+ inGroup++;
+ sb.append('(');
+ break;
+ case '}':
+ inGroup--;
+ sb.append(')');
+ break;
+ case ',':
+ sb.append(inGroup > 0 ? '|' : ',');
+ break;
+ default:
+ sb.append(ch);
+ }
+ }
+ return (caseSensitive ? Pattern.compile(sb.toString()) :
+ Pattern.compile(sb.toString(), Pattern.CASE_INSENSITIVE));
+ }
+
+
+}
\ No newline at end of file
Added: felix/trunk/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/ExpanderTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/ExpanderTest.java?rev=1793780&view=auto
==============================================================================
--- felix/trunk/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/ExpanderTest.java (added)
+++ felix/trunk/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/ExpanderTest.java Thu May 4 11:26:26 2017
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.gogo.runtime;
+
+import org.junit.Test;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+public class ExpanderTest {
+
+ @Test
+ public void testGenerateFiles() throws IOException {
+ final Path testdir = Paths.get(".").toAbsolutePath().resolve("target/testdir").normalize();
+ Evaluate evaluate = new Evaluate() {
+ @Override
+ public Object eval(Token t) throws Exception {
+ return null;
+ }
+ @Override
+ public Object get(String key) {
+ if ("HOME".equals(key)) {
+ return testdir.resolve("Users/gogo");
+ }
+ return null;
+ }
+ @Override
+ public Object put(String key, Object value) {
+ return null;
+ }
+ @Override
+ public Object expr(Token t) {
+ return null;
+ }
+ @Override
+ public Path currentDir() {
+ return testdir.resolve("Users/gogo/karaf/home");
+ }
+ };
+ deleteRecursive(testdir);
+ Files.createDirectories(testdir);
+ Files.createDirectories(evaluate.currentDir());
+ Path home = Paths.get(evaluate.get("HOME").toString());
+ Files.createDirectories(home);
+ Files.createFile(home.resolve("test1.txt"));
+ Files.createDirectories(home.resolve("child"));
+ Files.createFile(home.resolve("child/test2.txt"));
+
+ Expander expander = new Expander("", evaluate, false, false, false, false, false);
+ List<? extends CharSequence> files = expander.generateFileNames("~/*.[tx][v-z][!a]");
+ assertNotNull(files);
+ assertEquals(1, files.size());
+ assertEquals("test1.txt", home.relativize(Paths.get(files.get(0).toString())).toString());
+ }
+
+ private static void deleteRecursive(Path file) throws IOException {
+ if (file != null) {
+ if (Files.isDirectory(file)) {
+ for (Path child : Files.newDirectoryStream(file)) {
+ deleteRecursive(child);
+ }
+ }
+ Files.deleteIfExists(file);
+ }
+ }
+}
Added: felix/trunk/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/GlobPathMatcherTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/GlobPathMatcherTest.java?rev=1793780&view=auto
==============================================================================
--- felix/trunk/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/GlobPathMatcherTest.java (added)
+++ felix/trunk/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/GlobPathMatcherTest.java Thu May 4 11:26:26 2017
@@ -0,0 +1,343 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.gogo.runtime;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Part of these test case have been kindly borrowed from
+ * https://github.com/spring-projects/spring-framework/blob/master/spring-core/src/test/java/org/springframework/util/AntPathMatcherTests.java
+ */
+public class GlobPathMatcherTest {
+
+ @Test
+ public void isMatchWithCaseSensitiveWithDefaultPathSeparator() throws Exception {
+
+ final Builder.Matcher pathMatcher = new Builder().build();
+
+ // test exact matching
+ assertTrue(pathMatcher.isMatch("test", "test"));
+ assertTrue(pathMatcher.isMatch("/test", "/test"));
+ assertTrue(pathMatcher.isMatch("http://example.org", "http://example.org")); // SPR-14141
+ assertFalse(pathMatcher.isMatch("/test.jpg", "test.jpg"));
+ assertFalse(pathMatcher.isMatch("test", "/test"));
+ assertFalse(pathMatcher.isMatch("/test", "test"));
+
+ // test matching with ?'s
+ assertTrue(pathMatcher.isMatch("t?st", "test"));
+ assertTrue(pathMatcher.isMatch("??st", "test"));
+ assertTrue(pathMatcher.isMatch("tes?", "test"));
+ assertTrue(pathMatcher.isMatch("te??", "test"));
+ assertTrue(pathMatcher.isMatch("?es?", "test"));
+ assertFalse(pathMatcher.isMatch("tes?", "tes"));
+ assertFalse(pathMatcher.isMatch("tes?", "testt"));
+ assertFalse(pathMatcher.isMatch("tes?", "tsst"));
+
+ // test matching with *'s
+ assertTrue(pathMatcher.isMatch("*", "test"));
+ assertTrue(pathMatcher.isMatch("test*", "test"));
+ assertTrue(pathMatcher.isMatch("test*", "testTest"));
+ assertTrue(pathMatcher.isMatch("test/*", "test/Test"));
+ assertTrue(pathMatcher.isMatch("test/*", "test/t"));
+ assertTrue(pathMatcher.isMatch("test/*", "test/"));
+ assertTrue(pathMatcher.isMatch("*test*", "AnothertestTest"));
+ assertTrue(pathMatcher.isMatch("*test", "Anothertest"));
+ assertTrue(pathMatcher.isMatch("*.*", "test."));
+ assertTrue(pathMatcher.isMatch("*.*", "test.test"));
+ assertTrue(pathMatcher.isMatch("*.*", "test.test.test"));
+ assertTrue(pathMatcher.isMatch("test*aaa", "testblaaaa"));
+ assertFalse(pathMatcher.isMatch("test*", "tst"));
+ assertFalse(pathMatcher.isMatch("test*", "tsttest"));
+ assertFalse(pathMatcher.isMatch("test*", "test/"));
+ assertFalse(pathMatcher.isMatch("test*", "test/t"));
+ assertFalse(pathMatcher.isMatch("test/*", "test"));
+ assertFalse(pathMatcher.isMatch("*test*", "tsttst"));
+ assertFalse(pathMatcher.isMatch("*test", "tsttst"));
+ assertFalse(pathMatcher.isMatch("*.*", "tsttst"));
+ assertFalse(pathMatcher.isMatch("test*aaa", "test"));
+ assertFalse(pathMatcher.isMatch("test*aaa", "testblaaab"));
+
+ // test matching with ?'s and /'s
+ assertTrue(pathMatcher.isMatch("/?", "/a"));
+ assertTrue(pathMatcher.isMatch("/?/a", "/a/a"));
+ assertTrue(pathMatcher.isMatch("/a/?", "/a/b"));
+ assertTrue(pathMatcher.isMatch("/??/a", "/aa/a"));
+ assertTrue(pathMatcher.isMatch("/a/??", "/a/bb"));
+ assertTrue(pathMatcher.isMatch("/?", "/a"));
+
+ // test matching with **'s
+ assertTrue(pathMatcher.isMatch("/**", "/testing/testing"));
+ assertTrue(pathMatcher.isMatch("/*/**", "/testing/testing"));
+ assertTrue(pathMatcher.isMatch("/**/*", "/testing/testing"));
+ assertTrue(pathMatcher.isMatch("/bla/**/bla", "/bla/testing/testing/bla"));
+ assertTrue(pathMatcher.isMatch("/bla/**/bla", "/bla/testing/testing/bla/bla"));
+ assertTrue(pathMatcher.isMatch("/**/test", "/bla/bla/test"));
+ assertTrue(pathMatcher.isMatch("/bla/**/**/bla", "/bla/bla/bla/bla/bla/bla"));
+ assertTrue(pathMatcher.isMatch("/bla*bla/test", "/blaXXXbla/test"));
+ assertTrue(pathMatcher.isMatch("/*bla/test", "/XXXbla/test"));
+ assertFalse(pathMatcher.isMatch("/bla*bla/test", "/blaXXXbl/test"));
+ assertFalse(pathMatcher.isMatch("/*bla/test", "XXXblab/test"));
+ assertFalse(pathMatcher.isMatch("/*bla/test", "XXXbl/test"));
+
+ assertFalse(pathMatcher.isMatch("/????", "/bala/bla"));
+ assertFalse(pathMatcher.isMatch("/**/*bla", "/bla/bla/bla/bbb"));
+
+ assertTrue(pathMatcher.isMatch("/*bla*/**/bla/**", "/XXXblaXXXX/testing/testing/bla/testing/testing/"));
+ assertTrue(pathMatcher.isMatch("/*bla*/**/bla/*", "/XXXblaXXXX/testing/testing/bla/testing"));
+ assertTrue(pathMatcher.isMatch("/*bla*/**/bla/**", "/XXXblaXXXX/testing/testing/bla/testing/testing"));
+ assertTrue(pathMatcher.isMatch("/*bla*/**/bla/**", "/XXXblaXXXX/testing/testing/bla/testing/testing.jpg"));
+
+ assertTrue(pathMatcher.isMatch("*bla*/**/bla/**", "XXXblaXXXX/testing/testing/bla/testing/testing/"));
+ assertTrue(pathMatcher.isMatch("*bla*/**/bla/*", "XXXblaXXXX/testing/testing/bla/testing"));
+ assertTrue(pathMatcher.isMatch("*bla*/**/bla/**", "XXXblaXXXX/testing/testing/bla/testing/testing"));
+ assertFalse(pathMatcher.isMatch("*bla*/**/bla/*", "XXXblaXXXX/testing/testing/bla/testing/testing"));
+
+ assertFalse(pathMatcher.isMatch("/x/x/**/bla", "/x/x/x/"));
+
+ assertTrue(pathMatcher.isMatch("/foo/bar/**", "/foo/bar"));
+
+ assertTrue(pathMatcher.isMatch("", ""));
+
+ assertTrue(pathMatcher.isMatch("/foo/bar/**", "/foo/bar"));
+ assertTrue(pathMatcher.isMatch("/resource/1", "/resource/1"));
+ assertTrue(pathMatcher.isMatch("/resource/*", "/resource/1"));
+ assertTrue(pathMatcher.isMatch("/resource/*/", "/resource/1/"));
+ assertTrue(pathMatcher.isMatch("/top-resource/*/resource/*/sub-resource/*", "/top-resource/1/resource/2/sub-resource/3"));
+ assertTrue(pathMatcher.isMatch("/top-resource/*/resource/*/sub-resource/*", "/top-resource/999999/resource/8888888/sub-resource/77777777"));
+ assertTrue(pathMatcher.isMatch("/*/*/*/*/secret.html", "/this/is/protected/path/secret.html"));
+ assertTrue(pathMatcher.isMatch("/*/*/*/*/*.html", "/this/is/protected/path/secret.html"));
+ assertTrue(pathMatcher.isMatch("/*/*/*/*", "/this/is/protected/path"));
+ assertTrue(pathMatcher.isMatch("org/springframework/**/*.jsp", "org/springframework/web/views/hello.jsp"));
+ assertTrue(pathMatcher.isMatch("org/springframework/**/*.jsp", "org/springframework/web/default.jsp"));
+ assertTrue(pathMatcher.isMatch("org/springframework/**/*.jsp", "org/springframework/default.jsp"));
+ assertTrue(pathMatcher.isMatch("org/**/servlet/bla.jsp", "org/springframework/servlet/bla.jsp"));
+ assertTrue(pathMatcher.isMatch("org/**/servlet/bla.jsp", "org/springframework/testing/servlet/bla.jsp"));
+ assertTrue(pathMatcher.isMatch("org/**/servlet/bla.jsp", "org/servlet/bla.jsp"));
+ assertTrue(pathMatcher.isMatch("**/hello.jsp", "org/springframework/servlet/web/views/hello.jsp"));
+ assertTrue(pathMatcher.isMatch("**/**/hello.jsp", "org/springframework/servlet/web/views/hello.jsp"));
+
+ assertFalse(pathMatcher.isMatch("/foo/bar/**", "/foo /bar"));
+ assertFalse(pathMatcher.isMatch("/foo/bar/**", "/foo /bar"));
+ assertFalse(pathMatcher.isMatch("/foo/bar/**", "/foo / bar"));
+ assertFalse(pathMatcher.isMatch("/foo/bar/**", " / foo / bar"));
+ assertFalse(pathMatcher.isMatch("org/**/servlet/bla.jsp", " org / servlet / bla . jsp"));
+ }
+
+ @Test
+ public void isMatchWithCustomSeparator() throws Exception {
+ final Builder.Matcher pathMatcher = new Builder().withPathSeparator(".").build();
+
+ assertTrue(pathMatcher.isMatch(".foo.bar.**", ".foo.bar"));
+ assertTrue(pathMatcher.isMatch(".resource.1", ".resource.1"));
+ assertTrue(pathMatcher.isMatch(".resource.*", ".resource.1"));
+ assertTrue(pathMatcher.isMatch(".resource.*.", ".resource.1."));
+ assertTrue(pathMatcher.isMatch("org.springframework.**.*.jsp", "org.springframework.web.views.hello.jsp"));
+ assertTrue(pathMatcher.isMatch("org.springframework.**.*.jsp", "org.springframework.web.default.jsp"));
+ assertTrue(pathMatcher.isMatch("org.springframework.**.*.jsp", "org.springframework.default.jsp"));
+ assertTrue(pathMatcher.isMatch("org.**.servlet.bla.jsp", "org.springframework.servlet.bla.jsp"));
+ assertTrue(pathMatcher.isMatch("org.**.servlet.bla.jsp", "org.springframework.testing.servlet.bla.jsp"));
+ assertTrue(pathMatcher.isMatch("org.**.servlet.bla.jsp", "org.servlet.bla.jsp"));
+ assertTrue(pathMatcher.isMatch("http://example.org", "http://example.org"));
+ assertTrue(pathMatcher.isMatch("**.hello.jsp", "org.springframework.servlet.web.views.hello.jsp"));
+ assertTrue(pathMatcher.isMatch("**.**.hello.jsp", "org.springframework.servlet.web.views.hello.jsp"));
+
+ // test matching with ?'s and .'s
+ assertTrue(pathMatcher.isMatch(".?", ".a"));
+ assertTrue(pathMatcher.isMatch(".?.a", ".a.a"));
+ assertTrue(pathMatcher.isMatch(".a.?", ".a.b"));
+ assertTrue(pathMatcher.isMatch(".??.a", ".aa.a"));
+ assertTrue(pathMatcher.isMatch(".a.??", ".a.bb"));
+ assertTrue(pathMatcher.isMatch(".?", ".a"));
+
+ // test matching with **'s
+ assertTrue(pathMatcher.isMatch(".**", ".testing.testing"));
+ assertTrue(pathMatcher.isMatch(".*.**", ".testing.testing"));
+ assertTrue(pathMatcher.isMatch(".**.*", ".testing.testing"));
+ assertTrue(pathMatcher.isMatch(".bla.**.bla", ".bla.testing.testing.bla"));
+ assertTrue(pathMatcher.isMatch(".bla.**.bla", ".bla.testing.testing.bla.bla"));
+ assertTrue(pathMatcher.isMatch(".**.test", ".bla.bla.test"));
+ assertTrue(pathMatcher.isMatch(".bla.**.**.bla", ".bla.bla.bla.bla.bla.bla"));
+ assertFalse(pathMatcher.isMatch(".bla*bla.test", ".blaXXXbl.test"));
+ assertFalse(pathMatcher.isMatch(".*bla.test", "XXXblab.test"));
+ assertFalse(pathMatcher.isMatch(".*bla.test", "XXXbl.test"));
+ }
+
+ @Test
+ public void isMatchWithIgnoreCase() throws Exception {
+ final Builder.Matcher pathMatcher = new Builder().withIgnoreCase().build();
+
+ assertTrue(pathMatcher.isMatch("/foo/bar/**", "/FoO/baR"));
+ assertTrue(pathMatcher.isMatch("org/springframework/**/*.jsp", "ORG/SpringFramework/web/views/hello.JSP"));
+ assertTrue(pathMatcher.isMatch("org/**/servlet/bla.jsp", "Org/SERVLET/bla.jsp"));
+ assertTrue(pathMatcher.isMatch("/?", "/A"));
+ assertTrue(pathMatcher.isMatch("/?/a", "/a/A"));
+ assertTrue(pathMatcher.isMatch("/a/??", "/a/Bb"));
+ assertTrue(pathMatcher.isMatch("/?", "/a"));
+ assertTrue(pathMatcher.isMatch("/**", "/testing/teSting"));
+ assertTrue(pathMatcher.isMatch("/*/**", "/testing/testing"));
+ assertTrue(pathMatcher.isMatch("/**/*", "/tEsting/testinG"));
+ assertTrue(pathMatcher.isMatch("http://example.org", "HtTp://exAmple.org"));
+ assertTrue(pathMatcher.isMatch("HTTP://EXAMPLE.ORG", "HtTp://exAmple.org"));
+ }
+
+ @Test
+ public void isMatchWithIgnoreCaseWithCustomPathSeparator() throws Exception {
+ final Builder.Matcher pathMatcher = new Builder()
+ .withIgnoreCase()
+ .withPathSeparator(".").build();
+
+ assertTrue(pathMatcher.isMatch(".foo.bar.**", ".FoO.baR"));
+ assertTrue(pathMatcher.isMatch("org.springframework.**.*.jsp", "ORG.SpringFramework.web.views.hello.JSP"));
+ assertTrue(pathMatcher.isMatch("org.**.servlet.bla.jsp", "Org.SERVLET.bla.jsp"));
+ assertTrue(pathMatcher.isMatch(".?", ".A"));
+ assertTrue(pathMatcher.isMatch(".?.a", ".a.A"));
+ assertTrue(pathMatcher.isMatch(".a.??", ".a.Bb"));
+ assertTrue(pathMatcher.isMatch(".?", ".a"));
+ assertTrue(pathMatcher.isMatch(".**", ".testing.teSting"));
+ assertTrue(pathMatcher.isMatch(".*.**", ".testing.testing"));
+ assertTrue(pathMatcher.isMatch(".**.*", ".tEsting.testinG"));
+ assertTrue(pathMatcher.isMatch("http:..example.org", "HtTp:..exAmple.org"));
+ assertTrue(pathMatcher.isMatch("HTTP:..EXAMPLE.ORG", "HtTp:..exAmple.org"));
+ }
+
+ @Test
+ public void isMatchWithMatchStart() {
+ final Builder.Matcher pathMatcher = new Builder().withMatchStart().build();
+
+ // test exact matching
+ assertTrue(pathMatcher.isMatch("test", "test"));
+ assertTrue(pathMatcher.isMatch("/test", "/test"));
+ assertFalse(pathMatcher.isMatch("/test.jpg", "test.jpg"));
+ assertFalse(pathMatcher.isMatch("test", "/test"));
+ assertFalse(pathMatcher.isMatch("/test", "test"));
+
+ // test matching with ?'s
+ assertTrue(pathMatcher.isMatch("t?st", "test"));
+ assertTrue(pathMatcher.isMatch("??st", "test"));
+ assertTrue(pathMatcher.isMatch("tes?", "test"));
+ assertTrue(pathMatcher.isMatch("te??", "test"));
+ assertTrue(pathMatcher.isMatch("?es?", "test"));
+ assertFalse(pathMatcher.isMatch("tes?", "tes"));
+ assertFalse(pathMatcher.isMatch("tes?", "testt"));
+ assertFalse(pathMatcher.isMatch("tes?", "tsst"));
+
+ // test matching with *'s
+ assertTrue(pathMatcher.isMatch("*", "test"));
+ assertTrue(pathMatcher.isMatch("test*", "test"));
+ assertTrue(pathMatcher.isMatch("test*", "testTest"));
+ assertTrue(pathMatcher.isMatch("test/*", "test/Test"));
+ assertTrue(pathMatcher.isMatch("test/*", "test/t"));
+ assertTrue(pathMatcher.isMatch("test/*", "test/"));
+ assertTrue(pathMatcher.isMatch("*test*", "AnothertestTest"));
+ assertTrue(pathMatcher.isMatch("*test", "Anothertest"));
+ assertTrue(pathMatcher.isMatch("*.*", "test."));
+ assertTrue(pathMatcher.isMatch("*.*", "test.test"));
+ assertTrue(pathMatcher.isMatch("*.*", "test.test.test"));
+ assertTrue(pathMatcher.isMatch("test*aaa", "testblaaaa"));
+ assertFalse(pathMatcher.isMatch("test*", "tst"));
+ assertFalse(pathMatcher.isMatch("test*", "test/"));
+ assertFalse(pathMatcher.isMatch("test*", "tsttest"));
+ assertFalse(pathMatcher.isMatch("test*", "test/"));
+ assertFalse(pathMatcher.isMatch("test*", "test/t"));
+ assertTrue(pathMatcher.isMatch("test/*", "test"));
+ assertTrue(pathMatcher.isMatch("test/t*.txt", "test"));
+ assertFalse(pathMatcher.isMatch("*test*", "tsttst"));
+ assertFalse(pathMatcher.isMatch("*test", "tsttst"));
+ assertFalse(pathMatcher.isMatch("*.*", "tsttst"));
+ assertFalse(pathMatcher.isMatch("test*aaa", "test"));
+ assertFalse(pathMatcher.isMatch("test*aaa", "testblaaab"));
+
+ // test matching with ?'s and /'s
+ assertTrue(pathMatcher.isMatch("/?", "/a"));
+ assertTrue(pathMatcher.isMatch("/?/a", "/a/a"));
+ assertTrue(pathMatcher.isMatch("/a/?", "/a/b"));
+ assertTrue(pathMatcher.isMatch("/??/a", "/aa/a"));
+ assertTrue(pathMatcher.isMatch("/a/??", "/a/bb"));
+ assertTrue(pathMatcher.isMatch("/?", "/a"));
+
+ // test matching with **'s
+ assertTrue(pathMatcher.isMatch("/**", "/testing/testing"));
+ assertTrue(pathMatcher.isMatch("/*/**", "/testing/testing"));
+ assertTrue(pathMatcher.isMatch("/**/*", "/testing/testing"));
+ assertTrue(pathMatcher.isMatch("test*/**", "test/"));
+ assertTrue(pathMatcher.isMatch("test*/**", "test/t"));
+ assertTrue(pathMatcher.isMatch("/bla/**/bla", "/bla/testing/testing/bla"));
+ assertTrue(pathMatcher.isMatch("/bla/**/bla", "/bla/testing/testing/bla/bla"));
+ assertTrue(pathMatcher.isMatch("/**/test", "/bla/bla/test"));
+ assertTrue(pathMatcher.isMatch("/bla/**/**/bla", "/bla/bla/bla/bla/bla/bla"));
+ assertTrue(pathMatcher.isMatch("/bla*bla/test", "/blaXXXbla/test"));
+ assertTrue(pathMatcher.isMatch("/*bla/test", "/XXXbla/test"));
+ assertFalse(pathMatcher.isMatch("/bla*bla/test", "/blaXXXbl/test"));
+ assertFalse(pathMatcher.isMatch("/*bla/test", "XXXblab/test"));
+ assertFalse(pathMatcher.isMatch("/*bla/test", "XXXbl/test"));
+
+ assertFalse(pathMatcher.isMatch("/????", "/bala/bla"));
+ assertTrue(pathMatcher.isMatch("/**/*bla", "/bla/bla/bla/bbb"));
+
+ assertTrue(pathMatcher.isMatch("/*bla*/**/bla/**", "/XXXblaXXXX/testing/testing/bla/testing/testing/"));
+ assertTrue(pathMatcher.isMatch("/*bla*/**/bla/*", "/XXXblaXXXX/testing/testing/bla/testing"));
+ assertTrue(pathMatcher.isMatch("/*bla*/**/bla/**", "/XXXblaXXXX/testing/testing/bla/testing/testing"));
+ assertTrue(pathMatcher.isMatch("/*bla*/**/bla/**", "/XXXblaXXXX/testing/testing/bla/testing/testing.jpg"));
+
+ assertTrue(pathMatcher.isMatch("*bla*/**/bla/**", "XXXblaXXXX/testing/testing/bla/testing/testing/"));
+ assertTrue(pathMatcher.isMatch("*bla*/**/bla/*", "XXXblaXXXX/testing/testing/bla/testing"));
+ assertTrue(pathMatcher.isMatch("*bla*/**/bla/**", "XXXblaXXXX/testing/testing/bla/testing/testing"));
+ assertTrue(pathMatcher.isMatch("*bla*/**/bla/*", "XXXblaXXXX/testing/testing/bla/testing/testing"));
+
+ assertTrue(pathMatcher.isMatch("/x/x/**/bla", "/x/x/x/"));
+
+ assertTrue(pathMatcher.isMatch("", ""));
+ }
+
+ public static class Builder {
+
+ private boolean matchStart;
+ private boolean ignoreCase;
+ private String pathSeparator = GlobPathMatcher.DEFAULT_PATH_SEPARATOR;
+
+ public Matcher build() {
+ return new Matcher();
+ }
+
+ public Builder withMatchStart() {
+ this.matchStart = true;
+ return this;
+ }
+
+ public Builder withIgnoreCase() {
+ this.ignoreCase = true;
+ return this;
+ }
+
+ public Builder withPathSeparator(String sep) {
+ this.pathSeparator = sep;
+ return this;
+ }
+
+ private class Matcher {
+ public boolean isMatch(String pattern, String str) {
+ GlobPathMatcher matcher = new GlobPathMatcher(pattern, pathSeparator, !ignoreCase);
+ return matcher.matches(str, !matchStart);
+ }
+ }
+ }
+}
\ No newline at end of file