/* * Copyright 2002-2017 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.pattern; 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 org.springframework.util.AntPathMatcher; import org.springframework.web.util.pattern.ParsingPathMatcher; import org.springframework.web.util.pattern.PathPattern; import org.springframework.web.util.pattern.PathPatternParser; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; /** * Exercise matching of {@link PathPattern} objects. * * @author Andy Clement */ public class PathPatternMatcherTests { private char separator = PathPatternParser.DEFAULT_SEPARATOR; @Test public void basicMatching() { checkMatches("", ""); checkMatches("", null); checkNoMatch("/abc", null); 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 optionalTrailingSeparators() { // LiteralPathElement PathPattern pp = parse("/resource"); assertTrue(pp.matches("/resource")); assertTrue(pp.matches("/resource/")); assertFalse(pp.matches("/resource//")); pp = parse("/resource/"); assertFalse(pp.matches("/resource")); assertTrue(pp.matches("/resource/")); assertFalse(pp.matches("/resource//")); // SingleCharWildcardPathElement pp = parse("/res?urce"); assertTrue(pp.matches("/resource")); assertTrue(pp.matches("/resource/")); assertFalse(pp.matches("/resource//")); pp = parse("/res?urce/"); assertFalse(pp.matches("/resource")); assertTrue(pp.matches("/resource/")); assertFalse(pp.matches("/resource//")); // CaptureVariablePathElement pp = parse("/{var}"); assertTrue(pp.matches("/resource")); assertEquals("resource",pp.matchAndExtract("/resource").get("var")); assertTrue(pp.matches("/resource/")); assertEquals("resource",pp.matchAndExtract("/resource/").get("var")); assertFalse(pp.matches("/resource//")); pp = parse("/{var}/"); assertFalse(pp.matches("/resource")); assertTrue(pp.matches("/resource/")); assertEquals("resource",pp.matchAndExtract("/resource/").get("var")); assertFalse(pp.matches("/resource//")); // CaptureTheRestPathElement pp = parse("/{*var}"); assertTrue(pp.matches("/resource")); assertEquals("/resource",pp.matchAndExtract("/resource").get("var")); assertTrue(pp.matches("/resource/")); assertEquals("/resource/",pp.matchAndExtract("/resource/").get("var")); assertTrue(pp.matches("/resource//")); assertEquals("/resource//",pp.matchAndExtract("/resource//").get("var")); assertTrue(pp.matches("//resource//")); assertEquals("//resource//",pp.matchAndExtract("//resource//").get("var")); // WildcardTheRestPathElement pp = parse("/**"); assertTrue(pp.matches("/resource")); assertTrue(pp.matches("/resource/")); assertTrue(pp.matches("/resource//")); assertTrue(pp.matches("//resource//")); // WildcardPathElement pp = parse("/*"); assertTrue(pp.matches("/resource")); assertTrue(pp.matches("/resource/")); assertFalse(pp.matches("/resource//")); pp = parse("/*/"); assertFalse(pp.matches("/resource")); assertTrue(pp.matches("/resource/")); assertFalse(pp.matches("/resource//")); // RegexPathElement pp = parse("/{var1}_{var2}"); assertTrue(pp.matches("/res1_res2")); assertEquals("res1",pp.matchAndExtract("/res1_res2").get("var1")); assertEquals("res2",pp.matchAndExtract("/res1_res2").get("var2")); assertTrue(pp.matches("/res1_res2/")); assertEquals("res1",pp.matchAndExtract("/res1_res2/").get("var1")); assertEquals("res2",pp.matchAndExtract("/res1_res2/").get("var2")); assertFalse(pp.matches("/res1_res2//")); pp = parse("/{var1}_{var2}/"); assertFalse(pp.matches("/res1_res2")); assertTrue(pp.matches("/res1_res2/")); assertEquals("res1",pp.matchAndExtract("/res1_res2/").get("var1")); assertEquals("res2",pp.matchAndExtract("/res1_res2/").get("var2")); assertFalse(pp.matches("/res1_res2//")); pp = parse("/{var1}*"); assertTrue(pp.matches("/a")); assertTrue(pp.matches("/a/")); assertFalse(pp.matches("/")); // no characters for var1 assertFalse(pp.matches("//")); // no characters for var1 // Now with trailing matching turned OFF PathPatternParser parser = new PathPatternParser(); parser.setMatchOptionalTrailingSlash(false); // LiteralPathElement pp = parser.parse("/resource"); assertTrue(pp.matches("/resource")); assertFalse(pp.matches("/resource/")); assertFalse(pp.matches("/resource//")); pp = parser.parse("/resource/"); assertFalse(pp.matches("/resource")); assertTrue(pp.matches("/resource/")); assertFalse(pp.matches("/resource//")); // SingleCharWildcardPathElement pp = parser.parse("/res?urce"); assertTrue(pp.matches("/resource")); assertFalse(pp.matches("/resource/")); assertFalse(pp.matches("/resource//")); pp = parser.parse("/res?urce/"); assertFalse(pp.matches("/resource")); assertTrue(pp.matches("/resource/")); assertFalse(pp.matches("/resource//")); // CaptureVariablePathElement pp = parser.parse("/{var}"); assertTrue(pp.matches("/resource")); assertEquals("resource",pp.matchAndExtract("/resource").get("var")); assertFalse(pp.matches("/resource/")); assertFalse(pp.matches("/resource//")); pp = parser.parse("/{var}/"); assertFalse(pp.matches("/resource")); assertTrue(pp.matches("/resource/")); assertEquals("resource",pp.matchAndExtract("/resource/").get("var")); assertFalse(pp.matches("/resource//")); // CaptureTheRestPathElement pp = parser.parse("/{*var}"); assertTrue(pp.matches("/resource")); assertEquals("/resource",pp.matchAndExtract("/resource").get("var")); assertTrue(pp.matches("/resource/")); assertEquals("/resource/",pp.matchAndExtract("/resource/").get("var")); assertTrue(pp.matches("/resource//")); assertEquals("/resource//",pp.matchAndExtract("/resource//").get("var")); assertTrue(pp.matches("//resource//")); assertEquals("//resource//",pp.matchAndExtract("//resource//").get("var")); // WildcardTheRestPathElement pp = parser.parse("/**"); assertTrue(pp.matches("/resource")); assertTrue(pp.matches("/resource/")); assertTrue(pp.matches("/resource//")); assertTrue(pp.matches("//resource//")); // WildcardPathElement pp = parser.parse("/*"); assertTrue(pp.matches("/resource")); assertFalse(pp.matches("/resource/")); assertFalse(pp.matches("/resource//")); pp = parser.parse("/*/"); assertFalse(pp.matches("/resource")); assertTrue(pp.matches("/resource/")); assertFalse(pp.matches("/resource//")); // RegexPathElement pp = parser.parse("/{var1}_{var2}"); assertTrue(pp.matches("/res1_res2")); assertEquals("res1",pp.matchAndExtract("/res1_res2").get("var1")); assertEquals("res2",pp.matchAndExtract("/res1_res2").get("var2")); assertFalse(pp.matches("/res1_res2/")); assertFalse(pp.matches("/res1_res2//")); pp = parser.parse("/{var1}_{var2}/"); assertFalse(pp.matches("/res1_res2")); assertTrue(pp.matches("/res1_res2/")); assertEquals("res1",pp.matchAndExtract("/res1_res2/").get("var1")); assertEquals("res2",pp.matchAndExtract("/res1_res2/").get("var2")); assertFalse(pp.matches("/res1_res2//")); pp = parser.parse("/{var1}*"); assertTrue(pp.matches("/a")); assertFalse(pp.matches("/a/")); assertFalse(pp.matches("/")); // no characters for var1 assertFalse(pp.matches("//")); // no characters for var1 } @Test public void pathRemainderBasicCases_spr15336() { // Cover all PathElement kinds assertEquals("/bar", parse("/foo").getPathRemaining("/foo/bar").getPathRemaining()); assertEquals("/", parse("/foo").getPathRemaining("/foo/").getPathRemaining()); assertEquals("/bar",parse("/foo*").getPathRemaining("/foo/bar").getPathRemaining()); assertEquals("/bar", parse("/*").getPathRemaining("/foo/bar").getPathRemaining()); assertEquals("/bar", parse("/{foo}").getPathRemaining("/foo/bar").getPathRemaining()); assertNull(parse("/foo").getPathRemaining("/bar/baz")); assertEquals("",parse("/**").getPathRemaining("/foo/bar").getPathRemaining()); assertEquals("",parse("/{*bar}").getPathRemaining("/foo/bar").getPathRemaining()); assertEquals("/bar",parse("/a?b/d?e").getPathRemaining("/aab/dde/bar").getPathRemaining()); assertEquals("/bar",parse("/{abc}abc").getPathRemaining("/xyzabc/bar").getPathRemaining()); assertEquals("/bar",parse("/*y*").getPathRemaining("/xyzxyz/bar").getPathRemaining()); assertEquals("",parse("/").getPathRemaining("/").getPathRemaining()); assertEquals("a",parse("/").getPathRemaining("/a").getPathRemaining()); assertEquals("a/",parse("/").getPathRemaining("/a/").getPathRemaining()); assertEquals("/bar",parse("/a{abc}").getPathRemaining("/a/bar").getPathRemaining()); } @Test public void pathRemainingCornerCases_spr15336() { // No match when the literal path element is a longer form of the segment in the pattern assertNull(parse("/foo").getPathRemaining("/footastic/bar")); assertNull(parse("/f?o").getPathRemaining("/footastic/bar")); assertNull(parse("/f*o*p").getPathRemaining("/flooptastic/bar")); assertNull(parse("/{abc}abc").getPathRemaining("/xyzabcbar/bar")); // With a /** on the end have to check if there is any more data post // 'the match' it starts with a separator assertNull(parse("/resource/**").getPathRemaining("/resourceX")); assertEquals("",parse("/resource/**").getPathRemaining("/resource").getPathRemaining()); // Similar to above for the capture-the-rest variant assertNull(parse("/resource/{*foo}").getPathRemaining("/resourceX")); assertEquals("",parse("/resource/{*foo}").getPathRemaining("/resource").getPathRemaining()); PathPattern.PathRemainingMatchInfo pri = parse("/aaa/{bbb}/c?d/e*f/*/g").getPathRemaining("/aaa/b/ccd/ef/x/g/i"); assertEquals("/i",pri.getPathRemaining()); assertEquals("b",pri.getMatchingVariables().get("bbb")); pri = parse("/{aaa}_{bbb}/e*f/{x}/g").getPathRemaining("/aa_bb/ef/x/g/i"); assertEquals("/i",pri.getPathRemaining()); assertEquals("aa",pri.getMatchingVariables().get("aaa")); assertEquals("bb",pri.getMatchingVariables().get("bbb")); assertEquals("x",pri.getMatchingVariables().get("x")); assertNull(parse("/a/b").getPathRemaining("")); assertNull(parse("/a/b").getPathRemaining(null)); assertEquals("/a/b",parse("").getPathRemaining("/a/b").getPathRemaining()); assertEquals("",parse("").getPathRemaining("").getPathRemaining()); assertNull(parse("").getPathRemaining(null).getPathRemaining()); } @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() { checkMatches("/resource/{*foobar}", "/resource"); checkNoMatch("/resource/{*foobar}", "/resourceX"); checkNoMatch("/resource/{*foobar}", "/resourceX/foobar"); checkMatches("/resource/{*foobar}", "/resource/foobar"); checkCapture("/resource/{*foobar}", "/resource/foobar", "foobar", "/foobar"); 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//////", "something", "//"); checkCapture("/customer//////{*something}", "/customer//////99", "something", "/99"); checkCapture("/customer//////{*something}", "/customer//////99", "something", "/99"); checkCapture("/customer/{*something}", "/customer", "something", ""); checkCapture("/{*something}", "", "something", ""); checkCapture("/customer/{*something}", "/customer//////99", "something", "//////99"); } @Test public void multipleSeparatorsInPattern() { PathPattern pp = parse("a//b//c"); assertEquals("Literal(a) Separator(/) Separator(/) Literal(b) Separator(/) Separator(/) Literal(c)",pp.toChainString()); assertTrue(pp.matches("a//b//c")); assertEquals("Literal(a) Separator(/) WildcardTheRest(/**)",parse("a//**").toChainString()); checkMatches("///abc", "///abc"); checkNoMatch("///abc", "/abc"); checkNoMatch("//", "/"); checkMatches("//", "//"); checkNoMatch("///abc//d/e", "/abc/d/e"); checkMatches("///abc//d/e", "///abc//d/e"); checkNoMatch("///abc//{def}//////xyz", "/abc/foo/xyz"); checkMatches("///abc//{def}//////xyz", "///abc//p//////xyz"); } @Test public void multipleSelectorsInPath() { checkNoMatch("/abc", "////abc"); checkNoMatch("/", "//"); checkNoMatch("/abc/def/ghi", "/abc//def///ghi"); checkNoMatch("/abc", "////abc"); checkMatches("////abc", "////abc"); checkNoMatch("/", "//"); checkNoMatch("/abc//def///ghi", "/abc/def/ghi"); checkMatches("/abc//def///ghi", "/abc//def///ghi"); } @Test public void multipleSeparatorsInPatternAndPath() { checkNoMatch("///one///two///three", "//one/////two///////three"); checkMatches("//one/////two///////three", "//one/////two///////three"); checkNoMatch("//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"); checkNoMatch("/*/bar", "//bar"); checkMatches("/f*/bar", "/foo/bar"); checkMatches("/*/bar", "/foo/bar"); checkMatches("a/*","a/"); checkMatches("/*","/"); 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/"); checkNoMatch("a/*", "a//"); // trailing slash, so is allowed checkMatches("a/*", "a/a/"); // trailing slash, so is allowed PathPatternParser ppp = new PathPatternParser(); ppp.setMatchOptionalTrailingSlash(false); assertFalse(ppp.parse("a/*").matches("a//")); checkMatches("a/*", "a/a"); checkMatches("a/*", "a/a/"); // trailing slash is optional checkMatches("/resource/**", "/resource"); checkNoMatch("/resource/**", "/resourceX"); checkNoMatch("/resource/**", "/resourceX/foobar"); checkMatches("/resource/**", "/resource/foobar"); } @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"); checkMatches("test*", "test/"); // trailing slash is optional checkMatches("test*", "test"); // trailing slash is optional 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("/**", "/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 pathRemainingEnhancements_spr15419() { // It would be nice to partially match a path and get any bound variables in one step PathPattern pp = parse("/{this}/{one}/{here}"); PathPattern.PathRemainingMatchInfo pri = pp.getPathRemaining("/foo/bar/goo/boo"); assertEquals("/boo",pri.getPathRemaining()); assertEquals("foo",pri.getMatchingVariables().get("this")); assertEquals("bar",pri.getMatchingVariables().get("one")); assertEquals("goo",pri.getMatchingVariables().get("here")); pp = parse("/aaa/{foo}"); pri = pp.getPathRemaining("/aaa/bbb"); assertEquals("",pri.getPathRemaining()); assertEquals("bbb",pri.getMatchingVariables().get("foo")); pp = parse("/aaa/bbb"); pri = pp.getPathRemaining("/aaa/bbb"); assertEquals("",pri.getPathRemaining()); assertEquals(0,pri.getMatchingVariables().size()); pp = parse("/*/{foo}/b*"); pri = pp.getPathRemaining("/foo"); assertNull(pri); pri = pp.getPathRemaining("/abc/def/bhi"); assertEquals("",pri.getPathRemaining()); assertEquals("def",pri.getMatchingVariables().get("foo")); pri = pp.getPathRemaining("/abc/def/bhi/jkl"); assertEquals("/jkl",pri.getPathRemaining()); assertEquals("def",pri.getMatchingVariables().get("foo")); } @Test public void matchStart() { checkStartNoMatch("test/*/","test//"); checkStartMatches("test/*","test/abc"); checkStartMatches("test/*/def","test/abc/def"); checkStartNoMatch("test/*/def","test//"); checkStartNoMatch("test/*/def","test//def"); 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"); checkStartMatches("test*", "test/"); // trailing slash is optional checkStartMatches("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"); checkStartMatches("/abc/{foo}", "/abc/def/"); // trailing slash is optional checkStartMatches("/abc/{foo}/", "/abc/def/"); checkStartNoMatch("/abc/{foo}/", "/abc/def/ghi"); checkStartMatches("/abc/{foo}/", "/abc/def"); checkStartMatches("", ""); checkStartMatches("", null); checkStartMatches("/abc", null); } @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 { this.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 { this.separator = PathPatternParser.DEFAULT_SEPARATOR; } } @Test public void extractPathWithinPattern_spr15259() { checkExtractPathWithinPattern("/**","//","/"); checkExtractPathWithinPattern("/**","/",""); checkExtractPathWithinPattern("/**","",""); checkExtractPathWithinPattern("/**","/foobar","foobar"); } @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/c*/ddd/", "aaa/ccc///ddd///", "ccc/ddd"); checkExtractPathWithinPattern("", "", ""); checkExtractPathWithinPattern("/", "", ""); checkExtractPathWithinPattern("", "/", ""); checkExtractPathWithinPattern("//", "", ""); checkExtractPathWithinPattern("", "//", ""); checkExtractPathWithinPattern("//", "//", ""); checkExtractPathWithinPattern("//", "/", ""); checkExtractPathWithinPattern("/", "//", ""); } @Test public void extractUriTemplateVariables_spr15264() { PathPattern pp = new PathPatternParser().parse("/{foo}"); assertTrue(pp.matches("/abc")); assertFalse(pp.matches("/")); assertFalse(pp.matches("//")); checkCapture("/{foo}", "/abc", "foo", "abc"); pp = new PathPatternParser().parse("/{foo}/{bar}"); assertTrue(pp.matches("/abc/def")); assertFalse(pp.matches("/def")); assertFalse(pp.matches("/")); assertFalse(pp.matches("//def")); assertFalse(pp.matches("//")); pp = parse("/{foo}/boo"); assertTrue(pp.matches("/abc/boo")); assertTrue(pp.matches("/a/boo")); assertFalse(pp.matches("/boo")); assertFalse(pp.matches("//boo")); pp = parse("/{foo}*"); assertTrue(pp.matches("/abc")); assertFalse(pp.matches("/")); checkCapture("/{word:[a-z]*}", "/abc", "word", "abc"); pp = parse("/{word:[a-z]*}"); assertFalse(pp.matches("/1")); assertTrue(pp.matches("/a")); assertFalse(pp.matches("/")); // Two captures mean we use a RegexPathElement pp = new PathPatternParser().parse("/{foo}{bar}"); assertTrue(pp.matches("/abcdef")); assertFalse(pp.matches("/")); assertFalse(pp.matches("//")); checkCapture("/{foo:[a-z][a-z]}{bar:[a-z]}", "/abc", "foo", "ab", "bar", "c"); // Only patterns not capturing variables cannot match against just / PathPatternParser ppp = new PathPatternParser(); ppp.setMatchOptionalTrailingSlash(true); pp = ppp.parse("/****"); assertTrue(pp.matches("/abcdef")); assertTrue(pp.matches("/")); assertTrue(pp.matches("/")); assertTrue(pp.matches("//")); // Confirming AntPathMatcher behaviour: assertFalse(new AntPathMatcher().match("/{foo}", "/")); assertTrue(new AntPathMatcher().match("/{foo}", "/a")); assertTrue(new AntPathMatcher().match("/{foo}{bar}", "/a")); assertFalse(new AntPathMatcher().match("/{foo}*", "/")); assertTrue(new AntPathMatcher().match("/*", "/")); assertFalse(new AntPathMatcher().match("/*{foo}", "/")); Map<String, String> vars = new AntPathMatcher().extractUriTemplateVariables("/{foo}{bar}", "/a"); assertEquals("a",vars.get("foo")); assertEquals("",vars.get("bar")); } @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()); checkCapture("/{bla}/foo","/a/foo"); } @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("", "")); assertEquals("/hotels", pathMatcher.combine("/hotels", "")); assertEquals("/hotels", pathMatcher.combine("", "/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("/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 ParsingPathMatcher.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 patternCompareTo() { PathPatternParser p = new PathPatternParser(); PathPattern pp = p.parse("/abc"); assertEquals(-1, pp.compareTo(null)); } @Test public void patternComparatorSort() { Comparator<PathPattern> comparator = new ParsingPathMatcher.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 ParsingPathMatcher.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")); } private PathPattern parse(String path) { PathPatternParser pp = new PathPatternParser(); pp.setMatchOptionalTrailingSlash(true); return pp.parse(path); } private void checkMatches(String uriTemplate, String path) { PathPatternParser parser = new PathPatternParser(this.separator); parser.setMatchOptionalTrailingSlash(true); PathPattern p = parser.parse(uriTemplate); assertTrue(p.matches(path)); } private void checkStartNoMatch(String uriTemplate, String path) { PathPatternParser p = new PathPatternParser(); p.setMatchOptionalTrailingSlash(true); PathPattern pattern = p.parse(uriTemplate); assertFalse(pattern.matchStart(path)); } private void checkStartMatches(String uriTemplate, String path) { PathPatternParser p = new PathPatternParser(); p.setMatchOptionalTrailingSlash(true); 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); } } }