001: /* ====================================================================
002: * The Jcorporate Apache Style Software License, Version 1.2 05-07-2002
003: *
004: * Copyright (c) 1995-2002 Jcorporate Ltd. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * 1. Redistributions of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * 2. Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * 3. The end-user documentation included with the redistribution,
019: * if any, must include the following acknowledgment:
020: * "This product includes software developed by Jcorporate Ltd.
021: * (http://www.jcorporate.com/)."
022: * Alternately, this acknowledgment may appear in the software itself,
023: * if and wherever such third-party acknowledgments normally appear.
024: *
025: * 4. "Jcorporate" and product names such as "Expresso" must
026: * not be used to endorse or promote products derived from this
027: * software without prior written permission. For written permission,
028: * please contact info@jcorporate.com.
029: *
030: * 5. Products derived from this software may not be called "Expresso",
031: * or other Jcorporate product names; nor may "Expresso" or other
032: * Jcorporate product names appear in their name, without prior
033: * written permission of Jcorporate Ltd.
034: *
035: * 6. No product derived from this software may compete in the same
036: * market space, i.e. framework, without prior written permission
037: * of Jcorporate Ltd. For written permission, please contact
038: * partners@jcorporate.com.
039: *
040: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
041: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
042: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
043: * DISCLAIMED. IN NO EVENT SHALL JCORPORATE LTD OR ITS CONTRIBUTORS
044: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
045: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
046: * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
047: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
048: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
049: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
050: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
051: * SUCH DAMAGE.
052: * ====================================================================
053: *
054: * This software consists of voluntary contributions made by many
055: * individuals on behalf of the Jcorporate Ltd. Contributions back
056: * to the project(s) are encouraged when you make modifications.
057: * Please send them to support@jcorporate.com. For more information
058: * on Jcorporate Ltd. and its products, please see
059: * <http://www.jcorporate.com/>.
060: *
061: * Portions of this software are based upon other open source
062: * products and are subject to their respective licenses.
063: */
064:
065: package com.jcorporate.expresso.core.controller;
066:
067: import com.jcorporate.expresso.core.misc.StringUtil;
068: import com.jcorporate.expresso.core.misc.URLUTF8Encoder;
069: import org.apache.commons.logging.Log;
070: import org.apache.commons.logging.LogFactory;
071: import org.apache.commons.validator.Validator;
072: import org.apache.commons.validator.ValidatorException;
073: import org.apache.commons.validator.ValidatorResources;
074: import org.apache.commons.validator.ValidatorResults;
075: import org.apache.struts.action.ActionErrors;
076: import org.apache.struts.action.ActionForm;
077: import org.apache.struts.action.ActionMapping;
078: import org.apache.struts.validator.Resources;
079:
080: import javax.servlet.ServletContext;
081: import javax.servlet.ServletRequest;
082: import javax.servlet.http.HttpServletRequest;
083: import java.io.Serializable;
084: import java.util.Enumeration;
085: import java.util.Hashtable;
086: import java.util.Iterator;
087: import java.util.Locale;
088: import java.util.StringTokenizer;
089:
090: /**
091: * * <p> This class is a bridge between Expresso's Controller system and the Struts
092: * Action Form. It is automatically utilized by Expresso and you don't have
093: * to directly deal with it. </p>
094: *
095: * @author Michael Nash
096: * @since Expresso 4.0
097: */
098: public class DefaultForm extends ActionForm implements Serializable,
099: StateForm {
100: private Hashtable formData = new Hashtable();
101: private Hashtable formAttributes = null;
102:
103: public DefaultForm() {
104: }
105:
106: public ActionErrors validate(ActionMapping mapping,
107: ServletRequest request) {
108: return null;
109: }
110:
111: public void saveForm(ControllerRequest req)
112: throws ControllerException {
113: formData.clear();
114: formData.putAll(req.getParameters());
115: }
116:
117: public void restoreForm(ControllerRequest req)
118: throws ControllerException {
119: String oneKey = null;
120:
121: for (Enumeration e = formData.elements(); e.hasMoreElements();) {
122: oneKey = (String) e.nextElement();
123: req.setParameter(oneKey, (String) formData.get(oneKey));
124: }
125: }
126:
127: public Hashtable getFields() {
128: return formData;
129: }
130:
131: public void clear() {
132: formData.clear();
133: }
134:
135: public synchronized void setAttribute(String fieldName,
136: Object newValue) {
137: if (formAttributes == null) {
138: formAttributes = new Hashtable();
139: }
140:
141: formAttributes.put(fieldName, newValue);
142: }
143:
144: public synchronized void setField(String fieldName,
145: String fieldValue) throws ControllerException {
146:
147: if (fieldName == null || fieldName.length() == 0) {
148: throw new ControllerException(
149: "Field name cannot be null here");
150: }
151: formData.put(fieldName, fieldValue);
152: }
153:
154: /**
155: *
156: */
157: public synchronized void setUsingHashtableParameters(
158: Hashtable parameters) throws ControllerException {
159: for (Iterator i = parameters.keySet().iterator(); i.hasNext();) {
160: String key = (String) i.next();
161: setField(key, (String) parameters.get(key));
162: }
163: }
164:
165: /**
166: * Returns the attribute that has been set for a particular field OR
167: * null if it doesn't exist.
168: *
169: * @throws ControllerException if fieldName is blank.
170: */
171: public Object getAttribute(String fieldName)
172: throws ControllerException {
173: if (formAttributes == null) {
174: return null;
175: }
176:
177: if (fieldName == null || fieldName.length() == 0) {
178: throw new ControllerException(
179: "Field name cannot be null here");
180: }
181:
182: return formAttributes.get(fieldName);
183:
184: }
185:
186: /**
187: * Returns the attributes Hashtable.
188: */
189: public Hashtable getAttributes() {
190:
191: return formAttributes;
192:
193: }
194:
195: public String getField(String fieldName) throws ControllerException {
196: if (StringUtil.notNull(fieldName).equals("")) {
197: throw new ControllerException(
198: "Field name cannot be null here");
199: }
200:
201: return (String) formData.get(fieldName);
202: }
203:
204: /**
205: * Validate the properties that have been set from this HTTP request,
206: * and return an <code>ActionErrors</code> object that encapsulates any
207: * validation errors that have been found. If no errors are found, return
208: * <code>null</code> or an <code>ActionErrors</code> object with no
209: * recorded error messages.
210: *
211: * @param mapping The mapping used to select this instance.
212: * @param request The servlet request we are processing.
213: * @return ActionErrors containing validation errors.
214: */
215: /**
216: * @todo add this method so validation can be called from an external State *RD* Mon Jul 27 2004
217: */
218: public ActionErrors validate(ActionMapping mapping,
219: HttpServletRequest request) {
220:
221: ServletContext application = getServlet().getServletContext();
222: ActionErrors errors = new ErrorCollection();
223:
224: try {
225: Validator validator = initValidator(getState(request),
226: this , application, request, errors, page);
227:
228: validatorResults = validator.validate();
229: } catch (ControllerException ex) {
230: } catch (ValidatorException e) {
231: log.error(e.getMessage(), e);
232: }
233:
234: return errors;
235: }
236:
237: /**
238: * Validate the properties that have been set from this HTTP request,
239: * and return an <code>ActionErrors</code> object that encapsulates any
240: * validation errors that have been found. If no errors are found, return
241: * <code>null</code> or an <code>ActionErrors</code> object with no
242: * recorded error messages.
243: *
244: * @param mapping The mapping used to select this instance.
245: * @param request The servlet request we are processing.
246: * @return ActionErrors containing validation errors.
247: */
248: /**
249: * @todo add this method so the form can be validated with Struts Validator *RD* Mon Jul 27 2004
250: */
251: public ErrorCollection validate(ControllerRequest request) {
252: if (request instanceof ServletControllerRequest) {
253: ServletControllerRequest scr = (ServletControllerRequest) request;
254: return (ErrorCollection) this .validate(scr.getMapping(),
255: (HttpServletRequest) scr.getServletRequest());
256: } else {
257: return new ErrorCollection();
258: }
259: }
260:
261: /**
262: * Get the state requested by the transition button in the form
263: * @param params the hashtable of parameters to parse
264: * @return String
265: * @throws ControllerException
266: */
267: /** @todo add this method so Struts Validator can find the formset containing the rules *RD* Mon Jul 27 2004 */
268: /**
269: * @todo I'm sure there must be a more elegant way for doing this *RD* Mon Jul 27 2004
270: */
271: private static String getState(HttpServletRequest request)
272: throws ControllerException {
273:
274: log.debug("Adding button parameters");
275:
276: String oneParamName = null;
277: Object oneParamValue = null;
278: boolean gotControllerFromButton = false;
279:
280: for (Enumeration e = request.getParameterNames(); e
281: .hasMoreElements();) {
282: oneParamName = (String) e.nextElement();
283: oneParamValue = StringUtil.notNull(request
284: .getParameter(oneParamName));
285:
286: if (oneParamName.startsWith("button_")) {
287:
288: //We add a special parameter to the collection, which
289: //is simply "button"="name_ofButton"
290: String buttonName = oneParamName.substring(oneParamName
291: .indexOf("_") + 1);
292:
293: /**
294: * button names come in from the browser like
295:
296: button_promptLogin.y
297: button_promptLogin.x
298:
299: cut off those .x, .y trailers
300: */
301: if (buttonName.endsWith(".x")) {
302: buttonName = buttonName.substring(0, buttonName
303: .length() - 2);
304: }
305: if (buttonName.endsWith(".y")) {
306: buttonName = buttonName.substring(0, buttonName
307: .length() - 2);
308: }
309:
310: if (log.isDebugEnabled()) {
311: log.debug("There is a button parameter called '"
312: + buttonName + "'");
313: }
314:
315: /* pick up the corresponding parameter string, if any */
316: String paramString = (String) request
317: .getParameter(buttonName + "_params");
318:
319: if (paramString != null) {
320: log.debug("Button parameters:");
321:
322: //We first check to see if the button parameters string is encoded. If it is, we decode it
323: //before parsing it.
324: String encodeType = (String) request
325: .getParameter(buttonName + "_encoding");
326:
327: if ("u".equals(encodeType)) {
328:
329: //this param string has been URL encoded
330: try {
331: paramString = URLUTF8Encoder
332: .decode(paramString);
333: } catch (Exception ex) {
334: log.error("Could not URLDecode: "
335: + paramString, ex);
336: }
337: }
338: /* if the param string for the button was encoded */
339:
340: /* parse it into a hashtable */
341: StringTokenizer stkpm = null;
342: stkpm = new StringTokenizer(paramString, "&");
343:
344: while (stkpm.hasMoreTokens()) {
345: String pairValue = stkpm.nextToken();
346:
347: //This decode is paired up with the getParamString() method's encode() in
348: //the Transition class.
349: try {
350: pairValue = URLUTF8Encoder
351: .decode(pairValue);
352: } catch (Exception ex) {
353: log.error("Could not URLDecode: "
354: + pairValue, ex);
355: }
356:
357: String paramName = pairValue;
358: String paramValue = "";
359: int position = pairValue.indexOf("=");
360: if (position != -1) {
361: paramName = pairValue
362: .substring(0, position);
363: position++; //ignore the '='
364: if (position < pairValue.length()) {
365: paramValue = pairValue
366: .substring(position);
367: }
368: }
369:
370: if (paramName
371: .equals(Controller.CONTROLLER_PARAM_KEY)) {
372: gotControllerFromButton = true;
373: }
374: if (log.isDebugEnabled()) {
375: log.debug("Parameter '" + paramName
376: + "', value '" + paramValue + "'");
377: }
378: if (paramName
379: .equals(Controller.STATE_PARAM_KEY)) {
380: return paramValue;
381: }
382:
383: }
384: /* while more parameters in the button param string */
385:
386: log.debug("End button parameters");
387: } else { /* if the button param string exists */
388: throw new ControllerException("Button '"
389: + buttonName + "' was clicked, but no "
390: + "button parameters field called '"
391: + buttonName + "_params' was found.");
392: }
393: }
394: /* if this param is a buitton */
395:
396: }
397: /* for each of the parameters */
398:
399: return "";
400: }
401:
402: /**
403: * Initialize the <code>Validator</code> to perform validation.
404: *
405: * @param key The key that the validation rules are under (the form elements
406: * name attribute).
407: * @param bean The bean validation is being performed on.
408: * @param application servlet context
409: * @param request The current request object.
410: * @param errors The object any errors will be stored in.
411: * @param page This in conjunction with the page property of a
412: * <code>Field<code> can control the processing of fields. If the field's
413: * page is less than or equal to this page value, it will be processed.
414: */
415: /**
416: * @todo add this method for Struts Validator to work *RD* Mon Jul 27 2004
417: */
418: public Validator initValidator(String key, Object bean,
419: ServletContext application, HttpServletRequest request,
420: ActionErrors errors, int page) {
421:
422: ValidatorResources resources = Resources.getValidatorResources(
423: application, request);
424:
425: Locale locale = Resources.getLocale(request);
426:
427: Validator validator = new Validator(resources, key);
428: validator.setUseContextClassLoader(true);
429:
430: validator.setPage(page);
431:
432: validator.addResource(Resources.SERVLET_CONTEXT_KEY,
433: application);
434: validator.addResource(Resources.HTTP_SERVLET_REQUEST_KEY,
435: request);
436: validator.addResource(Validator.LOCALE_KEY, locale);
437: validator.addResource(Resources.ACTION_ERRORS_KEY, errors);
438: validator.addResource(Validator.BEAN_KEY, bean);
439:
440: return validator;
441: }
442:
443: /**
444: * Used to indicate the current page of a multi-page form.
445: */
446: /**
447: * @todo Struts Validator requires this variable to exist *RD* Mon Jul 27 2004
448: */
449: protected int page = 0;
450:
451: /**
452: * The results returned from the validation performed
453: * by the <code>Validator</code>.
454: */
455: /**
456: * @todo Struts Validator requires this variable to exist *RD* Mon Jul 27 2004
457: */
458: protected ValidatorResults validatorResults = null;
459:
460: /**
461: * Commons Logging instance.
462: */
463: /**
464: * @todo Struts Validator requires this variable to exist *RD* Mon Jul 27 2004
465: */
466: private static Log log = LogFactory.getLog(DefaultForm.class);
467:
468: }
|