001: package com.opensymphony.webwork.dispatcher.mapper;
002:
003: import com.opensymphony.webwork.RequestUtils;
004: import com.opensymphony.webwork.WebWorkConstants;
005: import com.opensymphony.webwork.config.Configuration;
006: import com.opensymphony.webwork.dispatcher.ServletRedirectResult;
007: import com.opensymphony.webwork.util.PrefixTrie;
008:
009: import javax.servlet.http.HttpServletRequest;
010: import java.util.Arrays;
011: import java.util.Iterator;
012: import java.util.List;
013: import java.util.Map;
014:
015: /**
016: * <!-- START SNIPPET: javadoc -->
017: *
018: * Default action mapper implementation, using the standard *.[ext] (where ext usually "action") pattern. The extension
019: * is looked up from the WebWork configuration key <b>webwork.action.exection</b>.
020: *
021: * <p/> To help with dealing with buttons and other related requirements, this mapper (and other {@link ActionMapper}s,
022: * we hope) has the ability to name a button with some predefined prefix and have that button name alter the execution
023: * behaviour. The four prefixes are:
024: *
025: * <ul>
026: *
027: * <li>Method prefix - <i>method:default</i></li>
028: *
029: * <li>Action prefix - <i>action:dashboard</i></li>
030: *
031: * <li>Redirect prefix - <i>redirect:cancel.jsp</i></li>
032: *
033: * <li>Redirect-action prefix - <i>redirect-action:cancel</i></li>
034: *
035: * </ul>
036: *
037: * <p/> In addition to these four prefixes, this mapper also understands the action naming pattern of <i>foo!bar</i> in
038: * either the extension form (eg: foo!bar.action) or in the prefix form (eg: action:foo!bar). This syntax tells this mapper
039: * to map to the action named <i>foo</i> and the method <i>bar</i>.
040: *
041: * <!-- END SNIPPET: javadoc -->
042: *
043: * <p/> <b>Method Prefix</b> <p/>
044: *
045: * <!-- START SNIPPET: method -->
046: *
047: * With method-prefix, instead of calling baz action's execute() method (by default if it isn't overriden in xwork.xml
048: * to be something else), the baz action's anotherMethod() will be called. A very elegant way determine which button is
049: * clicked. Alternatively, one would have submit button set a particular value on the action when clicked, and the
050: * execute() method decides on what to do with the setted value depending on which button is clicked.
051: *
052: * <!-- END SNIPPET: method -->
053: *
054: * <pre>
055: * <!-- START SNIPPET: method-example -->
056: * <ww:form action="baz">
057: * <ww:textfield label="Enter your name" name="person.name"/>
058: * <ww:submit value="Create person"/>
059: * <ww:submit name="method:anotherMethod" value="Cancel"/>
060: * </ww:form>
061: * <!-- END SNIPPET: method-example -->
062: * </pre>
063: *
064: * <p/> <b>Action prefix</b> <p/>
065: *
066: * <!-- START SNIPPET: action -->
067: *
068: * With action-prefix, instead of executing baz action's execute() method (by default if it isn't overriden in xwork.xml
069: * to be something else), the anotherAction action's execute() method (assuming again if it isn't overriden with
070: * something else in xwork.xml) will be executed.
071: *
072: * <!-- END SNIPPET: action -->
073: *
074: * <pre>
075: * <!-- START SNIPPET: action-example -->
076: * <ww:form action="baz">
077: * <ww:textfield label="Enter your name" name="person.name"/>
078: * <ww:submit value="Create person"/>
079: * <ww:submit name="action:anotherAction" value="Cancel"/>
080: * </ww:form>
081: * <!-- END SNIPPET: action-example -->
082: * </pre>
083: *
084: * <p/> <b>Redirect prefix</b> <p/>
085: *
086: * <!-- START SNIPPET: redirect -->
087: *
088: * With redirect-prefix, instead of executing baz action's execute() method (by default it isn't overriden in xwork.xml
089: * to be something else), it will get redirected to, in this case to www.google.com. Internally it uses
090: * ServletRedirectResult to do the task.
091: *
092: * <!-- END SNIPPET: redirect -->
093: *
094: * <pre>
095: * <!-- START SNIPPET: redirect-example -->
096: * <ww:form action="baz">
097: * <ww:textfield label="Enter your name" name="person.name"/>
098: * <ww:submit value="Create person"/>
099: * <ww:submit name="redirect:www.google.com" value="Cancel"/>
100: * </ww:form>
101: * <!-- END SNIPPET: redirect-example -->
102: * </pre>
103: *
104: * <p/> <b>Redirect-action prefix</b> <p/>
105: *
106: * <!-- START SNIPPET: redirect-action -->
107: *
108: * With redirect-action-prefix, instead of executing baz action's execute() method (by default it isn't overriden in
109: * xwork.xml to be something else), it will get redirected to, in this case 'dashboard.action'. Internally it uses
110: * ServletRedirectResult to do the task and read off the extension from the webwork.properties.
111: *
112: * <!-- END SNIPPET: redirect-action -->
113: *
114: * <pre>
115: * <!-- START SNIPPET: redirect-action-example -->
116: * <ww:form action="baz">
117: * <ww:textfield label="Enter your name" name="person.name"/>
118: * <ww:submit value="Create person"/>
119: * <ww:submit name="redirect-action:dashboard" value="Cancel"/>
120: * </ww:form>
121: * <!-- END SNIPPET: redirect-action-example -->
122: * </pre>
123: *
124: * @author Patrick Lightbody
125: * @author tm_jee
126: * @version $Date: 2007-03-24 08:16:10 +0100 (Sat, 24 Mar 2007) $ $Id: DefaultActionMapper.java 2872 2007-03-24 07:16:10Z tm_jee $
127: */
128: public class DefaultActionMapper implements ActionMapper {
129:
130: static final String METHOD_PREFIX = "method:";
131: static final String ACTION_PREFIX = "action:";
132: static final String REDIRECT_PREFIX = "redirect:";
133: static final String REDIRECT_ACTION_PREFIX = "redirect-action:";
134:
135: static PrefixTrie prefixTrie = new PrefixTrie() {
136: {
137: put(METHOD_PREFIX, new ParameterAction() {
138: public void execute(String key, ActionMapping mapping) {
139: mapping.setMethod(key.substring(METHOD_PREFIX
140: .length()));
141: }
142: });
143:
144: put(ACTION_PREFIX, new ParameterAction() {
145: public void execute(String key, ActionMapping mapping) {
146: String name = key.substring(ACTION_PREFIX.length());
147: int bang = name.indexOf('!');
148: if (bang != -1) {
149: String method = name.substring(bang + 1);
150: mapping.setMethod(method);
151: name = name.substring(0, bang);
152: }
153:
154: mapping.setName(name);
155: }
156: });
157:
158: put(REDIRECT_PREFIX, new ParameterAction() {
159: public void execute(String key, ActionMapping mapping) {
160: ServletRedirectResult redirect = new ServletRedirectResult();
161: redirect.setLocation(key.substring(REDIRECT_PREFIX
162: .length()));
163: mapping.setResult(redirect);
164: }
165: });
166:
167: put(REDIRECT_ACTION_PREFIX, new ParameterAction() {
168: public void execute(String key, ActionMapping mapping) {
169: String location = key
170: .substring(REDIRECT_ACTION_PREFIX.length());
171: ServletRedirectResult redirect = new ServletRedirectResult();
172: String extension = getDefaultExtension();
173: if (extension != null) {
174: location += "." + extension;
175: }
176: redirect.setLocation(location);
177: mapping.setResult(redirect);
178: }
179: });
180: }
181: };
182:
183: public ActionMapping getMapping(HttpServletRequest request) {
184: ActionMapping mapping = new ActionMapping();
185: String uri = getUri(request);
186:
187: parseNameAndNamespace(uri, mapping);
188:
189: handleSpecialParameters(request, mapping);
190:
191: if (mapping.getName() == null) {
192: return null;
193: }
194:
195: // handle "name!method" convention.
196: String name = mapping.getName();
197: int exclamation = name.lastIndexOf("!");
198: if (exclamation != -1) {
199: mapping.setName(name.substring(0, exclamation));
200: mapping.setMethod(name.substring(exclamation + 1));
201: }
202: return mapping;
203: }
204:
205: /**
206: * Extension hook, that handle special parameters, namely those with prefix
207: * eg.
208: * <ul>
209: * <li>action:someAction</li>
210: * <li>method:someMethod</li>
211: * <li>redirect:http://www.google.com</li>
212: * <li>redirect-action:someRedirectAction</li>
213: * </ul>
214: * @param request
215: * @param mapping
216: */
217: protected void handleSpecialParameters(HttpServletRequest request,
218: ActionMapping mapping) {
219: // handle special parameter prefixes.
220: Map parameterMap = request.getParameterMap();
221: for (Iterator iterator = parameterMap.keySet().iterator(); iterator
222: .hasNext();) {
223: String key = (String) iterator.next();
224: ParameterAction parameterAction = (ParameterAction) prefixTrie
225: .get(key);
226: if (parameterAction != null) {
227: parameterAction.execute(key, mapping);
228: break;
229: }
230: }
231: }
232:
233: protected void parseNameAndNamespace(String uri,
234: ActionMapping mapping) {
235: String namespace, name;
236: int lastSlash = uri.lastIndexOf("/");
237: if (lastSlash == -1) {
238: namespace = "";
239: name = uri;
240: } else if (lastSlash == 0) {
241: // ww-1046, assume it is the root namespace, it will fallback to default
242: // namespace anyway if not found in root namespace.
243: namespace = "/";
244: name = uri.substring(lastSlash + 1);
245: } else {
246: namespace = uri.substring(0, lastSlash);
247: name = uri.substring(lastSlash + 1);
248: }
249: mapping.setNamespace(namespace);
250: mapping.setName(dropExtension(name));
251: }
252:
253: String dropExtension(String name) {
254: List extensions = getExtensions();
255: if (extensions == null) {
256: return name;
257: }
258: Iterator it = extensions.iterator();
259: while (it.hasNext()) {
260: String extension = "." + (String) it.next();
261: if (name.endsWith(extension)) {
262: name = name.substring(0, name.length()
263: - extension.length());
264: return name;
265: }
266: }
267: return null;
268: }
269:
270: /**
271: * Returns null if no extension is specified.
272: */
273: static String getDefaultExtension() {
274: List extensions = getExtensions();
275: if (extensions == null) {
276: return null;
277: } else {
278: return (String) extensions.get(0);
279: }
280: }
281:
282: /**
283: * Returns null if no extension is specified.
284: */
285: static List getExtensions() {
286: String extensions = (String) Configuration
287: .get(WebWorkConstants.WEBWORK_ACTION_EXTENSION);
288:
289: if (extensions.equals("")) {
290: return null;
291: } else {
292: return Arrays.asList(extensions.split(","));
293: }
294: }
295:
296: String getUri(HttpServletRequest request) {
297: // handle http dispatcher includes.
298: String uri = (String) request
299: .getAttribute("javax.servlet.include.servlet_path");
300: if (uri != null) {
301: return uri;
302: }
303:
304: uri = RequestUtils.getServletPath(request);
305: if (uri != null && !"".equals(uri)) {
306: return uri;
307: }
308:
309: uri = request.getRequestURI();
310: return uri.substring(request.getContextPath().length());
311: }
312:
313: public String getUriFromActionMapping(ActionMapping mapping) {
314: StringBuffer uri = new StringBuffer();
315:
316: uri.append(mapping.getNamespace());
317: if (!"/".equals(mapping.getNamespace())) {
318: uri.append("/");
319: }
320: String name = mapping.getName();
321: String params = "";
322: if (name.indexOf('?') != -1) {
323: params = name.substring(name.indexOf('?'));
324: name = name.substring(0, name.indexOf('?'));
325: }
326: uri.append(name);
327:
328: if (null != mapping.getMethod()
329: && !"".equals(mapping.getMethod())) {
330: uri.append("!").append(mapping.getMethod());
331: }
332:
333: String extension = getDefaultExtension();
334: if (extension != null && uri.indexOf('.' + extension) == -1) {
335: uri.append(".").append(extension);
336: if (params.length() > 0) {
337: uri.append(params);
338: }
339: }
340:
341: return uri.toString();
342: }
343:
344: interface ParameterAction {
345: void execute(String key, ActionMapping mapping);
346: }
347: }
|