Introduce PathPatternParser for optimized path matching
This commit introduces a PathPatternParser which parses request pattern strings into PathPattern objects which can then be used to fast match incoming string paths. The parser and matching supports the syntax as described in SPR-14544. The code is optimized around the common usages of request patterns and is designed to create very little transient garbage when matching. Issue: SPR-14544
This commit is contained in:
committed by
Brian Clozel
parent
6f029392c7
commit
f58ffad939
@@ -0,0 +1,905 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.web.util.patterns;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Exercise matching of {@link PathPattern} objects.
|
||||
*
|
||||
* @author Andy Clement
|
||||
*/
|
||||
public class PathPatternMatcherTests {
|
||||
|
||||
@Test
|
||||
public void basicMatching() {
|
||||
checkMatches(null, null);
|
||||
checkMatches("", "");
|
||||
checkMatches("", null);
|
||||
checkNoMatch("/abc", null);
|
||||
checkMatches(null, "");
|
||||
checkNoMatch(null, "/abc");
|
||||
checkMatches("/", "/");
|
||||
checkNoMatch("/", "/a");
|
||||
checkMatches("f", "f");
|
||||
checkMatches("/foo", "/foo");
|
||||
checkMatches("/foo/", "/foo/");
|
||||
checkMatches("/foo/bar", "/foo/bar");
|
||||
checkMatches("foo/bar", "foo/bar");
|
||||
checkMatches("/foo/bar/", "/foo/bar/");
|
||||
checkMatches("foo/bar/", "foo/bar/");
|
||||
checkMatches("/foo/bar/woo", "/foo/bar/woo");
|
||||
checkNoMatch("foo", "foobar");
|
||||
checkMatches("/foo/bar", "/foo/bar");
|
||||
checkNoMatch("/foo/bar", "/foo/baz");
|
||||
// TODO Need more tests for escaped separators in path patterns and paths?
|
||||
checkMatches("/foo\\/bar","/foo\\/bar"); // chain string is Separator(/) Literal(foo\) Separator(/) Literal(bar)
|
||||
}
|
||||
|
||||
@Test
|
||||
public void questionMarks() {
|
||||
checkNoMatch("a", "ab");
|
||||
checkMatches("/f?o/bar", "/foo/bar");
|
||||
checkNoMatch("/foo/b2r", "/foo/bar");
|
||||
checkNoMatch("?", "te");
|
||||
checkMatches("?", "a");
|
||||
checkMatches("???", "abc");
|
||||
checkNoMatch("tes?", "te");
|
||||
checkNoMatch("tes?", "tes");
|
||||
checkNoMatch("tes?", "testt");
|
||||
checkNoMatch("tes?", "tsst");
|
||||
checkMatches(".?.a", ".a.a");
|
||||
checkNoMatch(".?.a", ".aba");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void captureTheRest() {
|
||||
checkCapture("/customer/{*something}", "/customer/99", "something", "/99");
|
||||
checkCapture("/customer/{*something}", "/customer/aa/bb/cc", "something",
|
||||
"/aa/bb/cc");
|
||||
checkCapture("/customer/{*something}", "/customer/", "something", "/");
|
||||
checkCapture("/customer/////{*something}", "/customer/", "something", "/");
|
||||
checkCapture("/customer/{*something}", "/customer//////99", "something", "/99");
|
||||
checkCapture("/customer///{*something}", "/customer//////99", "something", "/99");
|
||||
checkCapture("/customer/{*something}", "/customer", "something", "");
|
||||
checkCapture("/{*something}", "", "something", "");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleSelectorsInPattern() {
|
||||
checkMatches("///abc","/abc");
|
||||
checkMatches("//","/");
|
||||
checkMatches("abc","abc");
|
||||
checkMatches("///abc//d/e","/abc/d/e");
|
||||
checkMatches("///abc//{def}//////xyz","/abc/foo/xyz");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleSelectorsInPath() {
|
||||
checkMatches("/abc","////abc");
|
||||
checkMatches("/","//");
|
||||
checkMatches("/abc//def///ghi","/abc/def/ghi");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleSelectorsInPatternAndPath() {
|
||||
checkMatches("///one///two///three","//one/////two///////three");
|
||||
checkMatches("//one//two//three","/one/////two/three");
|
||||
checkCapture("///{foo}///bar","/one/bar","foo","one");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wildcards() {
|
||||
checkMatches("/*/bar", "/foo/bar");
|
||||
checkNoMatch("/*/bar", "/foo/baz");
|
||||
checkMatches("/f*/bar", "/foo/bar");
|
||||
checkMatches("/*/bar", "/foo/bar");
|
||||
checkMatches("/a*b*c*d/bar", "/abcd/bar");
|
||||
checkMatches("*a*", "testa");
|
||||
checkMatches("a/*","a/");
|
||||
checkMatches("a/*","a/a");
|
||||
checkNoMatch("a/*","a/a/");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void trailingSeparators() {
|
||||
checkNoMatch("aaa/", "aaa");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constrainedMatches() {
|
||||
checkCapture("{foo:[0-9]*}", "123", "foo", "123");
|
||||
checkNoMatch("{foo:[0-9]*}", "abc");
|
||||
checkNoMatch("/{foo:[0-9]*}", "abc");
|
||||
checkCapture("/*/{foo:....}/**","/foo/barg/foo","foo","barg");
|
||||
checkCapture("/*/{foo:....}/**","/foo/barg/abc/def/ghi","foo","barg");
|
||||
checkNoMatch("{foo:....}", "99");
|
||||
checkMatches("{foo:..}", "99");
|
||||
checkCapture("/{abc:\\{\\}}","/{}","abc","{}");
|
||||
checkCapture("/{abc:\\[\\]}","/[]","abc","[]");
|
||||
checkCapture("/{abc:\\\\\\\\}","/\\\\"); // this is fun...
|
||||
}
|
||||
|
||||
@Test
|
||||
public void antPathMatcherTests() {
|
||||
// test exact matching
|
||||
checkMatches("test", "test");
|
||||
checkMatches("/test", "/test");
|
||||
checkMatches("http://example.org", "http://example.org");
|
||||
checkNoMatch("/test.jpg", "test.jpg");
|
||||
checkNoMatch("test", "/test");
|
||||
checkNoMatch("/test", "test");
|
||||
|
||||
// test matching with ?'s
|
||||
checkMatches("t?st", "test");
|
||||
checkMatches("??st", "test");
|
||||
checkMatches("tes?", "test");
|
||||
checkMatches("te??", "test");
|
||||
checkMatches("?es?", "test");
|
||||
checkNoMatch("tes?", "tes");
|
||||
checkNoMatch("tes?", "testt");
|
||||
checkNoMatch("tes?", "tsst");
|
||||
|
||||
// test matching with *'s
|
||||
checkMatches("*", "test");
|
||||
checkMatches("test*", "test");
|
||||
checkMatches("test*", "testTest");
|
||||
checkMatches("test/*", "test/Test");
|
||||
checkMatches("test/*", "test/t");
|
||||
checkMatches("test/*", "test/");
|
||||
checkMatches("*test*", "AnothertestTest");
|
||||
checkMatches("*test", "Anothertest");
|
||||
checkMatches("*.*", "test.");
|
||||
checkMatches("*.*", "test.test");
|
||||
checkMatches("*.*", "test.test.test");
|
||||
checkMatches("test*aaa", "testblaaaa");
|
||||
checkNoMatch("test*", "tst");
|
||||
checkNoMatch("test*", "tsttest");
|
||||
checkNoMatch("test*", "test/");
|
||||
checkNoMatch("test*", "test/t");
|
||||
checkNoMatch("test/*", "test");
|
||||
checkNoMatch("*test*", "tsttst");
|
||||
checkNoMatch("*test", "tsttst");
|
||||
checkNoMatch("*.*", "tsttst");
|
||||
checkNoMatch("test*aaa", "test");
|
||||
checkNoMatch("test*aaa", "testblaaab");
|
||||
|
||||
// test matching with ?'s and /'s
|
||||
checkMatches("/?", "/a");
|
||||
checkMatches("/?/a", "/a/a");
|
||||
checkMatches("/a/?", "/a/b");
|
||||
checkMatches("/??/a", "/aa/a");
|
||||
checkMatches("/a/??", "/a/bb");
|
||||
checkMatches("/?", "/a");
|
||||
|
||||
checkMatches("/**", "");
|
||||
checkMatches("/books/**", "/books");
|
||||
checkMatches("/books////**", "/books");
|
||||
checkMatches("/books////**", "/books////");
|
||||
checkMatches("/**", "/testing/testing");
|
||||
checkMatches("/*/**", "/testing/testing");
|
||||
checkMatches("/bla*bla/test", "/blaXXXbla/test");
|
||||
checkMatches("/*bla/test", "/XXXbla/test");
|
||||
checkNoMatch("/bla*bla/test", "/blaXXXbl/test");
|
||||
checkNoMatch("/*bla/test", "XXXblab/test");
|
||||
checkNoMatch("/*bla/test", "XXXbl/test");
|
||||
checkNoMatch("/????", "/bala/bla");
|
||||
checkMatches("/foo/bar/**", "/foo/bar/");
|
||||
checkMatches("/{bla}.html", "/testing.html");
|
||||
checkCapture("/{bla}.*", "/testing.html", "bla", "testing");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchStart() {
|
||||
checkStartMatches("test/{a}_{b}/foo", "test/a_b");
|
||||
checkStartMatches("test/?/abc", "test/a");
|
||||
checkStartMatches("test/{*foobar}", "test/");
|
||||
checkStartMatches("test/*/bar", "test/a");
|
||||
checkStartMatches("test/{foo}/bar", "test/abc");
|
||||
checkStartMatches("test//foo", "test//");
|
||||
checkStartMatches("test/foo", "test/");
|
||||
checkStartMatches("test/*", "test/");
|
||||
checkStartMatches("test", "test");
|
||||
checkStartNoMatch("test", "tes");
|
||||
checkStartMatches("test/", "test");
|
||||
|
||||
// test exact matching
|
||||
checkStartMatches("test", "test");
|
||||
checkStartMatches("/test", "/test");
|
||||
checkStartNoMatch("/test.jpg", "test.jpg");
|
||||
checkStartNoMatch("test", "/test");
|
||||
checkStartNoMatch("/test", "test");
|
||||
|
||||
// test matching with ?'s
|
||||
checkStartMatches("t?st", "test");
|
||||
checkStartMatches("??st", "test");
|
||||
checkStartMatches("tes?", "test");
|
||||
checkStartMatches("te??", "test");
|
||||
checkStartMatches("?es?", "test");
|
||||
checkStartNoMatch("tes?", "tes");
|
||||
checkStartNoMatch("tes?", "testt");
|
||||
checkStartNoMatch("tes?", "tsst");
|
||||
|
||||
// test matching with *'s
|
||||
checkStartMatches("*", "test");
|
||||
checkStartMatches("test*", "test");
|
||||
checkStartMatches("test*", "testTest");
|
||||
checkStartMatches("test/*", "test/Test");
|
||||
checkStartMatches("test/*", "test/t");
|
||||
checkStartMatches("test/*", "test/");
|
||||
checkStartMatches("*test*", "AnothertestTest");
|
||||
checkStartMatches("*test", "Anothertest");
|
||||
checkStartMatches("*.*", "test.");
|
||||
checkStartMatches("*.*", "test.test");
|
||||
checkStartMatches("*.*", "test.test.test");
|
||||
checkStartMatches("test*aaa", "testblaaaa");
|
||||
checkStartNoMatch("test*", "tst");
|
||||
checkStartNoMatch("test*", "test/");
|
||||
checkStartNoMatch("test*", "tsttest");
|
||||
checkStartNoMatch("test*", "test/t");
|
||||
checkStartMatches("test/*", "test");
|
||||
checkStartMatches("test/t*.txt", "test");
|
||||
checkStartNoMatch("*test*", "tsttst");
|
||||
checkStartNoMatch("*test", "tsttst");
|
||||
checkStartNoMatch("*.*", "tsttst");
|
||||
checkStartNoMatch("test*aaa", "test");
|
||||
checkStartNoMatch("test*aaa", "testblaaab");
|
||||
|
||||
// test matching with ?'s and /'s
|
||||
checkStartMatches("/?", "/a");
|
||||
checkStartMatches("/?/a", "/a/a");
|
||||
checkStartMatches("/a/?", "/a/b");
|
||||
checkStartMatches("/??/a", "/aa/a");
|
||||
checkStartMatches("/a/??", "/a/bb");
|
||||
checkStartMatches("/?", "/a");
|
||||
|
||||
checkStartMatches("/**", "/testing/testing");
|
||||
checkStartMatches("/*/**", "/testing/testing");
|
||||
checkStartMatches("test*/**", "test/");
|
||||
checkStartMatches("test*/**", "test/t");
|
||||
checkStartMatches("/bla*bla/test", "/blaXXXbla/test");
|
||||
checkStartMatches("/*bla/test", "/XXXbla/test");
|
||||
checkStartNoMatch("/bla*bla/test", "/blaXXXbl/test");
|
||||
checkStartNoMatch("/*bla/test", "XXXblab/test");
|
||||
checkStartNoMatch("/*bla/test", "XXXbl/test");
|
||||
|
||||
checkStartNoMatch("/????", "/bala/bla");
|
||||
|
||||
checkStartMatches("/*bla*/*/bla/**",
|
||||
"/XXXblaXXXX/testing/bla/testing/testing/");
|
||||
checkStartMatches("/*bla*/*/bla/*",
|
||||
"/XXXblaXXXX/testing/bla/testing");
|
||||
checkStartMatches("/*bla*/*/bla/**",
|
||||
"/XXXblaXXXX/testing/bla/testing/testing");
|
||||
checkStartMatches("/*bla*/*/bla/**",
|
||||
"/XXXblaXXXX/testing/bla/testing/testing.jpg");
|
||||
|
||||
checkStartMatches("/abc/{foo}","/abc/def");
|
||||
checkStartNoMatch("/abc/{foo}","/abc/def/");
|
||||
checkStartMatches("/abc/{foo}/","/abc/def/");
|
||||
checkStartNoMatch("/abc/{foo}/","/abc/def/ghi");
|
||||
checkStartMatches("/abc/{foo}/","/abc/def");
|
||||
|
||||
checkStartMatches("", "");
|
||||
checkStartMatches("", null);
|
||||
checkStartMatches("/abc", null);
|
||||
checkStartMatches(null, "");
|
||||
checkStartMatches(null, null);
|
||||
checkStartNoMatch(null, "/abc");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void caseSensitivity() {
|
||||
PathPatternParser pp = new PathPatternParser();
|
||||
pp.setCaseSensitive(false);
|
||||
PathPattern p = pp.parse("abc");
|
||||
assertTrue(p.matches("AbC"));
|
||||
assertFalse(p.matches("def"));
|
||||
p = pp.parse("fOo");
|
||||
assertTrue(p.matches("FoO"));
|
||||
p = pp.parse("/fOo/bAr");
|
||||
assertTrue(p.matches("/FoO/BaR"));
|
||||
|
||||
pp = new PathPatternParser();
|
||||
pp.setCaseSensitive(true);
|
||||
p = pp.parse("abc");
|
||||
assertFalse(p.matches("AbC"));
|
||||
p = pp.parse("fOo");
|
||||
assertFalse(p.matches("FoO"));
|
||||
p = pp.parse("/fOo/bAr");
|
||||
assertFalse(p.matches("/FoO/BaR"));
|
||||
p = pp.parse("/fOO/bAr");
|
||||
assertTrue(p.matches("/fOO/bAr"));
|
||||
|
||||
pp = new PathPatternParser();
|
||||
pp.setCaseSensitive(false);
|
||||
p = pp.parse("{foo:[A-Z]*}");
|
||||
assertTrue(p.matches("abc"));
|
||||
assertTrue(p.matches("ABC"));
|
||||
|
||||
pp = new PathPatternParser();
|
||||
pp.setCaseSensitive(true);
|
||||
p = pp.parse("{foo:[A-Z]*}");
|
||||
assertFalse(p.matches("abc"));
|
||||
assertTrue(p.matches("ABC"));
|
||||
|
||||
pp = new PathPatternParser();
|
||||
pp.setCaseSensitive(false);
|
||||
p = pp.parse("ab?");
|
||||
assertTrue(p.matches("AbC"));
|
||||
p = pp.parse("fO?");
|
||||
assertTrue(p.matches("FoO"));
|
||||
p = pp.parse("/fO?/bA?");
|
||||
assertTrue(p.matches("/FoO/BaR"));
|
||||
assertFalse(p.matches("/bAr/fOo"));
|
||||
|
||||
pp = new PathPatternParser();
|
||||
pp.setCaseSensitive(true);
|
||||
p = pp.parse("ab?");
|
||||
assertFalse(p.matches("AbC"));
|
||||
p = pp.parse("fO?");
|
||||
assertFalse(p.matches("FoO"));
|
||||
p = pp.parse("/fO?/bA?");
|
||||
assertFalse(p.matches("/FoO/BaR"));
|
||||
p = pp.parse("/fO?/bA?");
|
||||
assertTrue(p.matches("/fOO/bAr"));
|
||||
|
||||
pp = new PathPatternParser();
|
||||
pp.setCaseSensitive(false);
|
||||
p = pp.parse("{abc:[A-Z]*}_{def:[A-Z]*}");
|
||||
assertTrue(p.matches("abc_abc"));
|
||||
assertTrue(p.matches("ABC_aBc"));
|
||||
|
||||
pp = new PathPatternParser();
|
||||
pp.setCaseSensitive(true);
|
||||
p = pp.parse("{abc:[A-Z]*}_{def:[A-Z]*}");
|
||||
assertFalse(p.matches("abc_abc"));
|
||||
assertTrue(p.matches("ABC_ABC"));
|
||||
|
||||
pp = new PathPatternParser();
|
||||
pp.setCaseSensitive(false);
|
||||
p = pp.parse("*?a?*");
|
||||
assertTrue(p.matches("bab"));
|
||||
assertTrue(p.matches("bAb"));
|
||||
|
||||
pp = new PathPatternParser();
|
||||
pp.setCaseSensitive(true);
|
||||
p = pp.parse("*?A?*");
|
||||
assertFalse(p.matches("bab"));
|
||||
assertTrue(p.matches("bAb"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void alternativeDelimiter() {
|
||||
try {
|
||||
separator = '.';
|
||||
|
||||
// test exact matching
|
||||
checkMatches("test", "test");
|
||||
checkMatches(".test", ".test");
|
||||
checkNoMatch(".test/jpg", "test/jpg");
|
||||
checkNoMatch("test", ".test");
|
||||
checkNoMatch(".test", "test");
|
||||
|
||||
// test matching with ?'s
|
||||
checkMatches("t?st", "test");
|
||||
checkMatches("??st", "test");
|
||||
checkMatches("tes?", "test");
|
||||
checkMatches("te??", "test");
|
||||
checkMatches("?es?", "test");
|
||||
checkNoMatch("tes?", "tes");
|
||||
checkNoMatch("tes?", "testt");
|
||||
checkNoMatch("tes?", "tsst");
|
||||
|
||||
// test matching with *'s
|
||||
checkMatches("*", "test");
|
||||
checkMatches("test*", "test");
|
||||
checkMatches("test*", "testTest");
|
||||
checkMatches("*test*", "AnothertestTest");
|
||||
checkMatches("*test", "Anothertest");
|
||||
checkMatches("*/*", "test/");
|
||||
checkMatches("*/*", "test/test");
|
||||
checkMatches("*/*", "test/test/test");
|
||||
checkMatches("test*aaa", "testblaaaa");
|
||||
checkNoMatch("test*", "tst");
|
||||
checkNoMatch("test*", "tsttest");
|
||||
checkNoMatch("*test*", "tsttst");
|
||||
checkNoMatch("*test", "tsttst");
|
||||
checkNoMatch("*/*", "tsttst");
|
||||
checkNoMatch("test*aaa", "test");
|
||||
checkNoMatch("test*aaa", "testblaaab");
|
||||
|
||||
// test matching with ?'s and .'s
|
||||
checkMatches(".?", ".a");
|
||||
checkMatches(".?.a", ".a.a");
|
||||
checkMatches(".a.?", ".a.b");
|
||||
checkMatches(".??.a", ".aa.a");
|
||||
checkMatches(".a.??", ".a.bb");
|
||||
checkMatches(".?", ".a");
|
||||
|
||||
// test matching with **'s
|
||||
checkMatches(".**", ".testing.testing");
|
||||
checkMatches(".*.**", ".testing.testing");
|
||||
checkMatches(".bla*bla.test", ".blaXXXbla.test");
|
||||
checkMatches(".*bla.test", ".XXXbla.test");
|
||||
checkNoMatch(".bla*bla.test", ".blaXXXbl.test");
|
||||
checkNoMatch(".*bla.test", "XXXblab.test");
|
||||
checkNoMatch(".*bla.test", "XXXbl.test");
|
||||
}
|
||||
finally {
|
||||
separator = PathPatternParser.DEFAULT_SEPARATOR;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractPathWithinPattern() throws Exception {
|
||||
checkExtractPathWithinPattern("/welcome*/", "/welcome/","welcome");
|
||||
checkExtractPathWithinPattern("/docs/commit.html","/docs/commit.html","");
|
||||
checkExtractPathWithinPattern("/docs/*","/docs/cvs/commit","cvs/commit");
|
||||
checkExtractPathWithinPattern("/docs/cvs/*.html","/docs/cvs/commit.html","commit.html");
|
||||
checkExtractPathWithinPattern("/docs/**","/docs/cvs/commit","cvs/commit");
|
||||
checkExtractPathWithinPattern("/doo/{*foobar}","/doo/customer.html","customer.html");
|
||||
checkExtractPathWithinPattern("/doo/{*foobar}","/doo/daa/customer.html","daa/customer.html");
|
||||
checkExtractPathWithinPattern("/*.html","/commit.html","commit.html");
|
||||
checkExtractPathWithinPattern("/docs/*/*/*/*","/docs/cvs/other/commit.html","cvs/other/commit.html");
|
||||
checkExtractPathWithinPattern("/d?cs/**","/docs/cvs/commit","docs/cvs/commit");
|
||||
checkExtractPathWithinPattern("/docs/c?s/*.html","/docs/cvs/commit.html","cvs/commit.html");
|
||||
checkExtractPathWithinPattern("/d?cs/*/*.html","/docs/cvs/commit.html","docs/cvs/commit.html");
|
||||
checkExtractPathWithinPattern("/a/b/c*d*/*.html","/a/b/cod/foo.html","cod/foo.html");
|
||||
checkExtractPathWithinPattern("a/{foo}/b/{bar}","a/c/b/d","c/b/d");
|
||||
checkExtractPathWithinPattern("a/{foo}_{bar}/d/e","a/b_c/d/e","b_c/d/e");
|
||||
checkExtractPathWithinPattern("aaa//*///ccc///ddd","aaa/bbb/ccc/ddd","bbb/ccc/ddd");
|
||||
checkExtractPathWithinPattern("aaa/*/ccc/ddd","aaa//bbb//ccc/ddd","bbb/ccc/ddd");
|
||||
checkExtractPathWithinPattern("aaa//*///ccc///ddd","aaa//bbb//ccc/ddd","bbb/ccc/ddd");
|
||||
checkExtractPathWithinPattern("aaa//*///ccc///ddd","aaa/////bbb//ccc/ddd","bbb/ccc/ddd");
|
||||
checkExtractPathWithinPattern("aaa/c*/ddd/","aaa/ccc///ddd///","ccc/ddd");
|
||||
checkExtractPathWithinPattern("", "", "");
|
||||
checkExtractPathWithinPattern("/", "", "");
|
||||
checkExtractPathWithinPattern("", "/", "");
|
||||
checkExtractPathWithinPattern("//", "", "");
|
||||
checkExtractPathWithinPattern("", "//", "");
|
||||
checkExtractPathWithinPattern("//", "//", "");
|
||||
checkExtractPathWithinPattern("//", "/", "");
|
||||
checkExtractPathWithinPattern("/", "//", "");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractUriTemplateVariables() throws Exception {
|
||||
checkCapture("/hotels/{hotel}", "/hotels/1", "hotel","1");
|
||||
checkCapture("/h?tels/{hotel}","/hotels/1","hotel","1");
|
||||
checkCapture("/hotels/{hotel}/bookings/{booking}","/hotels/1/bookings/2","hotel","1","booking","2");
|
||||
checkCapture("/*/hotels/*/{hotel}","/foo/hotels/bar/1","hotel","1");
|
||||
checkCapture("/{page}.html","/42.html","page","42");
|
||||
checkCapture("/{page}.*","/42.html","page","42");
|
||||
checkCapture("/A-{B}-C","/A-b-C","B","b");
|
||||
checkCapture("/{name}.{extension}","/test.html","name","test","extension","html");
|
||||
try {
|
||||
checkCapture("/{one}/", "//", "one", "");
|
||||
fail("Expected exception");
|
||||
} catch (IllegalStateException e) {
|
||||
assertEquals("Pattern \"/{one}/\" is not a match for \"//\"",e.getMessage());
|
||||
}
|
||||
try {
|
||||
checkCapture("", "/abc");
|
||||
fail("Expected exception");
|
||||
} catch (IllegalStateException e) {
|
||||
assertEquals("Pattern \"\" is not a match for \"/abc\"",e.getMessage());
|
||||
}
|
||||
assertEquals(0,checkCapture("", "").size());
|
||||
checkCapture("{id}", "99", "id", "99");
|
||||
checkCapture("/customer/{customerId}", "/customer/78", "customerId", "78");
|
||||
checkCapture("/customer/{customerId}/banana", "/customer/42/banana", "customerId",
|
||||
"42");
|
||||
checkCapture("{id}/{id2}", "99/98", "id", "99", "id2", "98");
|
||||
checkCapture("/foo/{bar}/boo/{baz}", "/foo/plum/boo/apple", "bar", "plum", "baz",
|
||||
"apple");
|
||||
checkCapture("/{bla}.*", "/testing.html", "bla", "testing");
|
||||
Map<String,String> extracted = checkCapture("/abc","/abc");
|
||||
assertEquals(0,extracted.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractUriTemplateVariablesRegex() {
|
||||
PathPatternParser pp = new PathPatternParser();
|
||||
PathPattern p = null;
|
||||
|
||||
p = pp.parse("{symbolicName:[\\w\\.]+}-{version:[\\w\\.]+}.jar");
|
||||
Map<String, String> result = p.matchAndExtract("com.example-1.0.0.jar");
|
||||
assertEquals("com.example", result.get("symbolicName"));
|
||||
assertEquals("1.0.0", result.get("version"));
|
||||
|
||||
p = pp.parse("{symbolicName:[\\w\\.]+}-sources-{version:[\\w\\.]+}.jar");
|
||||
result = p.matchAndExtract("com.example-sources-1.0.0.jar");
|
||||
assertEquals("com.example", result.get("symbolicName"));
|
||||
assertEquals("1.0.0", result.get("version"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractUriTemplateVarsRegexQualifiers() {
|
||||
PathPatternParser pp = new PathPatternParser();
|
||||
|
||||
PathPattern p = pp.parse("{symbolicName:[\\p{L}\\.]+}-sources-{version:[\\p{N}\\.]+}.jar");
|
||||
Map<String, String> result = p.matchAndExtract("com.example-sources-1.0.0.jar");
|
||||
assertEquals("com.example", result.get("symbolicName"));
|
||||
assertEquals("1.0.0", result.get("version"));
|
||||
|
||||
p = pp.parse("{symbolicName:[\\w\\.]+}-sources-{version:[\\d\\.]+}-{year:\\d{4}}{month:\\d{2}}{day:\\d{2}}.jar");
|
||||
result = p.matchAndExtract("com.example-sources-1.0.0-20100220.jar");
|
||||
assertEquals("com.example", result.get("symbolicName"));
|
||||
assertEquals("1.0.0", result.get("version"));
|
||||
assertEquals("2010", result.get("year"));
|
||||
assertEquals("02", result.get("month"));
|
||||
assertEquals("20", result.get("day"));
|
||||
|
||||
p = pp.parse("{symbolicName:[\\p{L}\\.]+}-sources-{version:[\\p{N}\\.\\{\\}]+}.jar");
|
||||
result = p.matchAndExtract("com.example-sources-1.0.0.{12}.jar");
|
||||
assertEquals("com.example", result.get("symbolicName"));
|
||||
assertEquals("1.0.0.{12}", result.get("version"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractUriTemplateVarsRegexCapturingGroups() {
|
||||
PathPatternParser pp = new PathPatternParser();
|
||||
PathPattern pathMatcher = pp.parse("/web/{id:foo(bar)?}_{goo}");
|
||||
exception.expect(IllegalArgumentException.class);
|
||||
exception.expectMessage(containsString("The number of capturing groups in the pattern"));
|
||||
pathMatcher.matchAndExtract("/web/foobar_goo");
|
||||
}
|
||||
|
||||
@Rule
|
||||
public final ExpectedException exception = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void combine() {
|
||||
TestPathCombiner pathMatcher = new TestPathCombiner();
|
||||
assertEquals("", pathMatcher.combine(null, null));
|
||||
assertEquals("/hotels", pathMatcher.combine("/hotels", null));
|
||||
assertEquals("/hotels", pathMatcher.combine(null, "/hotels"));
|
||||
assertEquals("/hotels/booking", pathMatcher.combine("/hotels/*", "booking"));
|
||||
assertEquals("/hotels/booking", pathMatcher.combine("/hotels/*", "/booking"));
|
||||
assertEquals("/hotels/**/booking", pathMatcher.combine("/hotels/**", "booking"));
|
||||
assertEquals("/hotels/**/booking", pathMatcher.combine("/hotels/**", "/booking"));
|
||||
assertEquals("/hotels/booking", pathMatcher.combine("/hotels", "/booking"));
|
||||
assertEquals("/hotels/booking", pathMatcher.combine("/hotels", "booking"));
|
||||
assertEquals("/hotels/booking", pathMatcher.combine("/hotels/", "booking"));
|
||||
assertEquals("/hotels/{hotel}", pathMatcher.combine("/hotels/*", "{hotel}"));
|
||||
assertEquals("/hotels/**/{hotel}", pathMatcher.combine("/hotels/**", "{hotel}"));
|
||||
assertEquals("/hotels/{hotel}", pathMatcher.combine("/hotels", "{hotel}"));
|
||||
assertEquals("/hotels/{hotel}.*", pathMatcher.combine("/hotels", "{hotel}.*"));
|
||||
assertEquals("/hotels/*/booking/{booking}",
|
||||
pathMatcher.combine("/hotels/*/booking", "{booking}"));
|
||||
assertEquals("/hotel.html", pathMatcher.combine("/*.html", "/hotel.html"));
|
||||
assertEquals("/hotel.html", pathMatcher.combine("/*.html", "/hotel"));
|
||||
assertEquals("/hotel.html", pathMatcher.combine("/*.html", "/hotel.*"));
|
||||
// TODO this seems rather bogus, should we eagerly show an error?
|
||||
assertEquals("/d/e/f/hotel.html", pathMatcher.combine("/a/b/c/*.html", "/d/e/f/hotel.*"));
|
||||
assertEquals("/*.html", pathMatcher.combine("/**", "/*.html"));
|
||||
assertEquals("/*.html", pathMatcher.combine("/*", "/*.html"));
|
||||
assertEquals("/*.html", pathMatcher.combine("/*.*", "/*.html"));
|
||||
assertEquals("/{foo}/bar", pathMatcher.combine("/{foo}", "/bar")); // SPR-8858
|
||||
assertEquals("/user/user", pathMatcher.combine("/user", "/user")); // SPR-7970
|
||||
assertEquals("/{foo:.*[^0-9].*}/edit/",
|
||||
pathMatcher.combine("/{foo:.*[^0-9].*}", "/edit/")); // SPR-10062
|
||||
assertEquals("/1.0/foo/test", pathMatcher.combine("/1.0", "/foo/test"));
|
||||
// SPR-10554
|
||||
assertEquals("/hotel", pathMatcher.combine("/", "/hotel")); // SPR-12975
|
||||
assertEquals("/hotel/booking", pathMatcher.combine("/hotel/", "/booking")); // SPR-12975
|
||||
assertEquals("",pathMatcher.combine(null, null));
|
||||
assertEquals("",pathMatcher.combine(null, ""));
|
||||
assertEquals("",pathMatcher.combine("",null));
|
||||
assertEquals("",pathMatcher.combine(null, null));
|
||||
assertEquals("",pathMatcher.combine("", ""));
|
||||
assertEquals("/hotel",pathMatcher.combine("", "/hotel"));
|
||||
assertEquals("/hotel",pathMatcher.combine("/hotel", null));
|
||||
assertEquals("/hotel",pathMatcher.combine("/hotel", ""));
|
||||
// TODO Do we need special handling when patterns contain multiple dots?
|
||||
}
|
||||
|
||||
@Test
|
||||
public void combineWithTwoFileExtensionPatterns() {
|
||||
TestPathCombiner pathMatcher = new TestPathCombiner();
|
||||
exception.expect(IllegalArgumentException.class);
|
||||
pathMatcher.combine("/*.html", "/*.txt");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void patternComparator() {
|
||||
Comparator<PathPattern> comparator = new PatternComparatorConsideringPath(
|
||||
"/hotels/new");
|
||||
|
||||
assertEquals(0, comparator.compare(null, null));
|
||||
assertEquals(1, comparator.compare(null, parse("/hotels/new")));
|
||||
assertEquals(-1, comparator.compare(parse("/hotels/new"), null));
|
||||
|
||||
assertEquals(0, comparator.compare(parse("/hotels/new"), parse("/hotels/new")));
|
||||
|
||||
assertEquals(-1, comparator.compare(parse("/hotels/new"), parse("/hotels/*")));
|
||||
assertEquals(1, comparator.compare(parse("/hotels/*"), parse("/hotels/new")));
|
||||
assertEquals(0, comparator.compare(parse("/hotels/*"), parse("/hotels/*")));
|
||||
|
||||
assertEquals(-1,
|
||||
comparator.compare(parse("/hotels/new"), parse("/hotels/{hotel}")));
|
||||
assertEquals(1,
|
||||
comparator.compare(parse("/hotels/{hotel}"), parse("/hotels/new")));
|
||||
assertEquals(0,
|
||||
comparator.compare(parse("/hotels/{hotel}"), parse("/hotels/{hotel}")));
|
||||
assertEquals(-1, comparator.compare(parse("/hotels/{hotel}/booking"),
|
||||
parse("/hotels/{hotel}/bookings/{booking}")));
|
||||
assertEquals(1, comparator.compare(parse("/hotels/{hotel}/bookings/{booking}"),
|
||||
parse("/hotels/{hotel}/booking")));
|
||||
|
||||
assertEquals(-1,
|
||||
comparator.compare(
|
||||
parse("/hotels/{hotel}/bookings/{booking}/cutomers/{customer}"),
|
||||
parse("/**")));
|
||||
assertEquals(1, comparator.compare(parse("/**"),
|
||||
parse("/hotels/{hotel}/bookings/{booking}/cutomers/{customer}")));
|
||||
assertEquals(0, comparator.compare(parse("/**"), parse("/**")));
|
||||
|
||||
assertEquals(-1,
|
||||
comparator.compare(parse("/hotels/{hotel}"), parse("/hotels/*")));
|
||||
assertEquals(1, comparator.compare(parse("/hotels/*"), parse("/hotels/{hotel}")));
|
||||
|
||||
assertEquals(-1, comparator.compare(parse("/hotels/*"), parse("/hotels/*/**")));
|
||||
assertEquals(1, comparator.compare(parse("/hotels/*/**"), parse("/hotels/*")));
|
||||
|
||||
assertEquals(-1,
|
||||
comparator.compare(parse("/hotels/new"), parse("/hotels/new.*")));
|
||||
|
||||
// SPR-6741
|
||||
assertEquals(-1,
|
||||
comparator.compare(
|
||||
parse("/hotels/{hotel}/bookings/{booking}/cutomers/{customer}"),
|
||||
parse("/hotels/**")));
|
||||
assertEquals(1, comparator.compare(parse("/hotels/**"),
|
||||
parse("/hotels/{hotel}/bookings/{booking}/cutomers/{customer}")));
|
||||
assertEquals(1, comparator.compare(parse("/hotels/foo/bar/**"),
|
||||
parse("/hotels/{hotel}")));
|
||||
assertEquals(-1, comparator.compare(parse("/hotels/{hotel}"),
|
||||
parse("/hotels/foo/bar/**")));
|
||||
|
||||
// SPR-8683
|
||||
assertEquals(1, comparator.compare(parse("/**"), parse("/hotels/{hotel}")));
|
||||
|
||||
// longer is better
|
||||
assertEquals(1, comparator.compare(parse("/hotels"), parse("/hotels2")));
|
||||
|
||||
// SPR-13139
|
||||
assertEquals(-1, comparator.compare(parse("*"), parse("*/**")));
|
||||
assertEquals(1, comparator.compare(parse("*/**"), parse("*")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pathPatternComparator() {
|
||||
PathPatternComparator ppc = new PathPatternComparator();
|
||||
assertEquals(0,ppc.compare(null, null));
|
||||
assertEquals(1,ppc.compare(null, parse("")));
|
||||
assertEquals(-1,ppc.compare(parse(""), null));
|
||||
assertEquals(0,ppc.compare(parse(""), parse("")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void patternCompareTo() {
|
||||
PathPatternParser p = new PathPatternParser();
|
||||
PathPattern pp = p.parse("/abc");
|
||||
assertEquals(-1,pp.compareTo(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void patternComparatorSort() {
|
||||
Comparator<PathPattern> comparator = new PatternComparatorConsideringPath(
|
||||
"/hotels/new");
|
||||
List<PathPattern> paths = new ArrayList<>(3);
|
||||
PathPatternParser pp = new PathPatternParser();
|
||||
paths.add(null);
|
||||
paths.add(pp.parse("/hotels/new"));
|
||||
Collections.sort(paths, comparator);
|
||||
assertEquals("/hotels/new", paths.get(0).getPatternString());
|
||||
assertNull(paths.get(1));
|
||||
paths.clear();
|
||||
|
||||
paths.add(pp.parse("/hotels/new"));
|
||||
paths.add(null);
|
||||
Collections.sort(paths, comparator);
|
||||
assertEquals("/hotels/new", paths.get(0).getPatternString());
|
||||
assertNull(paths.get(1));
|
||||
paths.clear();
|
||||
|
||||
paths.add(pp.parse("/hotels/*"));
|
||||
paths.add(pp.parse("/hotels/new"));
|
||||
Collections.sort(paths, comparator);
|
||||
assertEquals("/hotels/new", paths.get(0).getPatternString());
|
||||
assertEquals("/hotels/*", paths.get(1).getPatternString());
|
||||
paths.clear();
|
||||
|
||||
paths.add(pp.parse("/hotels/new"));
|
||||
paths.add(pp.parse("/hotels/*"));
|
||||
Collections.sort(paths, comparator);
|
||||
assertEquals("/hotels/new", paths.get(0).getPatternString());
|
||||
assertEquals("/hotels/*", paths.get(1).getPatternString());
|
||||
paths.clear();
|
||||
|
||||
paths.add(pp.parse("/hotels/**"));
|
||||
paths.add(pp.parse("/hotels/*"));
|
||||
Collections.sort(paths, comparator);
|
||||
assertEquals("/hotels/*", paths.get(0).getPatternString());
|
||||
assertEquals("/hotels/**", paths.get(1).getPatternString());
|
||||
paths.clear();
|
||||
|
||||
paths.add(pp.parse("/hotels/*"));
|
||||
paths.add(pp.parse("/hotels/**"));
|
||||
Collections.sort(paths, comparator);
|
||||
assertEquals("/hotels/*", paths.get(0).getPatternString());
|
||||
assertEquals("/hotels/**", paths.get(1).getPatternString());
|
||||
paths.clear();
|
||||
|
||||
paths.add(pp.parse("/hotels/{hotel}"));
|
||||
paths.add(pp.parse("/hotels/new"));
|
||||
Collections.sort(paths, comparator);
|
||||
assertEquals("/hotels/new", paths.get(0).getPatternString());
|
||||
assertEquals("/hotels/{hotel}", paths.get(1).getPatternString());
|
||||
paths.clear();
|
||||
|
||||
paths.add(pp.parse("/hotels/new"));
|
||||
paths.add(pp.parse("/hotels/{hotel}"));
|
||||
Collections.sort(paths, comparator);
|
||||
assertEquals("/hotels/new", paths.get(0).getPatternString());
|
||||
assertEquals("/hotels/{hotel}", paths.get(1).getPatternString());
|
||||
paths.clear();
|
||||
|
||||
paths.add(pp.parse("/hotels/*"));
|
||||
paths.add(pp.parse("/hotels/{hotel}"));
|
||||
paths.add(pp.parse("/hotels/new"));
|
||||
Collections.sort(paths, comparator);
|
||||
assertEquals("/hotels/new", paths.get(0).getPatternString());
|
||||
assertEquals("/hotels/{hotel}", paths.get(1).getPatternString());
|
||||
assertEquals("/hotels/*", paths.get(2).getPatternString());
|
||||
paths.clear();
|
||||
|
||||
paths.add(pp.parse("/hotels/ne*"));
|
||||
paths.add(pp.parse("/hotels/n*"));
|
||||
Collections.shuffle(paths);
|
||||
Collections.sort(paths, comparator);
|
||||
assertEquals("/hotels/ne*", paths.get(0).getPatternString());
|
||||
assertEquals("/hotels/n*", paths.get(1).getPatternString());
|
||||
paths.clear();
|
||||
|
||||
// comparator = new PatternComparatorConsideringPath("/hotels/new.html");
|
||||
// paths.add(pp.parse("/hotels/new.*"));
|
||||
// paths.add(pp.parse("/hotels/{hotel}"));
|
||||
// Collections.shuffle(paths);
|
||||
// Collections.sort(paths, comparator);
|
||||
// assertEquals("/hotels/new.*", paths.get(0).toPatternString());
|
||||
// assertEquals("/hotels/{hotel}", paths.get(1).toPatternString());
|
||||
// paths.clear();
|
||||
|
||||
comparator = new PatternComparatorConsideringPath("/web/endUser/action/login.html");
|
||||
paths.add(pp.parse("/*/login.*"));
|
||||
paths.add(pp.parse("/*/endUser/action/login.*"));
|
||||
Collections.sort(paths, comparator);
|
||||
assertEquals("/*/endUser/action/login.*", paths.get(0).getPatternString());
|
||||
assertEquals("/*/login.*", paths.get(1).getPatternString());
|
||||
paths.clear();
|
||||
}
|
||||
|
||||
@Test // SPR-13286
|
||||
public void caseInsensitive() {
|
||||
PathPatternParser pp = new PathPatternParser();
|
||||
pp.setCaseSensitive(false);
|
||||
PathPattern p = pp.parse("/group/{groupName}/members");
|
||||
assertTrue(p.matches("/group/sales/members"));
|
||||
assertTrue(p.matches("/Group/Sales/Members"));
|
||||
assertTrue(p.matches("/group/Sales/members"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void patternmessage() {
|
||||
PatternMessage[] values = PatternMessage.values();
|
||||
assertNotNull(values);
|
||||
for (PatternMessage pm: values) {
|
||||
String name = pm.toString();
|
||||
assertEquals(pm.ordinal(),PatternMessage.valueOf(name).ordinal());
|
||||
}
|
||||
}
|
||||
|
||||
private PathPattern parse(String path) {
|
||||
PathPatternParser pp = new PathPatternParser();
|
||||
return pp.parse(path);
|
||||
}
|
||||
|
||||
private char separator = PathPatternParser.DEFAULT_SEPARATOR;
|
||||
|
||||
private void checkMatches(String uriTemplate, String path) {
|
||||
PathPatternParser parser = (separator == PathPatternParser.DEFAULT_SEPARATOR
|
||||
? new PathPatternParser() : new PathPatternParser(separator));
|
||||
PathPattern p = parser.parse(uriTemplate);
|
||||
assertTrue(p.matches(path));
|
||||
}
|
||||
|
||||
private void checkStartNoMatch(String uriTemplate, String path) {
|
||||
PathPatternParser p = new PathPatternParser();
|
||||
PathPattern pattern = p.parse(uriTemplate);
|
||||
assertFalse(pattern.matchStart(path));
|
||||
}
|
||||
|
||||
private void checkStartMatches(String uriTemplate, String path) {
|
||||
PathPatternParser p = new PathPatternParser();
|
||||
PathPattern pattern = p.parse(uriTemplate);
|
||||
assertTrue(pattern.matchStart(path));
|
||||
}
|
||||
|
||||
private void checkNoMatch(String uriTemplate, String path) {
|
||||
PathPatternParser p = new PathPatternParser();
|
||||
PathPattern pattern = p.parse(uriTemplate);
|
||||
assertFalse(pattern.matches(path));
|
||||
}
|
||||
|
||||
private Map<String,String> checkCapture(String uriTemplate, String path, String... keyValues) {
|
||||
PathPatternParser parser = new PathPatternParser();
|
||||
PathPattern pattern = parser.parse(uriTemplate);
|
||||
Map<String, String> matchResults = pattern.matchAndExtract(path);
|
||||
Map<String, String> expectedKeyValues = new HashMap<>();
|
||||
if (keyValues != null) {
|
||||
for (int i = 0; i < keyValues.length; i += 2) {
|
||||
expectedKeyValues.put(keyValues[i], keyValues[i + 1]);
|
||||
}
|
||||
}
|
||||
Map<String, String> capturedVariables = matchResults;
|
||||
for (Map.Entry<String, String> me : expectedKeyValues.entrySet()) {
|
||||
String value = capturedVariables.get(me.getKey());
|
||||
if (value == null) {
|
||||
fail("Did not find key '" + me.getKey() + "' in captured variables: "
|
||||
+ capturedVariables);
|
||||
}
|
||||
if (!value.equals(me.getValue())) {
|
||||
fail("Expected value '" + me.getValue() + "' for key '" + me.getKey()
|
||||
+ "' but was '" + value + "'");
|
||||
}
|
||||
}
|
||||
return capturedVariables;
|
||||
}
|
||||
|
||||
private void checkExtractPathWithinPattern(String pattern, String path, String expected) {
|
||||
PathPatternParser ppp = new PathPatternParser();
|
||||
PathPattern pp = ppp.parse(pattern);
|
||||
String s = pp.extractPathWithinPattern(path);
|
||||
assertEquals(expected,s);
|
||||
}
|
||||
|
||||
static class TestPathCombiner {
|
||||
|
||||
PathPatternParser pp = new PathPatternParser();
|
||||
|
||||
public String combine(String string1, String string2) {
|
||||
PathPattern pattern1 = pp.parse(string1);
|
||||
return pattern1.combine(string2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,465 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.web.util.patterns;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Exercise the {@link PathPatternParser}.
|
||||
*
|
||||
* @author Andy Clement
|
||||
*/
|
||||
public class PathPatternParserTests {
|
||||
|
||||
private PathPattern p;
|
||||
|
||||
@Test
|
||||
public void basicPatterns() {
|
||||
checkStructure("/");
|
||||
checkStructure("/foo");
|
||||
checkStructure("foo");
|
||||
checkStructure("foo/");
|
||||
checkStructure("/foo/");
|
||||
checkStructure("//");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleCharWildcardPatterns() {
|
||||
p = checkStructure("?");
|
||||
assertPathElements(p , SingleCharWildcardedPathElement.class);
|
||||
checkStructure("/?/");
|
||||
checkStructure("//?abc?/");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multiwildcardPattern() {
|
||||
p = checkStructure("/**");
|
||||
assertPathElements(p,WildcardTheRestPathElement.class);
|
||||
p = checkStructure("/**acb"); // this is not double wildcard use, it is / then **acb (an odd, unnecessary use of double *)
|
||||
assertPathElements(p,SeparatorPathElement.class, RegexPathElement.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toStringTests() {
|
||||
assertEquals("CaptureTheRest(/{*foobar})", checkStructure("/{*foobar}").toChainString());
|
||||
assertEquals("CaptureVariable({foobar})", checkStructure("{foobar}").toChainString());
|
||||
assertEquals("Literal(abc)", checkStructure("abc").toChainString());
|
||||
assertEquals("Regex({a}_*_{b})", checkStructure("{a}_*_{b}").toChainString());
|
||||
assertEquals("Separator(/)", checkStructure("/").toChainString());
|
||||
assertEquals("SingleCharWildcarding(?a?b?c)", checkStructure("?a?b?c").toChainString());
|
||||
assertEquals("Wildcard(*)", checkStructure("*").toChainString());
|
||||
assertEquals("WildcardTheRest(/**)", checkStructure("/**").toChainString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void captureTheRestPatterns() {
|
||||
checkError("/{*foobar}x{abc}", 10, PatternMessage.NO_MORE_DATA_EXPECTED_AFTER_CAPTURE_THE_REST);
|
||||
p = checkStructure("{*foobar}");
|
||||
assertPathElements(p, CaptureTheRestPathElement.class);
|
||||
p = checkStructure("/{*foobar}");
|
||||
assertPathElements(p, CaptureTheRestPathElement.class);
|
||||
checkError("/{*foobar}/", 10, PatternMessage.NO_MORE_DATA_EXPECTED_AFTER_CAPTURE_THE_REST);
|
||||
checkError("/{*foobar}abc", 10, PatternMessage.NO_MORE_DATA_EXPECTED_AFTER_CAPTURE_THE_REST);
|
||||
checkError("/{*f%obar}", 4, PatternMessage.ILLEGAL_CHARACTER_IN_CAPTURE_DESCRIPTOR);
|
||||
checkError("/{*foobar}abc",10,PatternMessage.NO_MORE_DATA_EXPECTED_AFTER_CAPTURE_THE_REST);
|
||||
checkError("/{f*oobar}",3,PatternMessage.ILLEGAL_CHARACTER_IN_CAPTURE_DESCRIPTOR);
|
||||
checkError("/{*foobar}/abc",10,PatternMessage.NO_MORE_DATA_EXPECTED_AFTER_CAPTURE_THE_REST);
|
||||
checkError("/{abc}{*foobar}",1,PatternMessage.CAPTURE_ALL_IS_STANDALONE_CONSTRUCT);
|
||||
checkError("/{abc}{*foobar}{foo}",15,PatternMessage.NO_MORE_DATA_EXPECTED_AFTER_CAPTURE_THE_REST);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equalsAndHashcode() {
|
||||
PathPatternParser caseInsensitiveParser = new PathPatternParser();
|
||||
caseInsensitiveParser.setCaseSensitive(false);
|
||||
PathPatternParser caseSensitiveParser = new PathPatternParser();
|
||||
PathPattern pp1 = caseInsensitiveParser.parse("/abc");
|
||||
PathPattern pp2 = caseInsensitiveParser.parse("/abc");
|
||||
PathPattern pp3 = caseInsensitiveParser.parse("/def");
|
||||
assertEquals(pp1,pp2);
|
||||
assertEquals(pp1.hashCode(),pp2.hashCode());
|
||||
assertNotEquals(pp1, pp3);
|
||||
assertFalse(pp1.equals("abc"));
|
||||
|
||||
pp1 = caseInsensitiveParser.parse("/abc");
|
||||
pp2 = caseSensitiveParser.parse("/abc");
|
||||
assertFalse(pp1.equals(pp2));
|
||||
assertNotEquals(pp1.hashCode(),pp2.hashCode());
|
||||
|
||||
PathPatternParser alternateSeparatorParser = new PathPatternParser(':');
|
||||
pp1 = caseInsensitiveParser.parse("abc");
|
||||
pp2 = alternateSeparatorParser.parse("abc");
|
||||
assertFalse(pp1.equals(pp2));
|
||||
assertNotEquals(pp1.hashCode(),pp2.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void regexPathElementPatterns() {
|
||||
checkError("/{var:[^/]*}", 8, PatternMessage.MISSING_CLOSE_CAPTURE);
|
||||
checkError("/{var:abc", 8, PatternMessage.MISSING_CLOSE_CAPTURE);
|
||||
checkError("/{var:a{{1,2}}}", 6, PatternMessage.JDK_PATTERN_SYNTAX_EXCEPTION);
|
||||
|
||||
p = checkStructure("/{var:\\\\}");
|
||||
assertEquals(CaptureVariablePathElement.class.getName(),p.getHeadSection().next.getClass().getName());
|
||||
assertTrue(p.matches("/\\"));
|
||||
|
||||
p = checkStructure("/{var:\\/}");
|
||||
assertEquals(CaptureVariablePathElement.class.getName(),p.getHeadSection().next.getClass().getName());
|
||||
assertFalse(p.matches("/aaa"));
|
||||
|
||||
p = checkStructure("/{var:a{1,2}}",1);
|
||||
assertEquals(CaptureVariablePathElement.class.getName(),p.getHeadSection().next.getClass().getName());
|
||||
|
||||
p = checkStructure("/{var:[^\\/]*}",1);
|
||||
assertEquals(CaptureVariablePathElement.class.getName(),p.getHeadSection().next.getClass().getName());
|
||||
Map<String, String> result = p.matchAndExtract("/foo");
|
||||
assertEquals("foo",result.get("var"));
|
||||
|
||||
p = checkStructure("/{var:\\[*}",1);
|
||||
assertEquals(CaptureVariablePathElement.class.getName(),p.getHeadSection().next.getClass().getName());
|
||||
result = p.matchAndExtract("/[[[");
|
||||
assertEquals("[[[",result.get("var"));
|
||||
|
||||
p = checkStructure("/{var:[\\{]*}",1);
|
||||
assertEquals(CaptureVariablePathElement.class.getName(),p.getHeadSection().next.getClass().getName());
|
||||
result = p.matchAndExtract("/{{{");
|
||||
assertEquals("{{{",result.get("var"));
|
||||
|
||||
p = checkStructure("/{var:[\\}]*}",1);
|
||||
assertEquals(CaptureVariablePathElement.class.getName(),p.getHeadSection().next.getClass().getName());
|
||||
result = p.matchAndExtract("/}}}");
|
||||
assertEquals("}}}",result.get("var"));
|
||||
|
||||
p = checkStructure("*");
|
||||
assertEquals(WildcardPathElement.class.getName(),p.getHeadSection().getClass().getName());
|
||||
checkStructure("/*");
|
||||
checkStructure("/*/");
|
||||
checkStructure("*/");
|
||||
checkStructure("/*/");
|
||||
p = checkStructure("/*a*/");
|
||||
assertEquals(RegexPathElement.class.getName(),p.getHeadSection().next.getClass().getName());
|
||||
p = checkStructure("*/");
|
||||
assertEquals(WildcardPathElement.class.getName(),p.getHeadSection().getClass().getName());
|
||||
checkError("{foo}_{foo}", 0, PatternMessage.ILLEGAL_DOUBLE_CAPTURE, "foo");
|
||||
checkError("/{bar}/{bar}", 7, PatternMessage.ILLEGAL_DOUBLE_CAPTURE, "bar");
|
||||
checkError("/{bar}/{bar}_{foo}", 7, PatternMessage.ILLEGAL_DOUBLE_CAPTURE, "bar");
|
||||
|
||||
p = checkStructure("{symbolicName:[\\p{L}\\.]+}-sources-{version:[\\p{N}\\.]+}.jar");
|
||||
assertEquals(RegexPathElement.class.getName(),p.getHeadSection().getClass().getName());
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void completeCapturingPatterns() {
|
||||
p = checkStructure("{foo}");
|
||||
assertEquals(CaptureVariablePathElement.class.getName(),p.getHeadSection().getClass().getName());
|
||||
checkStructure("/{foo}");
|
||||
checkStructure("//{f}/");
|
||||
checkStructure("/{foo}/{bar}/{wibble}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void completeCaptureWithConstraints() {
|
||||
p = checkStructure("{foo:...}");
|
||||
assertPathElements(p, CaptureVariablePathElement.class);
|
||||
p = checkStructure("{foo:[0-9]*}");
|
||||
assertPathElements(p, CaptureVariablePathElement.class);
|
||||
checkError("{foo:}",5,PatternMessage.MISSING_REGEX_CONSTRAINT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void partialCapturingPatterns() {
|
||||
p = checkStructure("{foo}abc");
|
||||
assertEquals(RegexPathElement.class.getName(),p.getHeadSection().getClass().getName());
|
||||
checkStructure("abc{foo}");
|
||||
checkStructure("/abc{foo}");
|
||||
checkStructure("{foo}def/");
|
||||
checkStructure("/abc{foo}def/");
|
||||
checkStructure("{foo}abc{bar}");
|
||||
checkStructure("{foo}abc{bar}/");
|
||||
checkStructure("/{foo}abc{bar}/");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void illegalCapturePatterns() {
|
||||
checkError("{abc/",4,PatternMessage.MISSING_CLOSE_CAPTURE);
|
||||
checkError("{abc:}/",5,PatternMessage.MISSING_REGEX_CONSTRAINT);
|
||||
checkError("{",1,PatternMessage.MISSING_CLOSE_CAPTURE);
|
||||
checkError("{abc",4,PatternMessage.MISSING_CLOSE_CAPTURE);
|
||||
checkError("{/}",1,PatternMessage.MISSING_CLOSE_CAPTURE);
|
||||
checkError("//{",3,PatternMessage.MISSING_CLOSE_CAPTURE);
|
||||
checkError("}",0,PatternMessage.MISSING_OPEN_CAPTURE);
|
||||
checkError("/}",1,PatternMessage.MISSING_OPEN_CAPTURE);
|
||||
checkError("def}",3,PatternMessage.MISSING_OPEN_CAPTURE);
|
||||
checkError("//{/}",3,PatternMessage.MISSING_CLOSE_CAPTURE);
|
||||
checkError("//{{/}",3,PatternMessage.ILLEGAL_NESTED_CAPTURE);
|
||||
checkError("//{abc{/}",6,PatternMessage.ILLEGAL_NESTED_CAPTURE);
|
||||
checkError("/{0abc}/abc",2,PatternMessage.ILLEGAL_CHARACTER_AT_START_OF_CAPTURE_DESCRIPTOR);
|
||||
checkError("/{a?bc}/abc",3,PatternMessage.ILLEGAL_CHARACTER_IN_CAPTURE_DESCRIPTOR);
|
||||
checkError("/{abc}_{abc}",1,PatternMessage.ILLEGAL_DOUBLE_CAPTURE);
|
||||
checkError("/foobar/{abc}_{abc}",8,PatternMessage.ILLEGAL_DOUBLE_CAPTURE);
|
||||
checkError("/foobar/{abc:..}_{abc:..}",8,PatternMessage.ILLEGAL_DOUBLE_CAPTURE);
|
||||
PathPattern pp = parse("/{abc:foo(bar)}");
|
||||
try {
|
||||
pp.matchAndExtract("/foo");
|
||||
fail("Should have raised exception");
|
||||
} catch (IllegalArgumentException iae) {
|
||||
assertEquals("No capture groups allowed in the constraint regex: foo(bar)",iae.getMessage());
|
||||
}
|
||||
try {
|
||||
pp.matchAndExtract("/foobar");
|
||||
fail("Should have raised exception");
|
||||
} catch (IllegalArgumentException iae) {
|
||||
assertEquals("No capture groups allowed in the constraint regex: foo(bar)",iae.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void badPatterns() {
|
||||
// checkError("/{foo}{bar}/",6,PatternMessage.CANNOT_HAVE_ADJACENT_CAPTURES);
|
||||
checkError("/{?}/",2,PatternMessage.ILLEGAL_CHARACTER_AT_START_OF_CAPTURE_DESCRIPTOR,"?");
|
||||
checkError("/{a?b}/",3,PatternMessage.ILLEGAL_CHARACTER_IN_CAPTURE_DESCRIPTOR,"?");
|
||||
checkError("/{%%$}",2,PatternMessage.ILLEGAL_CHARACTER_AT_START_OF_CAPTURE_DESCRIPTOR,"%");
|
||||
checkError("/{ }",2,PatternMessage.ILLEGAL_CHARACTER_AT_START_OF_CAPTURE_DESCRIPTOR," ");
|
||||
checkError("/{%:[0-9]*}",2,PatternMessage.ILLEGAL_CHARACTER_AT_START_OF_CAPTURE_DESCRIPTOR,"%");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void patternPropertyGetCaptureCountTests() {
|
||||
// Test all basic section types
|
||||
assertEquals(1,parse("{foo}").getCapturedVariableCount());
|
||||
assertEquals(0,parse("foo").getCapturedVariableCount());
|
||||
assertEquals(1,parse("{*foobar}").getCapturedVariableCount());
|
||||
assertEquals(1,parse("/{*foobar}").getCapturedVariableCount());
|
||||
assertEquals(0,parse("/**").getCapturedVariableCount());
|
||||
assertEquals(1,parse("{abc}asdf").getCapturedVariableCount());
|
||||
assertEquals(1,parse("{abc}_*").getCapturedVariableCount());
|
||||
assertEquals(2,parse("{abc}_{def}").getCapturedVariableCount());
|
||||
assertEquals(0,parse("/").getCapturedVariableCount());
|
||||
assertEquals(0,parse("a?b").getCapturedVariableCount());
|
||||
assertEquals(0,parse("*").getCapturedVariableCount());
|
||||
|
||||
// Test on full templates
|
||||
assertEquals(0,parse("/foo/bar").getCapturedVariableCount());
|
||||
assertEquals(1,parse("/{foo}").getCapturedVariableCount());
|
||||
assertEquals(2,parse("/{foo}/{bar}").getCapturedVariableCount());
|
||||
assertEquals(4,parse("/{foo}/{bar}_{goo}_{wibble}/abc/bar").getCapturedVariableCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void patternPropertyGetWildcardCountTests() {
|
||||
// Test all basic section types
|
||||
assertEquals(computeScore(1,0),parse("{foo}").getScore());
|
||||
assertEquals(computeScore(0,0),parse("foo").getScore());
|
||||
assertEquals(computeScore(0,0),parse("{*foobar}").getScore());
|
||||
// assertEquals(1,parse("/**").getScore());
|
||||
assertEquals(computeScore(1,0),parse("{abc}asdf").getScore());
|
||||
assertEquals(computeScore(1,1),parse("{abc}_*").getScore());
|
||||
assertEquals(computeScore(2,0),parse("{abc}_{def}").getScore());
|
||||
assertEquals(computeScore(0,0),parse("/").getScore());
|
||||
assertEquals(computeScore(0,0),parse("a?b").getScore()); // currently deliberate
|
||||
assertEquals(computeScore(0,1),parse("*").getScore());
|
||||
|
||||
// Test on full templates
|
||||
assertEquals(computeScore(0,0),parse("/foo/bar").getScore());
|
||||
assertEquals(computeScore(1,0),parse("/{foo}").getScore());
|
||||
assertEquals(computeScore(2,0),parse("/{foo}/{bar}").getScore());
|
||||
assertEquals(computeScore(4,0),parse("/{foo}/{bar}_{goo}_{wibble}/abc/bar").getScore());
|
||||
assertEquals(computeScore(4,3),parse("/{foo}/*/*_*/{bar}_{goo}_{wibble}/abc/bar").getScore());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleSeparatorPatterns() {
|
||||
p = checkStructure("///aaa");
|
||||
assertEquals(4,p.getNormalizedLength());
|
||||
assertPathElements(p,SeparatorPathElement.class,LiteralPathElement.class);
|
||||
p = checkStructure("///aaa////aaa/b");
|
||||
assertEquals(10,p.getNormalizedLength());
|
||||
assertPathElements(p,SeparatorPathElement.class, LiteralPathElement.class,
|
||||
SeparatorPathElement.class, LiteralPathElement.class, SeparatorPathElement.class, LiteralPathElement.class);
|
||||
p = checkStructure("/////**");
|
||||
assertEquals(1,p.getNormalizedLength());
|
||||
assertPathElements(p,WildcardTheRestPathElement.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void patternPropertyGetLengthTests() {
|
||||
// Test all basic section types
|
||||
assertEquals(1,parse("{foo}").getNormalizedLength());
|
||||
assertEquals(3,parse("foo").getNormalizedLength());
|
||||
assertEquals(1,parse("{*foobar}").getNormalizedLength());
|
||||
assertEquals(1,parse("/{*foobar}").getNormalizedLength());
|
||||
assertEquals(1,parse("/**").getNormalizedLength());
|
||||
assertEquals(5,parse("{abc}asdf").getNormalizedLength());
|
||||
assertEquals(3,parse("{abc}_*").getNormalizedLength());
|
||||
assertEquals(3,parse("{abc}_{def}").getNormalizedLength());
|
||||
assertEquals(1,parse("/").getNormalizedLength());
|
||||
assertEquals(3,parse("a?b").getNormalizedLength());
|
||||
assertEquals(1,parse("*").getNormalizedLength());
|
||||
|
||||
// Test on full templates
|
||||
assertEquals(8,parse("/foo/bar").getNormalizedLength());
|
||||
assertEquals(2,parse("/{foo}").getNormalizedLength());
|
||||
assertEquals(4,parse("/{foo}/{bar}").getNormalizedLength());
|
||||
assertEquals(16,parse("/{foo}/{bar}_{goo}_{wibble}/abc/bar").getNormalizedLength());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareTests() {
|
||||
PathPattern p1,p2,p3;
|
||||
|
||||
// Based purely on number of captures
|
||||
p1 = parse("{a}");
|
||||
p2 = parse("{a}/{b}");
|
||||
p3 = parse("{a}/{b}/{c}");
|
||||
assertEquals(-1,p1.compareTo(p2)); // Based on number of captures
|
||||
List<PathPattern> patterns = new ArrayList<>();
|
||||
patterns.add(p2);
|
||||
patterns.add(p3);
|
||||
patterns.add(p1);
|
||||
Collections.sort(patterns,new PathPatternComparator());
|
||||
assertEquals(p1,patterns.get(0));
|
||||
|
||||
// Based purely on length
|
||||
p1 = parse("/a/b/c");
|
||||
p2 = parse("/a/boo/c/doo");
|
||||
p3 = parse("/asdjflaksjdfjasdf");
|
||||
assertEquals(1,p1.compareTo(p2));
|
||||
patterns = new ArrayList<>();
|
||||
patterns.add(p2);
|
||||
patterns.add(p3);
|
||||
patterns.add(p1);
|
||||
Collections.sort(patterns,new PathPatternComparator());
|
||||
assertEquals(p3,patterns.get(0));
|
||||
|
||||
// Based purely on 'wildness'
|
||||
p1 = parse("/*");
|
||||
p2 = parse("/*/*");
|
||||
p3 = parse("/*/*/*_*");
|
||||
assertEquals(-1,p1.compareTo(p2));
|
||||
patterns = new ArrayList<>();
|
||||
patterns.add(p2);
|
||||
patterns.add(p3);
|
||||
patterns.add(p1);
|
||||
Collections.sort(patterns,new PathPatternComparator());
|
||||
assertEquals(p1,patterns.get(0));
|
||||
|
||||
// Based purely on catchAll
|
||||
p1 = parse("{*foobar}");
|
||||
p2 = parse("{*goo}");
|
||||
assertEquals(0,p1.compareTo(p2));
|
||||
|
||||
p1 = parse("/{*foobar}");
|
||||
p2 = parse("/abc/{*ww}");
|
||||
assertEquals(+1,p1.compareTo(p2));
|
||||
assertEquals(-1,p2.compareTo(p1));
|
||||
|
||||
p3 = parse("/this/that/theother");
|
||||
assertTrue(p1.isCatchAll());
|
||||
assertTrue(p2.isCatchAll());
|
||||
assertFalse(p3.isCatchAll());
|
||||
patterns = new ArrayList<>();
|
||||
patterns.add(p2);
|
||||
patterns.add(p3);
|
||||
patterns.add(p1);
|
||||
Collections.sort(patterns,new PathPatternComparator());
|
||||
assertEquals(p3,patterns.get(0));
|
||||
assertEquals(p2,patterns.get(1));
|
||||
|
||||
patterns = new ArrayList<>();
|
||||
patterns.add(parse("/abc"));
|
||||
patterns.add(null);
|
||||
patterns.add(parse("/def"));
|
||||
Collections.sort(patterns,new PathPatternComparator());
|
||||
assertNull(patterns.get(2));
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
private PathPattern parse(String pattern) {
|
||||
PathPatternParser patternParser = new PathPatternParser();
|
||||
return patternParser.parse(pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the parsed chain of sections matches the original pattern and the separator count
|
||||
* that has been determined is correct.
|
||||
*/
|
||||
private PathPattern checkStructure(String pattern) {
|
||||
int count = 0;
|
||||
for (int i=0;i<pattern.length();i++) {
|
||||
if (pattern.charAt(i)=='/') {
|
||||
// if (peekDoubleWildcard(pattern,i)) {
|
||||
// // it is /**
|
||||
// i+=2;
|
||||
// } else {
|
||||
count++;
|
||||
// }
|
||||
}
|
||||
}
|
||||
return checkStructure(pattern,count);
|
||||
}
|
||||
|
||||
private PathPattern checkStructure(String pattern, int expectedSeparatorCount) {
|
||||
p = parse(pattern);
|
||||
assertEquals(pattern,p.getPatternString());
|
||||
// assertEquals(expectedSeparatorCount,p.getSeparatorCount());
|
||||
return p;
|
||||
}
|
||||
|
||||
private void checkError(String pattern, int expectedPos, PatternMessage expectedMessage, String... expectedInserts) {
|
||||
try {
|
||||
p = parse(pattern);
|
||||
fail("Expected to fail");
|
||||
} catch (PatternParseException ppe) {
|
||||
// System.out.println(ppe.toDetailedString());
|
||||
assertEquals(ppe.toDetailedString(), expectedPos, ppe.getPosition());
|
||||
assertEquals(ppe.toDetailedString(), expectedMessage, ppe.getMessageType());
|
||||
if (expectedInserts.length!=0) {
|
||||
assertEquals(ppe.getInserts().length,expectedInserts.length);
|
||||
for (int i=0;i<expectedInserts.length;i++) {
|
||||
assertEquals("Insert at position "+i+" is wrong",expectedInserts[i],ppe.getInserts()[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
private final void assertPathElements(PathPattern p, Class<? extends PathElement>... sectionClasses) {
|
||||
PathElement head = p.getHeadSection();
|
||||
for (int i=0;i<sectionClasses.length;i++) {
|
||||
if (head == null) {
|
||||
fail("Ran out of data in parsed pattern. Pattern is: "+p.toChainString());
|
||||
}
|
||||
assertEquals("Not expected section type. Pattern is: "+p.toChainString(),sectionClasses[i].getSimpleName(),head.getClass().getSimpleName());
|
||||
head = head.next;
|
||||
}
|
||||
}
|
||||
|
||||
// Mirrors the score computation logic in PathPattern
|
||||
private int computeScore(int capturedVariableCount, int wildcardCount) {
|
||||
return capturedVariableCount+wildcardCount*100;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user