001 package com.khubla.pragmatach.framework.router;
002
003 import java.net.URLDecoder;
004 import java.util.LinkedHashMap;
005 import java.util.List;
006
007 import com.khubla.pragmatach.framework.annotation.Route;
008 import com.khubla.pragmatach.framework.annotation.RouteParameter;
009 import com.khubla.pragmatach.framework.api.PragmatachException;
010 import com.khubla.pragmatach.framework.api.Request;
011 import com.khubla.pragmatach.framework.url.RouteSpecificationSegment;
012 import com.khubla.pragmatach.framework.url.URICracker;
013
014 /**
015 * @author tome
016 */
017 public class RouteFinder {
018 /**
019 * check that the static parts of the route match
020 */
021 private static boolean staticPartsMatch(PragmatachRoute pragmatachRoute, String[] crackedURI) throws PragmatachException {
022 try {
023 /*
024 * check that the static path parts match
025 */
026 int j = 0;
027 for (final RouteSpecificationSegment routeSpecificationSegment : pragmatachRoute.getRouteSpecification().getSegments()) {
028 final String staticPathPart = routeSpecificationSegment.getPath();
029 if (null != staticPathPart) {
030 if (false == (crackedURI[j].compareTo(staticPathPart) == 0)) {
031 return false;
032 }
033 }
034 j++;
035 }
036 return true;
037 } catch (final Exception e) {
038 throw new PragmatachException("Exception in staticPartsMatch", e);
039 }
040 }
041
042 /**
043 * check that the static parts of wildcard route match
044 */
045 private static boolean staticWildcardPartsMatch(PragmatachRoute pragmatachRoute, String[] crackedURI) throws PragmatachException {
046 try {
047 /*
048 * check that the static path parts match
049 */
050 final List<RouteSpecificationSegment> routeSpecificationSegments = pragmatachRoute.getRouteSpecification().getSegments();
051 for (int i = 0; i < (routeSpecificationSegments.size() - 1); i++) {
052 final RouteSpecificationSegment routeSpecificationSegment = routeSpecificationSegments.get(i);
053 final String staticPathPart = routeSpecificationSegment.getPath();
054 if (null != staticPathPart) {
055 if (false == (crackedURI[i].compareTo(staticPathPart) == 0)) {
056 return false;
057 }
058 }
059 }
060 return true;
061 } catch (final Exception e) {
062 throw new PragmatachException("Exception in staticPartsMatch", e);
063 }
064 }
065
066 /**
067 * the method parameters. key is the id specified in the routespecification, value is the value passed in the uri
068 */
069 private LinkedHashMap<String, String> parameterMap;
070 /**
071 * the route
072 */
073 private PragmatachRoute pragmatachRoute;
074
075 /**
076 * get the parameter map that the URI actually means
077 */
078 private LinkedHashMap<String, String> buildParameterMap(PragmatachRoute pragmatachRoute, String[] crackedURI) throws PragmatachException {
079 try {
080 /*
081 * the ret
082 */
083 final LinkedHashMap<String, String> ret = new LinkedHashMap<String, String>();
084 /*
085 * check if wildcard
086 */
087 if (false == pragmatachRoute.isWildcardRoute()) {
088 /*
089 * walk the route specification
090 */
091 int i = 0;
092 for (final RouteSpecificationSegment routeSpecificationSegment : pragmatachRoute.getRouteSpecification().getSegments()) {
093 final String id = routeSpecificationSegment.getVariableId();
094 if ((null != id) && (id.length() > 0)) {
095 /*
096 * URL decode the parameter
097 */
098 final String decodedParameter = URLDecoder.decode(crackedURI[i], "UTF-8");
099 /*
100 * set the parameter
101 */
102 ret.put(id, decodedParameter);
103 }
104 i++;
105 }
106 } else {
107 /*
108 * hacky, but it works
109 */
110 for (int i = pragmatachRoute.getSegmentCount() - 1; i < crackedURI.length; i++) {
111 ret.put(crackedURI[i], crackedURI[i]);
112 }
113 }
114 return ret;
115 } catch (final Exception e) {
116 throw new PragmatachException("Exception in getParameters", e);
117 }
118 }
119
120 /**
121 * get the appropriate routes to search. These routes are already ordered.
122 */
123 private List<PragmatachRoute> getMethodRoutes(Route.HttpMethod httpMethod) throws PragmatachException {
124 if (httpMethod == Route.HttpMethod.get) {
125 return PragmatachRoutes.getInstance().getGETRoutes();
126 } else {
127 return PragmatachRoutes.getInstance().getPOSTRoutes();
128 }
129 }
130
131 public LinkedHashMap<String, String> getParameterMap() {
132 return parameterMap;
133 }
134
135 public PragmatachRoute getPragmatachRoute() {
136 return pragmatachRoute;
137 }
138
139 /**
140 * Match route. Returns true if a matching route was found.
141 * <p>
142 *
143 * <pre>
144 * There are multiple criteria for matching.
145 * 1 - Every part of the URI must match the name or regex
146 * 2 - Values must be provided for each method signature variable
147 * </pre>
148 * <p>
149 */
150 public boolean match(Request request) throws PragmatachException {
151 try {
152 /*
153 * crack the URI
154 */
155 final String[] crackedURI = URICracker.crackURI(request.getResourcePath());
156 /*
157 * get the routes
158 */
159 final List<PragmatachRoute> pragmatachRoutes = getMethodRoutes(request.getMethod());
160 if (null != pragmatachRoutes) {
161 /*
162 * check for matching regexs
163 */
164 for (final PragmatachRoute pragmatachRoute : pragmatachRoutes) {
165 /*
166 * route is a regex match at each segment
167 */
168 if (matchesRouteSpecification(pragmatachRoute, crackedURI)) {
169 /*
170 * build parameter map
171 */
172 parameterMap = buildParameterMap(pragmatachRoute, crackedURI);
173 /*
174 * remember the matched controller
175 */
176 this.pragmatachRoute = pragmatachRoute;
177 /*
178 * return true to indicate a match
179 */
180 return true;
181 }
182 }
183 }
184 /*
185 * no match
186 */
187 return false;
188 } catch (final Exception e) {
189 throw new PragmatachException("Exception in match", e);
190 }
191 }
192
193 /**
194 * check that the number of segments matches and that each segment matches the regex, if there is one
195 */
196 private boolean matchesRouteSpecification(PragmatachRoute pragmatachRoute, String[] crackedURI) throws PragmatachException {
197 try {
198 if (false == pragmatachRoute.isWildcardRoute()) {
199 /*
200 * there is a uri?
201 */
202 if (null != crackedURI) {
203 /*
204 * # of segments passed matches the route specification number of segments?
205 */
206 if (crackedURI.length == pragmatachRoute.getSegmentCount()) {
207 /*
208 * walk the route annotations
209 */
210 int i = 0;
211 final List<RouteParameter> routeParameters = pragmatachRoute.getBoundRouteParameters();
212 if ((null != routeParameters) && (routeParameters.size() > 0)) {
213 for (final RouteParameter routeParameter : routeParameters) {
214 /*
215 * check regex
216 */
217 final String regex = routeParameter.regex();
218 if ((null != regex) && (regex.length() > 0)) {
219 if (false == crackedURI[i].matches(regex)) {
220 return false;
221 }
222 }
223 i++;
224 }
225 }
226 /*
227 * check that the static path parts match
228 */
229 if (false == staticPartsMatch(pragmatachRoute, crackedURI)) {
230 return false;
231 }
232 /*
233 * everything matches
234 */
235 return true;
236 } else {
237 /*
238 * its not a match
239 */
240 return false;
241 }
242 } else {
243 if ((0 == pragmatachRoute.getParameterCount()) && (0 == pragmatachRoute.getSegmentCount())) {
244 /*
245 * no parameters; its a match if the static parts match
246 */
247 return staticPartsMatch(pragmatachRoute, crackedURI);
248 } else {
249 /*
250 * the route requires parameters, and none were passed
251 */
252 return false;
253 }
254 }
255 } else {
256 /*
257 * check that the static path parts match
258 */
259 if (false == staticWildcardPartsMatch(pragmatachRoute, crackedURI)) {
260 return false;
261 }
262 /*
263 * matches
264 */
265 return true;
266 }
267 } catch (final Exception e) {
268 throw new PragmatachException("Exception in matchesRouteSpecification", e);
269 }
270 }
271 }