View Javadoc

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 }