1 package org.seasar.cubby.convention.impl; 2 3 import java.io.IOException; 4 import java.lang.reflect.Method; 5 import java.net.URLDecoder; 6 import java.util.ArrayList; 7 import java.util.HashMap; 8 import java.util.LinkedHashMap; 9 import java.util.List; 10 import java.util.Map; 11 import java.util.regex.Matcher; 12 import java.util.regex.Pattern; 13 14 import org.seasar.cubby.action.Action; 15 import org.seasar.cubby.convention.ForwardInfo; 16 import org.seasar.cubby.convention.PathResolver; 17 import org.seasar.cubby.util.CubbyUtils; 18 import org.seasar.cubby.util.Uri; 19 import org.seasar.framework.convention.NamingConvention; 20 import org.seasar.framework.exception.IORuntimeException; 21 import org.seasar.framework.log.Logger; 22 import org.seasar.framework.util.ClassUtil; 23 import org.seasar.framework.util.Disposable; 24 import org.seasar.framework.util.DisposableUtil; 25 import org.seasar.framework.util.StringUtil; 26 27 public class PathResolverImpl implements PathResolver, Disposable { 28 29 private static Pattern urlRewritePattern = Pattern 30 .compile("([{]([^}]+)[}])([^{]*)"); 31 32 private final Logger logger = Logger.getLogger(this.getClass()); 33 34 private boolean initialized; 35 36 private NamingConvention namingConvention; 37 38 private Map<Pattern, RewriteInfo> patternToRewriteInfoMap = new LinkedHashMap<Pattern, RewriteInfo>(); 39 40 private Map<Pattern, RewriteInfo> customPatternToRewriteInfoMap = new LinkedHashMap<Pattern, RewriteInfo>(); 41 42 private String uriEncoding; 43 44 public void setUriEncoding(final String uriEncoding) { 45 this.uriEncoding = uriEncoding; 46 } 47 48 public void initialize() { 49 if (!initialized) { 50 patternToRewriteInfoMap.clear(); 51 ClassCollector classCollector = new ActionClassCollector(); 52 classCollector.collect(); 53 54 DisposableUtil.add(this); 55 initialized = true; 56 } 57 } 58 59 public void dispose() { 60 patternToRewriteInfoMap.clear(); 61 initialized = false; 62 } 63 64 public void add(final String patternStr, 65 final Class<? extends Action> actionClass, final String methodName) { 66 67 final Method method = ClassUtil.getMethod(actionClass, methodName, 68 new Class<?>[0]); 69 this.add(patternStr, actionClass, method, customPatternToRewriteInfoMap); 70 } 71 72 private void add(final String patternStr, 73 final Class<? extends Action> actionClass, final Method method, 74 Map<Pattern, RewriteInfo> patternToRewriteInfoMap) { 75 76 String actionFullName = patternStr; 77 final List<String> uriParameterNames = new ArrayList<String>(); 78 final Matcher matcher = urlRewritePattern.matcher(actionFullName); 79 while (matcher.find()) { 80 final String name = matcher.group(2); 81 final String[] names = name.split(",", 2); 82 if (names.length == 1) { 83 actionFullName = StringUtil.replace(actionFullName, matcher 84 .group(1), "([a-zA-Z0-9]+)"); 85 uriParameterNames.add(matcher.group(2)); 86 } else { 87 actionFullName = StringUtil.replace(actionFullName, matcher 88 .group(1), "(" + names[1] + ")"); 89 uriParameterNames.add(names[0]); 90 } 91 } 92 93 final String rewritePath = this.fromActionClassToPath(actionClass, 94 method); 95 96 final RewriteInfo rewriteInfo = new RewriteInfo(actionClass, method, 97 uriParameterNames, rewritePath); 98 final Pattern pattern = Pattern.compile("^" + actionFullName + "$"); 99 100 patternToRewriteInfoMap.put(pattern, rewriteInfo); 101 if (logger.isDebugEnabled()) { 102 logger.log("DCUB0007", new Object[] { actionFullName, method, 103 uriParameterNames }); 104 } 105 } 106 107 private String fromActionClassToPath(final Class<? extends Action> actionClass, 108 final Method method) { 109 final String componentName = namingConvention 110 .fromClassNameToComponentName(actionClass.getCanonicalName()); 111 final StringBuilder builder = new StringBuilder(100); 112 builder.append('/'); 113 builder.append(componentName.substring( 114 0, 115 componentName.length() 116 - namingConvention.getActionSuffix().length()) 117 .replaceAll("_", "/")); 118 builder.append('/'); 119 builder.append(method.getName()); 120 return builder.toString(); 121 } 122 123 public ForwardInfo getForwardInfo(final String path) { 124 if (logger.isDebugEnabled()) { 125 logger.log("DCUB0006", new Object[] { path }); 126 } 127 128 initialize(); 129 130 ForwardInfo forwardInfo = findForwardInfo(path, customPatternToRewriteInfoMap); 131 if (forwardInfo == null) { 132 forwardInfo = findForwardInfo(path, patternToRewriteInfoMap); 133 } 134 return forwardInfo; 135 } 136 137 private ForwardInfo findForwardInfo(final String path, 138 final Map<Pattern, RewriteInfo> patternToRewriteInfoMap) { 139 final Map<String, String> uriParams = new HashMap<String, String>(); 140 for (final Pattern p : patternToRewriteInfoMap.keySet()) { 141 final Matcher matcher = p.matcher(path); 142 if (matcher.find()) { 143 final RewriteInfo rewriteInfo = patternToRewriteInfoMap.get(p); 144 for (int i = 1; i < matcher.groupCount() + 1; i++) { 145 final String name = rewriteInfo.getUriParameterNames() 146 .get(i - 1); 147 final String value; 148 try { 149 value = URLDecoder.decode(matcher.group(i), uriEncoding); 150 } catch (final IOException e) { 151 throw new IORuntimeException(e); 152 } 153 uriParams.put(name, value); 154 } 155 final ForwardInfoImpl forwardInfo = new ForwardInfoImpl( 156 rewriteInfo, uriParams); 157 return forwardInfo; 158 } 159 } 160 return null; 161 } 162 163 public void setNamingConvention(final NamingConvention namingConvention) { 164 this.namingConvention = namingConvention; 165 } 166 167 class RewriteInfo { 168 169 private final Class<? extends Action> actionClass; 170 171 private final Method method; 172 173 private final List<String> uriParameterNames; 174 175 private final String rewritePath; 176 177 public RewriteInfo( 178 final Class<? extends Action> actionClass, 179 final Method method, 180 final List<String> uriParameterNames, 181 final String rewritePath) { 182 this.actionClass = actionClass; 183 this.method = method; 184 this.uriParameterNames = uriParameterNames; 185 this.rewritePath = rewritePath; 186 } 187 188 public String buildRewritePath(final Map<String, String> uriParams) { 189 final StringBuilder builder = new StringBuilder(100); 190 builder.append(rewritePath); 191 if (!uriParams.isEmpty()) { 192 builder.append("?"); 193 final Uri uri = new Uri(); 194 for (final String key : uriParams.keySet()) { 195 final String value = uriParams.get(key); 196 uri.setParam(key, value); 197 } 198 builder.append(uri.getQueryString()); 199 } 200 return builder.toString(); 201 } 202 203 public Class<? extends Action> getActionClass() { 204 return actionClass; 205 } 206 207 public Method getMethod() { 208 return method; 209 } 210 211 public List<String> getUriParameterNames() { 212 return uriParameterNames; 213 } 214 215 public String getRewritePath() { 216 return rewritePath; 217 } 218 219 } 220 221 class ActionClassCollector extends ClassCollector { 222 223 public ActionClassCollector() { 224 super(namingConvention); 225 } 226 227 public void processClass(String packageName, String shortClassName) { 228 if (shortClassName.indexOf('$') != -1) { 229 return; 230 } 231 String className = ClassUtil 232 .concatName(packageName, shortClassName); 233 if (!namingConvention.isTargetClassName(className)) { 234 return; 235 } 236 if (!className.endsWith(namingConvention.getActionSuffix())) { 237 return; 238 } 239 Class<? extends Action> clazz = classForName(className); 240 if (namingConvention.isSkipClass(clazz)) { 241 return; 242 } 243 244 for (final Method method : clazz.getMethods()) { 245 if (CubbyUtils.isActionMethod(method)) { 246 final String actionFullName = CubbyUtils 247 .getActionUrl(clazz, method); 248 add(actionFullName, clazz, method, patternToRewriteInfoMap); 249 } 250 } 251 } 252 253 } 254 255 @SuppressWarnings("unchecked") 256 private static <T> Class<T> classForName(String className) { 257 return ClassUtil.forName(className); 258 } 259 260 }