Save This Page
Home » sitemesh-2.3 » com.opensymphony.module » sitemesh » mapper » [javadoc | source]
    1   /*
    2    * Title:        PathMapper
    3    * Description:
    4    *
    5    * This software is published under the terms of the OpenSymphony Software
    6    * License version 1.1, of which a copy has been included with this
    7    * distribution in the LICENSE.txt file.
    8    */
    9   
   10   package com.opensymphony.module.sitemesh.mapper;
   11   
   12   import java.util.HashMap;
   13   import java.util.Map;
   14   import java.util.Iterator;
   15   
   16   /**
   17    * The PathMapper is used to map file patterns to keys, and find an approriate
   18    * key for a given file path. The pattern rules are consistent with those defined
   19    * in the Servlet 2.3 API on the whole. Wildcard patterns are also supported, using
   20    * any combination of * and ?.
   21    *
   22    * <h3>Example</h3>
   23    *
   24    * <blockquote><code>
   25    * PathMapper pm = new PathMapper();<br>
   26    * <br>
   27    * pm.put("one","/");<br>
   28    * pm.put("two","/mydir/*");<br>
   29    * pm.put("three","*.xml");<br>
   30    * pm.put("four","/myexactfile.html");<br>
   31    * pm.put("five","/*\/admin/*.??ml");<br>
   32    * <br>
   33    * String result1 = pm.get("/mydir/myfile.xml"); // returns "two";<br>
   34    * String result2 = pm.get("/mydir/otherdir/admin/myfile.html"); // returns "five";<br>
   35    * </code></blockquote>
   36    *
   37    * @author <a href="mailto:joe@truemesh.com">Joe Walnes</a>
   38    * @author <a href="mailto:mcannon@internet.com">Mike Cannon-Brookes</a>
   39    * @author <a href="mailto:hani@formicary.net">Hani Suleiman</a>
   40    * @version $Revision: 1.2 $
   41    */
   42   public final class PathMapper {
   43       private Map mappings = new HashMap();
   44   
   45       /** Add a key and appropriate matching pattern. */
   46       public void put(String key, String pattern) {
   47           if (key != null) {
   48               mappings.put(pattern, key);
   49           }
   50       }
   51   
   52       /** Retrieve appropriate key by matching patterns with supplied path. */
   53       public String get(String path) {
   54           if (path == null) path = "/";
   55           String mapped = findKey(path, mappings);
   56           if (mapped == null) return null;
   57           return (String) mappings.get(mapped);
   58       }
   59   
   60       /** Find exact key in mappings. */
   61       private static String findKey(String path, Map mappings) {
   62           String result = findExactKey(path, mappings);
   63           if (result == null) result = findComplexKey(path, mappings);
   64           if (result == null) result = findDefaultKey(mappings);
   65           return result;
   66       }
   67   
   68       /** Check if path matches exact pattern ( /blah/blah.jsp ). */
   69       private static String findExactKey(String path, Map mappings) {
   70           if (mappings.containsKey(path)) return path;
   71           return null;
   72       }
   73   
   74       private static String findComplexKey(String path, Map mappings) {
   75           Iterator i = mappings.keySet().iterator();
   76           String result = null, key = null;
   77           while (i.hasNext()) {
   78               key = (String) i.next();
   79               if (key.length() > 1 && (key.indexOf('?') != -1 || key.indexOf('*') != -1) && match(key, path, false)) {
   80                   if (result == null || key.length() > result.length()) {
   81                       // longest key wins
   82                       result = key;
   83                   }
   84               }
   85           }
   86           return result;
   87       }
   88   
   89       /** Look for root pattern ( / ). */
   90       private static String findDefaultKey(Map mappings) {
   91           String[] defaultKeys = {"/", "*", "/*"};
   92           for (int i = 0; i < defaultKeys.length; i++) {
   93               if (mappings.containsKey(defaultKeys[i])) return defaultKeys[i];
   94           }
   95           return null;
   96       }
   97   
   98       private static boolean match(String pattern, String str, boolean isCaseSensitive) {
   99           char[] patArr = pattern.toCharArray();
  100           char[] strArr = str.toCharArray();
  101           int patIdxStart = 0;
  102           int patIdxEnd = patArr.length - 1;
  103           int strIdxStart = 0;
  104           int strIdxEnd = strArr.length - 1;
  105           char ch;
  106   
  107           boolean containsStar = false;
  108           for (int i = 0; i < patArr.length; i++) {
  109               if (patArr[i] == '*') {
  110                   containsStar = true;
  111                   break;
  112               }
  113           }
  114   
  115           if (!containsStar) {
  116               // No '*'s, so we make a shortcut
  117               if (patIdxEnd != strIdxEnd) {
  118                   return false; // Pattern and string do not have the same size
  119               }
  120               for (int i = 0; i <= patIdxEnd; i++) {
  121                   ch = patArr[i];
  122                   if (ch != '?') {
  123                       if (isCaseSensitive && ch != strArr[i]) {
  124                           return false; // Character mismatch
  125                       }
  126                       if (!isCaseSensitive && Character.toUpperCase(ch) !=
  127                               Character.toUpperCase(strArr[i])) {
  128                           return false; // Character mismatch
  129                       }
  130                   }
  131               }
  132               return true; // String matches against pattern
  133           }
  134   
  135           if (patIdxEnd == 0) {
  136               return true; // Pattern contains only '*', which matches anything
  137           }
  138   
  139           // Process characters before first star
  140           while ((ch = patArr[patIdxStart]) != '*' && strIdxStart <= strIdxEnd) {
  141               if (ch != '?') {
  142                   if (isCaseSensitive && ch != strArr[strIdxStart]) {
  143                       return false; // Character mismatch
  144                   }
  145                   if (!isCaseSensitive && Character.toUpperCase(ch) !=
  146                           Character.toUpperCase(strArr[strIdxStart])) {
  147                       return false; // Character mismatch
  148                   }
  149               }
  150               patIdxStart++;
  151               strIdxStart++;
  152           }
  153           if (strIdxStart > strIdxEnd) {
  154               // All characters in the string are used. Check if only '*'s are
  155               // left in the pattern. If so, we succeeded. Otherwise failure.
  156               for (int i = patIdxStart; i <= patIdxEnd; i++) {
  157                   if (patArr[i] != '*') {
  158                       return false;
  159                   }
  160               }
  161               return true;
  162           }
  163   
  164           // Process characters after last star
  165           while ((ch = patArr[patIdxEnd]) != '*' && strIdxStart <= strIdxEnd) {
  166               if (ch != '?') {
  167                   if (isCaseSensitive && ch != strArr[strIdxEnd]) {
  168                       return false; // Character mismatch
  169                   }
  170                   if (!isCaseSensitive && Character.toUpperCase(ch) !=
  171                           Character.toUpperCase(strArr[strIdxEnd])) {
  172                       return false; // Character mismatch
  173                   }
  174               }
  175               patIdxEnd--;
  176               strIdxEnd--;
  177           }
  178           if (strIdxStart > strIdxEnd) {
  179               // All characters in the string are used. Check if only '*'s are
  180               // left in the pattern. If so, we succeeded. Otherwise failure.
  181               for (int i = patIdxStart; i <= patIdxEnd; i++) {
  182                   if (patArr[i] != '*') {
  183                       return false;
  184                   }
  185               }
  186               return true;
  187           }
  188   
  189           // process pattern between stars. padIdxStart and patIdxEnd point
  190           // always to a '*'.
  191           while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
  192               int patIdxTmp = -1;
  193               for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
  194                   if (patArr[i] == '*') {
  195                       patIdxTmp = i;
  196                       break;
  197                   }
  198               }
  199               if (patIdxTmp == patIdxStart + 1) {
  200                   // Two stars next to each other, skip the first one.
  201                   patIdxStart++;
  202                   continue;
  203               }
  204               // Find the pattern between padIdxStart & padIdxTmp in str between
  205               // strIdxStart & strIdxEnd
  206               int patLength = (patIdxTmp - patIdxStart - 1);
  207               int strLength = (strIdxEnd - strIdxStart + 1);
  208               int foundIdx = -1;
  209               strLoop:
  210               for (int i = 0; i <= strLength - patLength; i++) {
  211                   for (int j = 0; j < patLength; j++) {
  212                       ch = patArr[patIdxStart + j + 1];
  213                       if (ch != '?') {
  214                           if (isCaseSensitive && ch != strArr[strIdxStart + i + j]) {
  215                               continue strLoop;
  216                           }
  217                           if (!isCaseSensitive && Character.toUpperCase(ch) !=
  218                                   Character.toUpperCase(strArr[strIdxStart + i + j])) {
  219                               continue strLoop;
  220                           }
  221                       }
  222                   }
  223   
  224                   foundIdx = strIdxStart + i;
  225                   break;
  226               }
  227   
  228               if (foundIdx == -1) {
  229                   return false;
  230               }
  231   
  232               patIdxStart = patIdxTmp;
  233               strIdxStart = foundIdx + patLength;
  234           }
  235   
  236           // All characters in the string are used. Check if only '*'s are left
  237           // in the pattern. If so, we succeeded. Otherwise failure.
  238           for (int i = patIdxStart; i <= patIdxEnd; i++) {
  239               if (patArr[i] != '*') {
  240                   return false;
  241               }
  242           }
  243           return true;
  244       }
  245   }

Save This Page
Home » sitemesh-2.3 » com.opensymphony.module » sitemesh » mapper » [javadoc | source]