001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.rest;
014
015import static org.apache.juneau.internal.StringUtils.*;
016
017import java.util.*;
018import java.util.regex.*;
019
020import org.apache.juneau.rest.annotation.*;
021
022/**
023 * @deprecated Unused.
024 */
025@Deprecated
026public final class UrlPathPattern implements Comparable<UrlPathPattern> {
027
028   private final Pattern pattern;
029   private final String patternString;
030   private final boolean isOnlyDotAll, isDotAll;
031   private final String[] vars;
032
033   /**
034    * Constructor.
035    *
036    * @param patternString The raw pattern string from the {@link RestMethod#path() @RestMethod.path()} annotation.
037    */
038   public UrlPathPattern(String patternString) {
039      this.patternString = patternString;
040      Builder b = new Builder(patternString);
041      pattern = b.pattern;
042      isDotAll = b.isDotAll;
043      isOnlyDotAll = b.isOnlyDotAll;
044      vars = b.vars.toArray(new String[b.vars.size()]);
045   }
046
047   private final class Builder {
048      boolean isDotAll, isOnlyDotAll;
049      Pattern pattern;
050      List<String> vars = new LinkedList<>();
051
052      Builder(String patternString) {
053         if (! startsWith(patternString, '/'))
054            patternString = '/' + patternString;
055         if (patternString.equals("/*")) {
056            isOnlyDotAll = true;
057            return;
058         }
059         if (patternString.endsWith("/*"))
060            isDotAll = true;
061
062         // Find all {xxx} variables.
063         Pattern p = Pattern.compile("\\{([^\\}]+)\\}");
064         Matcher m = p.matcher(patternString);
065         while (m.find())
066            vars.add(m.group(1));
067
068         patternString = patternString.replaceAll("\\{[^\\}]+\\}", "([^\\/]+)");
069         patternString = patternString.replaceAll("\\/\\*$", "((?:)|(?:\\/.*))");
070         pattern = Pattern.compile(patternString);
071      }
072   }
073
074   /**
075    * Returns a non-<jk>null</jk> value if the specified path matches this pattern.
076    *
077    * @param path The path to match against.
078    * @return
079    *    An array of values matched against <js>"{var}"</js> variable in the pattern, or an empty array if the
080    *    pattern matched but no vars were present, or <jk>null</jk> if the specified path didn't match the pattern.
081    */
082   protected String[] match(String path) {
083
084      if (isOnlyDotAll) {
085         // Remainder always gets leading slash trimmed.
086         if (path != null)
087            path = path.substring(1);
088         return new String[]{path};
089      }
090
091      if (path == null)
092         return (patternString.equals("/") ? new String[]{} : null);
093
094      // If we're not doing a /* match, ignore all trailing slashes.
095      if (! isDotAll)
096         while (path.length() > 1 && path.charAt(path.length()-1) == '/')
097            path = path.substring(0, path.length()-1);
098
099      Matcher m = pattern.matcher(path);
100      if (! m.matches())
101         return null;
102
103      int len = m.groupCount();
104      String[] v = new String[len];
105
106      for (int i = 0; i < len; i++) {
107         if (isDotAll && i == len-1)
108            v[i] = m.group(i+1).isEmpty() ? null : m.group(i+1).substring(1);
109         else
110         v[i] = urlDecode(m.group(i+1));
111      }
112
113      return v;
114   }
115
116   /**
117    * Comparator for this object.
118    *
119    * <p>
120    * The comparator is designed to order URL pattern from most-specific to least-specific.
121    * For example, the following patterns would be ordered as follows:
122    * <ol>
123    *    <li><code>/foo/bar</code>
124    *    <li><code>/foo/bar/*</code>
125    *    <li><code>/foo/{id}/bar</code>
126    *    <li><code>/foo/{id}/bar/*</code>
127    *    <li><code>/foo/{id}</code>
128    *    <li><code>/foo/{id}/*</code>
129    *    <li><code>/foo</code>
130    *    <li><code>/foo/*</code>
131    * </ol>
132    */
133   @Override /* Comparable */
134   public int compareTo(UrlPathPattern o) {
135      String s1 = patternString.replaceAll("\\{[^\\}]+\\}", ".").replaceAll("\\w+", "X").replaceAll("\\.", "W");
136      String s2 = o.patternString.replaceAll("\\{[^\\}]+\\}", ".").replaceAll("\\w+", "X").replaceAll("\\.", "W");
137      if (s1.isEmpty())
138         s1 = "+";
139      if (s2.isEmpty())
140         s2 = "+";
141      if (! s1.endsWith("/*"))
142         s1 = s1 + "/W";
143      if (! s2.endsWith("/*"))
144         s2 = s2 + "/W";
145      int c = s2.compareTo(s1);
146      if (c == 0)
147         return o.toRegEx().compareTo(toRegEx());
148      return c;
149   }
150
151   @Override /* Object */
152   public boolean equals(Object o) {
153      if (! (o instanceof UrlPathPattern))
154         return false;
155      return (compareTo((UrlPathPattern)o) == 0);
156   }
157
158   @Override /* Object */
159   public int hashCode() {
160      return super.hashCode();
161   }
162
163   @Override /* Object */
164   public String toString() {
165      return patternString;
166   }
167
168   /**
169    * Returns this path pattern as the compiled regular expression.
170    *
171    * <p>
172    * Useful for debugging.
173    *
174    * @return The path pattern.
175    */
176   public String toRegEx() {
177      return isOnlyDotAll ? "*" : pattern.pattern();
178   }
179
180   /**
181    * Bean property getter:  <property>vars</property>.
182    *
183    * @return The value of the <property>vars</property> property on this bean, or <jk>null</jk> if it is not set.
184    */
185   public String[] getVars() {
186      return vars;
187   }
188
189   /**
190    * Bean property getter:  <property>patternString</property>.
191    *
192    * @return The value of the <property>patternString</property> property on this bean, or <jk>null</jk> if it is not set.
193    */
194   public String getPatternString() {
195      return patternString;
196   }
197}