11/*
22 Original Copyright Headers
3- This file has been modified, but copied from
4- https://github.com/RestExpress/RestExpress /blob/master/core/ src/main/java/org/restexpress/url/UrlPattern .java
3+ This file include excerpt copied from
4+ https://github.com/wilkincheung/URI-Template-Pattern-Matcher /blob/master/src/main/java/com/prodigi/service/UriTemplateValidator .java
55 */
66/**
7- * Copyright 2010, Strategic Gains, Inc.
8- * <p>
9- * Licensed under the Apache License, Version 2.0 (the "License");
10- * you may not use this file except in compliance with the License.
11- * You may obtain a copy of the License at
12- * <p>
13- * http://www.apache.org/licenses/LICENSE-2.0
14- * <p>
15- * Unless required by applicable law or agreed to in writing, software
16- * distributed under the License is distributed on an "AS IS" BASIS,
17- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18- * See the License for the specific language governing permissions and
19- * limitations under the License.
20- */
21- package io .quarkiverse .openapi .generator .providers ;
22-
23- import java .util .regex .Pattern ;
24-
25- /**
26- * PathPatternMatcher leverages Regex Pattern to represent a parameterized URL. Parameters within the URL are
27- * denoted by curly braces '{}' with the parameter name contained within (e.g. '{userid}').
28- * <p>
29- * <p/>
30- * Parameter names must be formed of word characters (e.g. A-Z, a-z, 0-9, '_').
31- * <p/>
32- * An optional format parameter following a dot ('.') may be added to the end. While it could be named any valid parameter name,
33- * RestExpress offers special handling (e.g. within the Request, etc.) if it's named 'format'.
34- * <p/>
35- * Note that the format specifier allows only word characters and percent-encoded characters.
36- * <p>
37- * <p/>
38- * URL Pattern examples:
39- * <ul>
40- * <li>/api/search.{format}</li>
41- * <li>/api/search/users/{userid}.{format}</li>
42- * <li>/api/{version}/search/users/{userid}</li>
43- * </ul>
44- * <p>
45- * RestExpress parses URI paths which is described in the URI Generic Syntax IETF RFC 3986 specification,
46- * section 3.3 (http://tools.ietf.org/html/rfc3986#section-3.3). RestExpress parses paths into segments
47- * separated by slashes ("/"), the segments of which are composed of unreserved, percent encoded,
48- * sub-delimiters, colon (":") or ampersand ("@"), each of which are defined below (from the spec):
49- * <p/>
50- * pct-encoded = "%" HEXDIG HEXDIG
51- * <p/>
52- * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"<br/>
53- * reserved = gen-delims / sub-delims<br/>
54- * gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"</br>
55- * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" *
56- * <p/>
57- * In other words, RestExpress accepts path segments containing: [A-Z] [a-z] [0-9] % - . _ ~ ! $ & ' ( ) * + , ; = : @
58- * <p/>
59- * RestExpress also accepts square brackets ('[' and ']'), but this is deprecated and not recommended.
60- *
61- * @author toddf
62- * @see <a href="Uniform Resource Identifier (URI): Generic Syntax">http://www.ietf.org/rfc/rfc3986.txt</a>
63- * @since Apr 28, 2010
64- */
65- public class UrlPatternMatcher {
66- // Finds parameters in the URL pattern string.
67- private static final String URL_PARAM_REGEX = "\\ {(\\ S*?)\\ }" ;
7+ The MIT License (MIT)
8+ Copyright (c) 2015 Wilkin Cheung
689
69- // Replaces parameter names in the URL pattern string to match parameters in URLs.
70- private static final String URL_PARAM_MATCH_REGEX = "\\ ([%\\ \\ w-.\\ \\ ~!\\ $&'\\ \\ (\\ \\ )\\ \\ *\\ \\ +,;=:\\ \\ [\\ \\ ]@]+?\\ )" ;
10+ Permission is hereby granted, free of charge, to any person obtaining a copy
11+ of this software and associated documentation files (the "Software"), to deal
12+ in the Software without restriction, including without limitation the rights
13+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14+ copies of the Software, and to permit persons to whom the Software is
15+ furnished to do so, subject to the following conditions:
7116
72- // Finds the 'format' portion of the URL pattern string.
73- private static final String URL_FORMAT_REGEX = "(?: \\ . \\ {format \\ })$" ;
17+ The above copyright notice and this permission notice shall be included in all
18+ copies or substantial portions of the Software.
7419
75- // Replaces the format parameter name in the URL pattern string to match the format specifier in URLs. Appended to the end of the regex string
76- // when a URL pattern contains a format parameter.
77- private static final String URL_FORMAT_MATCH_REGEX = "(?:\\ \\ .\\ ([\\ \\ w%]+?\\ ))?" ;
20+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26+ SOFTWARE.
27+ */
28+ package io .quarkiverse .openapi .generator .providers ;
7829
79- // Finds the query string portion within a URL. Appended to the end of the built-up regex string.
80- private static final String URL_QUERY_STRING_REGEX = "(?:\\ ?.*?)?$" ;
30+ import java .util .ArrayList ;
31+ import java .util .LinkedHashMap ;
32+ import java .util .List ;
33+ import java .util .Map ;
34+ import java .util .regex .Matcher ;
35+ import java .util .regex .Pattern ;
8136
82- /**
83- * The URL pattern describing the URL layout and any parameters.
84- */
85- private final String urlPattern ;
37+ public class UrlPatternMatcher {
8638
87- /**
88- * A compiled regex created from the urlPattern, above.
89- */
90- private Pattern compiledUrl ;
39+ private UriTemplate uriTemplate ;
9140
92- /**
93- * @param pattern
94- */
9541 public UrlPatternMatcher (String pattern ) {
96- this .urlPattern = pattern ;
97- String parsedPattern = this .urlPattern .replaceFirst (URL_FORMAT_REGEX , URL_FORMAT_MATCH_REGEX );
98- parsedPattern = parsedPattern .replaceAll (URL_PARAM_REGEX , URL_PARAM_MATCH_REGEX );
99- this .compiledUrl = Pattern .compile (parsedPattern + URL_QUERY_STRING_REGEX );
42+ this .uriTemplate = new UriTemplate (pattern );
10043 }
10144
10245 /**
@@ -107,6 +50,113 @@ public UrlPatternMatcher(String pattern) {
10750 * @return true if the given URL matches the underlying pattern. Otherwise false.
10851 */
10952 public boolean matches (String url ) {
110- return compiledUrl .matcher (url ).matches ();
53+ return uriTemplate .matches (url );
54+ }
55+
56+ private static class UriTemplate {
57+
58+ // For each pattern {keyName} replaces it with (.*)
59+ private static final Pattern LEVEL_ONE_PATTERN = Pattern
60+ .compile ("\\ {([^/]+?)\\ }" );
61+ // Replaces each {keyName} with (.*)
62+ private static final String REPLACES_WITH = "(.*)" ;
63+
64+ /**
65+ * Ordered keyNames
66+ */
67+ private final List <String > keys ;
68+
69+ /**
70+ * Pattern
71+ */
72+ private final Pattern pattern ;
73+
74+ /**
75+ * UriTemplate for internal parsing to regular expression
76+ *
77+ * @param uriTemplate uriTemplate to be parsed
78+ */
79+ public UriTemplate (String uriTemplate ) {
80+
81+ StringBuilder patternBuilder = new StringBuilder ();
82+
83+ keys = new ArrayList <String >();
84+ Matcher m = LEVEL_ONE_PATTERN .matcher (uriTemplate );
85+ int start ;
86+ int end = 0 ;
87+
88+ // In each loop, find next pattern in URI that is "{keyName}"
89+ // If found, then add "keyName" to keyNames, and append the substring to
90+ // patternBuilder.
91+ while (m .find ()) {
92+
93+ // move start pointer to last match
94+ start = m .start ();
95+
96+ // Mark the pattern as escaped
97+ String escaped = Pattern .quote (uriTemplate .substring (end , start ));
98+
99+ patternBuilder .append (escaped );
100+
101+ patternBuilder .append (REPLACES_WITH );
102+
103+ // save the previously matched sequence (that is, keyName)
104+ // group(1) means the substring within (.*)
105+ keys .add (m .group (1 ));
106+
107+ // move end pointer to the end of matched string
108+ end = m .end ();
109+ }
110+
111+ // Mark the pattern as escaped
112+ patternBuilder .append (Pattern .quote (uriTemplate .substring (end ,
113+ uriTemplate
114+ .length ())));
115+ this .pattern = Pattern .compile (patternBuilder .toString ());
116+
117+ }
118+
119+ /**
120+ * Match the given URI to a map of key values. Keys in the returned map are
121+ * key names, values are key values, as occurred in the given URI.
122+ *
123+ * For example:
124+ * <code>
125+ * UriTemplate t = new UriTemplate("/human/v1/{typeA}/en-US/{typeB}/id/{typeC}.json");
126+ * t.match("http://api.prodigisoftware.com/human/v1/rec/en-US/movies/id/123.json");
127+ * </code>
128+ * ...would return: {typeA=rec, typeB=movies, typeC=123}
129+ *
130+ * If not a match, then returning <code>map</code> will have size 0.
131+ *
132+ * @param uri to try to match to
133+ * @return Map of matching key name (Map key), key value (Map value)
134+ */
135+ public Map <String , String > match (String uri ) {
136+ // LinkedHashMap to maintain key insertion order
137+ Map <String , String > result = new LinkedHashMap <>(keys .size ());
138+
139+ Matcher matcher = pattern .matcher (uri );
140+
141+ // find next part in uri that matches the pattern
142+ if (matcher .find ()) {
143+ for (int i = 0 ; i < matcher .groupCount (); i ++) {
144+ String name = keys .get (i );
145+ String value = matcher .group (i + 1 );
146+ result .put (name , value );
147+ }
148+ }
149+ return result ;
150+ }
151+
152+ /**
153+ * See if uri matches the pattern.
154+ *
155+ * @param uri String for matching
156+ * @return true if there is one or more matches; false otherwise
157+ */
158+ public boolean matches (String uri ) {
159+ return match (uri ).size () > 0 ;
160+ }
111161 }
112- }
162+ }
0 commit comments