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    }