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}