coliver 2003/02/23 14:51:31
Modified: src/java/org/apache/cocoon/components/xmlform Form.java Log: removed println's Revision Changes Path 1.18 +671 -672 xml-cocoon2/src/java/org/apache/cocoon/components/xmlform/Form.java Index: Form.java =================================================================== RCS file: /home/cvs/xml-cocoon2/src/java/org/apache/cocoon/components/xmlform/Form.java,v retrieving revision 1.17 retrieving revision 1.18 diff -u -r1.17 -r1.18 --- Form.java 23 Feb 2003 19:21:32 -0000 1.17 +++ Form.java 23 Feb 2003 22:51:30 -0000 1.18 @@ -99,100 +99,100 @@ */ public class Form { - public static String SCOPE_REQUEST = "request"; - public static String SCOPE_SESSION = "session"; + public static String SCOPE_REQUEST = "request"; + public static String SCOPE_SESSION = "session"; - public static String FORM_VIEW_PARAM = "cocoon-xmlform-view"; + public static String FORM_VIEW_PARAM = "cocoon-xmlform-view"; - public static String VIOLATION_MESSAGE_DATA_FORMAT_ERROR = - "Invalid data format"; + public static String VIOLATION_MESSAGE_DATA_FORMAT_ERROR = + "Invalid data format"; - /** - * an XMLForm is only usable when it has an id and an underlying model - */ - public Form(String id, Object model) { - - if ((id == null) || (model == null)) - throw new java.lang.IllegalStateException( - "Form cannot be created with null id or null model "); - setId(id); - setModel(model); - } - - public String getId() { - return id_; - } - - public void setId(String newId) { - id_ = newId; - } - - public Object getModel() { - return model_; - } - - public void setModel(Object newModel) { - model_ = newModel; - jxcontext_ = JXPathContext.newContext(model_); - jxcontext_.setLenient(false); - } - - public Validator getValidator() { - return validator_; - } - - public void setValidator(Validator newValidator) { - validator_ = newValidator; - } - - public List getViolations() { - return violations_; - } - - /** - * - * This method allows custom validations to be added - * after population and after a call to validate - * (either automatic or explicit). - * Usually used from within the perform method of - * a concrete XMLFormAction. - * - * @param newViolations - * - */ - public void addViolations(List newViolations) { - - if (violations_ != null) - violations_.addAll(newViolations); - else - violations_ = newViolations; - updateViolationsAsSortedSet(); - - } - - public SortedSet getViolationsAsSortedSet() { - return violationsAsSortedSet_; - } - - public void clearViolations() { - violations_ = null; - violationsAsSortedSet_ = null; - } - - /** - * Encapsulates access to the model - * - * @param xpath to the model attribute - * @param value to be set - */ - public void setValue(String xpath, Object value) { - if (model_ == null) - throw new IllegalStateException("Form model not set"); - jxcontext_.setValue(xpath, value); - } + /** + * an XMLForm is only usable when it has an id and an underlying model + */ + public Form(String id, Object model) { + + if ((id == null) || (model == null)) + throw new java.lang.IllegalStateException( + "Form cannot be created with null id or null model "); + setId(id); + setModel(model); + } + + public String getId() { + return id_; + } + + public void setId(String newId) { + id_ = newId; + } + + public Object getModel() { + return model_; + } + + public void setModel(Object newModel) { + model_ = newModel; + jxcontext_ = JXPathContext.newContext(model_); + jxcontext_.setLenient(false); + } + + public Validator getValidator() { + return validator_; + } + + public void setValidator(Validator newValidator) { + validator_ = newValidator; + } + + public List getViolations() { + return violations_; + } + + /** + * + * This method allows custom validations to be added + * after population and after a call to validate + * (either automatic or explicit). + * Usually used from within the perform method of + * a concrete XMLFormAction. + * + * @param newViolations + * + */ + public void addViolations(List newViolations) { + + if (violations_ != null) + violations_.addAll(newViolations); + else + violations_ = newViolations; + updateViolationsAsSortedSet(); + + } + + public SortedSet getViolationsAsSortedSet() { + return violationsAsSortedSet_; + } + + public void clearViolations() { + violations_ = null; + violationsAsSortedSet_ = null; + } + + /** + * Encapsulates access to the model + * + * @param xpath to the model attribute + * @param value to be set + */ + public void setValue(String xpath, Object value) { + if (model_ == null) + throw new IllegalStateException("Form model not set"); + jxcontext_.setValue(xpath, value); + } public void setValue(String xpath, Object[] values) { - Pointer ptr = jxcontext_.getPointer(xpath); + Pointer ptr = jxcontext_.getPointer(xpath); // // Dmitri Plotnikov's patch // @@ -234,605 +234,604 @@ Collection cl = (Collection) property; cl.clear(); cl.addAll(java.util.Arrays.asList(values)); - } else if (property instanceof NativeArray) { - Context.enter(); - try { - NativeArray arr = (NativeArray)property; - ScriptableObject.putProperty(arr, "length", - new Integer(0)); - ScriptableObject.putProperty(arr, "length", - new Integer(values.length)); - for (int i = 0; i < values.length; i++) { - Object val = values[i]; - if (!(val == null - || val instanceof String - || val instanceof Number - || val instanceof Boolean)) { - val = Context.toObject(val, arr); - } - ScriptableObject.putProperty(arr, i, val); - } - } catch (Exception willNotBeThrown) { - // shouldn't happen - willNotBeThrown.printStackTrace(); - } finally { - Context.exit(); - } - } else { - jxcontext_.setValue(xpath, values[0]); - } - } - - /** - * Encapsulates access to the model - * - * @param xpath of the model attribute - * - * @throws RuntimeException if the xpath value - * has invalid XPath syntax or it doesn't point - * to an attribute of the model - * - */ - public Object getValue(String xpath) { - if (model_ == null) - throw new IllegalStateException("Form model not set"); - Object result = jxcontext_.getValue(xpath); - if (result instanceof NativeArray) { - // Convert JavaScript array to Collection - NativeArray arr = (NativeArray)result; - int len = (int)arr.jsGet_length(); - List list = new ArrayList(len); - for (int i = 0; i < len; i++) { - Object obj = arr.get(i, arr); - if (obj == Context.getUndefinedValue()) { - obj = null; - } - list.add(obj); - } - result = list; - } - return result; - } - - /** - * Resolves a nodeset selector - * into a list of concrete node locations - * @param xpathSelector the nodeset selector - * - * @return a Set of XPath strings pointing to - * each nodeset satisfying the nodeset selector - * + } else if (property instanceof NativeArray) { + Context.enter(); + try { + NativeArray arr = (NativeArray)property; + ScriptableObject.putProperty(arr, "length", + new Integer(0)); + ScriptableObject.putProperty(arr, "length", + new Integer(values.length)); + for (int i = 0; i < values.length; i++) { + Object val = values[i]; + if (!(val == null + || val instanceof String + || val instanceof Number + || val instanceof Boolean)) { + val = Context.toObject(val, arr); + } + ScriptableObject.putProperty(arr, i, val); + } + } catch (Exception willNotBeThrown) { + // shouldn't happen + willNotBeThrown.printStackTrace(); + } finally { + Context.exit(); + } + } else { + jxcontext_.setValue(xpath, values[0]); + } + } + + /** + * Encapsulates access to the model + * + * @param xpath of the model attribute + * + * @throws RuntimeException if the xpath value + * has invalid XPath syntax or it doesn't point + * to an attribute of the model + * + */ + public Object getValue(String xpath) { + if (model_ == null) + throw new IllegalStateException("Form model not set"); + Object result = jxcontext_.getValue(xpath); + if (result instanceof NativeArray) { + // Convert JavaScript array to Collection + NativeArray arr = (NativeArray)result; + int len = (int)arr.jsGet_length(); + List list = new ArrayList(len); + for (int i = 0; i < len; i++) { + Object obj = arr.get(i, arr); + if (obj == Context.getUndefinedValue()) { + obj = null; + } + list.add(obj); + } + result = list; + } + return result; + } + + /** + * Resolves a nodeset selector + * into a list of concrete node locations + * @param xpathSelector the nodeset selector + * + * @return a Set of XPath strings pointing to + * each nodeset satisfying the nodeset selector + * * <p> - * TODO: the Collection return type should be replaced with a Set. - * LinkedHashSet implementation should be used. All resolved - * nodes are unique in the resulting set, therefore Set is more appropriate. - * Since LinkedHashSet is only available in JDK 1.4 or later, it is not - * appropriate to make the change immediately. - */ - public Collection locate(String xpathSelector) { - if (model_ == null) - throw new IllegalStateException("Form model not set"); - List nodeset = new LinkedList(); - Pointer ptr = jxcontext_.getPointer(xpathSelector); - Iterator iter = jxcontext_.iteratePointers(xpathSelector); - while (iter.hasNext()) { - Pointer nextPointer = (Pointer) iter.next(); - String path = nextPointer.asPath(); - nodeset.add(path); - } - return nodeset; - } - - /** - * Performs complete validation - * of the form model - */ - public boolean validate() { - return validate(null); - } - - /** - * - * @param phase the validation phase - * - * @return - * if validation finishes without any violations, - * return true otherwise return false and save all violations - * - */ - public boolean validate(String phase) { - if (validator_ == null) - return true; - - validator_.setProperty(Validator.PROPERTY_PHASE, phase); - List vs = validator_.validate(model_); - if (vs != null) { - if (violations_ != null) { - violations_.addAll(vs); - } else { - if (!vs.isEmpty()) - violations_ = vs; - } - } - if (violations_ == null) - return true; - else { - updateViolationsAsSortedSet(); - return false; - } - } - - /** - * Populates an HTML Form POST into the XMLForm model (JavaBean or DOM node). - * + * TODO: the Collection return type should be replaced with a Set. + * LinkedHashSet implementation should be used. All resolved + * nodes are unique in the resulting set, therefore Set is more appropriate. + * Since LinkedHashSet is only available in JDK 1.4 or later, it is not + * appropriate to make the change immediately. + */ + public Collection locate(String xpathSelector) { + if (model_ == null) + throw new IllegalStateException("Form model not set"); + List nodeset = new LinkedList(); + Pointer ptr = jxcontext_.getPointer(xpathSelector); + Iterator iter = jxcontext_.iteratePointers(xpathSelector); + while (iter.hasNext()) { + Pointer nextPointer = (Pointer) iter.next(); + String path = nextPointer.asPath(); + nodeset.add(path); + } + return nodeset; + } + + /** + * Performs complete validation + * of the form model + */ + public boolean validate() { + return validate(null); + } + + /** + * + * @param phase the validation phase + * + * @return + * if validation finishes without any violations, + * return true otherwise return false and save all violations + * + */ + public boolean validate(String phase) { + if (validator_ == null) + return true; + + validator_.setProperty(Validator.PROPERTY_PHASE, phase); + List vs = validator_.validate(model_); + if (vs != null) { + if (violations_ != null) { + violations_.addAll(vs); + } else { + if (!vs.isEmpty()) + violations_ = vs; + } + } + if (violations_ == null) + return true; + else { + updateViolationsAsSortedSet(); + return false; + } + } + + /** + * Populates an HTML Form POST into the XMLForm model (JavaBean or DOM node). + * * <p> - * Expects that all request parameter names are XPath expressions - * to attributes of the model. - * For each request parameter, finds and assigns its value to the - * JavaBean property corresponding to the parameter's name + * Expects that all request parameter names are XPath expressions + * to attributes of the model. + * For each request parameter, finds and assigns its value to the + * JavaBean property corresponding to the parameter's name * </p> - * - * TODO: provide a more sophisticated examples with checkboxes, multi choice, - * radio button, text area, file upload, etc. - */ - public void populate(Map sitemapObjectModel) { - // clean violations_ set - clearViolations(); - - // let listeners know that - // population is about to start - reset(); - - // data format violations - // gathered during population - // For example when - // a request parameter value is "saymyname" - // while the request parameter name points to an int attribute - List pviolations = new ArrayList(); - - Map filteredParameters = - getFilteredRequestParameters(sitemapObjectModel); - Iterator iter = filteredParameters.entrySet().iterator(); - while (iter.hasNext()) { - Map.Entry entry = (Map.Entry) iter.next(); - - String path = (String) entry.getKey(); - - // filter custom request parameter - // not refering to the model - if (filterRequestParameter(path)) - continue; - - Object[] values = (Object[]) entry.getValue(); - - try { - System.out.println("attempting to set value of " + path + " to " + java.util.Arrays.asList(values)); - setValue(path, values); - } catch (JXPathException ex) { - Violation v = new Violation(); - v.setPath(path); - String message = VIOLATION_MESSAGE_DATA_FORMAT_ERROR; - v.setMessage(ex.getMessage()); - pviolations.add(v); - } - } // while - - // validate form model - autoValidate(sitemapObjectModel); - - // merge violation sets - if (violations_ != null) { - violations_.addAll(pviolations); - } else { - if (!pviolations.isEmpty()) - violations_ = pviolations; - } - if (violations_ != null) { - updateViolationsAsSortedSet(); - } - } - - /** - * - * Filters request parameters which are not references to model properties. - * Sets default values for parameters which were expected in the request, - * but did not arrive (e.g. check boxes). - * - * @return filtered request parameters - * - */ - protected Map getFilteredRequestParameters(Map sitemapObjectModel) { - - Request request = getRequest(sitemapObjectModel); - - Map filteredParameters = new HashMap(); - - // first filter out request parameters which do not refer to model properties - Enumeration enum = request.getParameterNames(); - while (enum.hasMoreElements()) { - String path = (String) enum.nextElement(); - - // filter custom request parameter - // not refering to the model - if (filterRequestParameter(path)) - continue; - - Object[] values = request.getParameterValues(path); - - filteredParameters.put(path, values); - } - - // now, find expected parameters which did not arrive - // and set default values for them - String viewName = getFormView(sitemapObjectModel); - Map expectedReferences = - getFormViewState(viewName).getModelReferenceMap(); - - Iterator iter = expectedReferences.entrySet().iterator(); - while (iter.hasNext()) { - Map.Entry entry = (Map.Entry) iter.next(); - String propertyReference = (String) entry.getKey(); - - // check if the expected parameter actually arrived in the request - if (filteredParameters.get(propertyReference) == null) { - // Since it is not there, try to provide a default value - String inputType = (String) entry.getValue(); - - Object defaultValue = null; - if (inputType.equals(XMLFormTransformer.TAG_SELECTBOOLEAN)) { - // false for boolean type (usually, single check-box) - defaultValue = new Object[] { Boolean.FALSE }; - } else if ( - inputType.equals(XMLFormTransformer.TAG_SELECTMANY)) { - // empty array for select many (usually, multi check-box) - defaultValue = new Object[0]; - } else { - // for all the rest, use a blank value and hope for the best - defaultValue = new Object[] { "" }; - } - - filteredParameters.put(propertyReference, defaultValue); - - } - - } // iterate over expectedReferences.entrySet() - - return filteredParameters; - - } // getFilteredRequestParameters - - /** create a SortedSet view of the violations collection - * for convenience of processors down the pipeline - * protected void updateViolationsAsSortedSet() - */ - protected void updateViolationsAsSortedSet() { - violationsAsSortedSet_ = new TreeSet(violations_); - } - - /** - * - * Convenience method invoked after populate() - * By default it performs Form model validation. - * - * <br> - * - If default validation is not necessary - * setAutoValidate( false ) should be used - * - * <br> - * If the validation - * criteria needs to be different, subclasses can override - * this method to change the behaviour. - * - */ - protected void autoValidate(Map sitemapObjectModel) { - if (!autoValidateEnabled_) - return; - // perform validation for the phase - // which matches the name of the current form view - // if one is available - String formView = getFormView(sitemapObjectModel); - if (formView != null) { - validate(formView); - } - } - - /** - * Filters custom request parameter not refering to the model - * + * + * TODO: provide a more sophisticated examples with checkboxes, multi choice, + * radio button, text area, file upload, etc. + */ + public void populate(Map sitemapObjectModel) { + // clean violations_ set + clearViolations(); + + // let listeners know that + // population is about to start + reset(); + + // data format violations + // gathered during population + // For example when + // a request parameter value is "saymyname" + // while the request parameter name points to an int attribute + List pviolations = new ArrayList(); + + Map filteredParameters = + getFilteredRequestParameters(sitemapObjectModel); + Iterator iter = filteredParameters.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = (Map.Entry) iter.next(); + + String path = (String) entry.getKey(); + + // filter custom request parameter + // not refering to the model + if (filterRequestParameter(path)) + continue; + + Object[] values = (Object[]) entry.getValue(); + + try { + setValue(path, values); + } catch (JXPathException ex) { + Violation v = new Violation(); + v.setPath(path); + String message = VIOLATION_MESSAGE_DATA_FORMAT_ERROR; + v.setMessage(ex.getMessage()); + pviolations.add(v); + } + } // while + + // validate form model + autoValidate(sitemapObjectModel); + + // merge violation sets + if (violations_ != null) { + violations_.addAll(pviolations); + } else { + if (!pviolations.isEmpty()) + violations_ = pviolations; + } + if (violations_ != null) { + updateViolationsAsSortedSet(); + } + } + + /** + * + * Filters request parameters which are not references to model properties. + * Sets default values for parameters which were expected in the request, + * but did not arrive (e.g. check boxes). + * + * @return filtered request parameters + * + */ + protected Map getFilteredRequestParameters(Map sitemapObjectModel) { + + Request request = getRequest(sitemapObjectModel); + + Map filteredParameters = new HashMap(); + + // first filter out request parameters which do not refer to model properties + Enumeration enum = request.getParameterNames(); + while (enum.hasMoreElements()) { + String path = (String) enum.nextElement(); + + // filter custom request parameter + // not refering to the model + if (filterRequestParameter(path)) + continue; + + Object[] values = request.getParameterValues(path); + + filteredParameters.put(path, values); + } + + // now, find expected parameters which did not arrive + // and set default values for them + String viewName = getFormView(sitemapObjectModel); + Map expectedReferences = + getFormViewState(viewName).getModelReferenceMap(); + + Iterator iter = expectedReferences.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = (Map.Entry) iter.next(); + String propertyReference = (String) entry.getKey(); + + // check if the expected parameter actually arrived in the request + if (filteredParameters.get(propertyReference) == null) { + // Since it is not there, try to provide a default value + String inputType = (String) entry.getValue(); + + Object defaultValue = null; + if (inputType.equals(XMLFormTransformer.TAG_SELECTBOOLEAN)) { + // false for boolean type (usually, single check-box) + defaultValue = new Object[] { Boolean.FALSE }; + } else if ( + inputType.equals(XMLFormTransformer.TAG_SELECTMANY)) { + // empty array for select many (usually, multi check-box) + defaultValue = new Object[0]; + } else { + // for all the rest, use a blank value and hope for the best + defaultValue = new Object[] { "" }; + } + + filteredParameters.put(propertyReference, defaultValue); + + } + + } // iterate over expectedReferences.entrySet() + + return filteredParameters; + + } // getFilteredRequestParameters + + /** create a SortedSet view of the violations collection + * for convenience of processors down the pipeline + * protected void updateViolationsAsSortedSet() + */ + protected void updateViolationsAsSortedSet() { + violationsAsSortedSet_ = new TreeSet(violations_); + } + + /** + * + * Convenience method invoked after populate() + * By default it performs Form model validation. + * + * <br> + * - If default validation is not necessary + * setAutoValidate( false ) should be used + * + * <br> + * If the validation + * criteria needs to be different, subclasses can override + * this method to change the behaviour. + * + */ + protected void autoValidate(Map sitemapObjectModel) { + if (!autoValidateEnabled_) + return; + // perform validation for the phase + // which matches the name of the current form view + // if one is available + String formView = getFormView(sitemapObjectModel); + if (formView != null) { + validate(formView); + } + } + + /** + * Filters custom request parameter not refering to the model + * * <p> - * TODO: implement default filtering - * for standard Cocoon parameters - * like cocoon-action[-suffix] - * - */ - protected boolean filterRequestParameter(String name) { - // filter standard cocoon-* parameters - if (filterDefaultRequestParameter(name)) - return true; - - // then consult with FormListeners - Set ls = new HashSet(); - ls.addAll(Collections.synchronizedSet(formListeners_)); - Iterator iter = ls.iterator(); - while (iter.hasNext()) { - FormListener fl = (FormListener) iter.next(); - // if any of the listeners wants this parameter filtered - // then filter it (return true) - if (fl.filterRequestParameter(this, name)) - return true; - } - // if none of the listeners wants this parameter filtered - // then don't filter it - return false; - } - - /** - * Filters the standard cocoon request parameters. - * If default filtering needs to be different, - * subclasses can override this method. - * It is invoked before all listeners are asked to filter the parameter - */ - protected boolean filterDefaultRequestParameter(String paramName) { - if (paramName.startsWith(Constants.ACTION_PARAM_PREFIX) - || paramName.startsWith(Constants.VIEW_PARAM)) - return true; - if (paramName.equals(FORM_VIEW_PARAM)) - return true; - else - return false; - } - - /** - * Try to extract from the request - * and return the current form view - */ - public String getFormView(Map sitemapObjectModel) { - return getRequest(sitemapObjectModel).getParameter(Form.FORM_VIEW_PARAM); - } - - /** - * This method is called before - * the form is populated with request parameters. - * - * Semantically similar to that of the - * ActionForm.reset() in Struts - * - * Can be used for clearing checkbox fields, - * because the browser will not send them when - * not checked. - * - * Calls reset on all FormListeners - */ - protected void reset() { - // notify FormListeners - Set ls = new HashSet(); - ls.addAll(Collections.synchronizedSet(formListeners_)); - Iterator iter = ls.iterator(); - while (iter.hasNext()) { - FormListener fl = (FormListener) iter.next(); - fl.reset(this); - } - return; - } - - /** - * Loads a form from the request or session - * - * @param sitemapObjectModel - * @param id the form id - */ - public static Form lookup(Map sitemapObjectModel, String id) { - Request request = getRequest(sitemapObjectModel); - Form form = (Form) request.getAttribute(id); - if (form != null) - return form; - else { - Session session = request.getSession(false); - if (session != null) - form = (Form) session.getAttribute(id); - return form; - } - } - - /** - * Removes a form from the request and session. - * This method will remove the attribute bindings - * correspoding to the form id from both request - * and session to ensure that a subsequent - * Form.lookup will not succeed. - * - * @param sitemapObjectModel - * @param id the form id - */ - public static void remove(Map sitemapObjectModel, String id) { - Request request = getRequest(sitemapObjectModel); - request.removeAttribute(id); - - Session session = request.getSession(false); - if (session != null) - session.removeAttribute(id); - } - - /** - * Saves the form in the request or session - * - * @param sitemapObjectModel - * @param scope if true the form will be bound in the session, otherwise request - */ - public void save(Map sitemapObjectModel, String scope) { - Request request = getRequest(sitemapObjectModel); - - if (lookup(sitemapObjectModel, id_) != null) - throw new java.lang.IllegalStateException( - "Form [id=" + id_ + "] already bound in request or session "); - - if (SCOPE_REQUEST.equals(scope)) { - request.setAttribute(id_, this); - } else // session scope - { - Session session = request.getSession(true); - session.setAttribute(id_, this); - } - - } - - /** - * Add another FormListener - * - */ - public synchronized void addFormListener(FormListener formListener) { - formListeners_.add(formListener); - } - - /** - * Add another FormListener - * - */ - public synchronized void removeFormListener(FormListener formListener) { - formListeners_.remove(formListener); - } - - protected final static Request getRequest(Map sitemapObjectModel) { - return (Request) sitemapObjectModel.get( - ObjectModelHelper.REQUEST_OBJECT); - } - - public void setAutoValidate(boolean newAVFlag) { - autoValidateEnabled_ = newAVFlag; - } - - /** - * <pre> - * When the transformer renders a form view, - * it lets the form wrapper know about each referenced model property. - * This allows a precise tracking and can be used for multiple reasons: - * 1) Verify that the client does not temper with the input fields as specified by the - * form view author - * 2) Allow default values to be used for properties which were expected to be send by the client, - * but for some reason were not. A typical example is a check box. When unchecked, the browser - * does not send any request parameter, leaving it to the server to handle the situation. - * This proves to be a very error prone problem when solved on a case by case basis. - * By having a list of expected property references, the model populator can detect - * a checkbox which was not send and set the property value to false. - * - * NOTE: This added functionality is ONLY useful for SESSION scope forms. - * Request scope forms are constructed anew for every request and therefore - * cannot benefit from this extra feature. - * With the high performance CPUs and cheap memory used in today's servers, - * session scope forms are a safe choice. - * - * </pre> - * - */ - public void saveExpectedModelReferenceForView( - String currentFormView, - String ref, - String inputType) { - // if the form view is null, we are not interested in saving any references - if (currentFormView == null) - return; - - FormViewState formViewState = getFormViewState(currentFormView); - formViewState.addModelReferenceAndInputType(ref, inputType); - } - - /** - * When the transformer starts rendering a new form element - * It needs to reset previously saved references for another - * transformation of the same view. - */ - public void clearSavedModelReferences(String currentFormView) { - FormViewState formViewState = getFormViewState(currentFormView); - formViewState.clear(); - } - - /** - * We keep a map of ViewState objects which store - * all references to model properties in a particular form view - * which were rendered by the - * XMLFormTansformer in the most recent transformation. - */ - protected FormViewState getFormViewState(String viewName) { - FormViewState formViewState = - (FormViewState) viewStateMap_.get(viewName); - if (formViewState == null) { - formViewState = new FormViewState(); - viewStateMap_.put(viewName, formViewState); - } - return formViewState; - } - - /** - * Internal class used for keeping state information - * during the life cycle of a form. - * - * <p>Used only for session scoped forms - */ - class FormViewState { + * TODO: implement default filtering + * for standard Cocoon parameters + * like cocoon-action[-suffix] + * + */ + protected boolean filterRequestParameter(String name) { + // filter standard cocoon-* parameters + if (filterDefaultRequestParameter(name)) + return true; + + // then consult with FormListeners + Set ls = new HashSet(); + ls.addAll(Collections.synchronizedSet(formListeners_)); + Iterator iter = ls.iterator(); + while (iter.hasNext()) { + FormListener fl = (FormListener) iter.next(); + // if any of the listeners wants this parameter filtered + // then filter it (return true) + if (fl.filterRequestParameter(this, name)) + return true; + } + // if none of the listeners wants this parameter filtered + // then don't filter it + return false; + } + + /** + * Filters the standard cocoon request parameters. + * If default filtering needs to be different, + * subclasses can override this method. + * It is invoked before all listeners are asked to filter the parameter + */ + protected boolean filterDefaultRequestParameter(String paramName) { + if (paramName.startsWith(Constants.ACTION_PARAM_PREFIX) + || paramName.startsWith(Constants.VIEW_PARAM)) + return true; + if (paramName.equals(FORM_VIEW_PARAM)) + return true; + else + return false; + } + + /** + * Try to extract from the request + * and return the current form view + */ + public String getFormView(Map sitemapObjectModel) { + return getRequest(sitemapObjectModel).getParameter(Form.FORM_VIEW_PARAM); + } + + /** + * This method is called before + * the form is populated with request parameters. + * + * Semantically similar to that of the + * ActionForm.reset() in Struts + * + * Can be used for clearing checkbox fields, + * because the browser will not send them when + * not checked. + * + * Calls reset on all FormListeners + */ + protected void reset() { + // notify FormListeners + Set ls = new HashSet(); + ls.addAll(Collections.synchronizedSet(formListeners_)); + Iterator iter = ls.iterator(); + while (iter.hasNext()) { + FormListener fl = (FormListener) iter.next(); + fl.reset(this); + } + return; + } + + /** + * Loads a form from the request or session + * + * @param sitemapObjectModel + * @param id the form id + */ + public static Form lookup(Map sitemapObjectModel, String id) { + Request request = getRequest(sitemapObjectModel); + Form form = (Form) request.getAttribute(id); + if (form != null) + return form; + else { + Session session = request.getSession(false); + if (session != null) + form = (Form) session.getAttribute(id); + return form; + } + } + + /** + * Removes a form from the request and session. + * This method will remove the attribute bindings + * correspoding to the form id from both request + * and session to ensure that a subsequent + * Form.lookup will not succeed. + * + * @param sitemapObjectModel + * @param id the form id + */ + public static void remove(Map sitemapObjectModel, String id) { + Request request = getRequest(sitemapObjectModel); + request.removeAttribute(id); + + Session session = request.getSession(false); + if (session != null) + session.removeAttribute(id); + } + + /** + * Saves the form in the request or session + * + * @param sitemapObjectModel + * @param scope if true the form will be bound in the session, otherwise request + */ + public void save(Map sitemapObjectModel, String scope) { + Request request = getRequest(sitemapObjectModel); + + if (lookup(sitemapObjectModel, id_) != null) + throw new java.lang.IllegalStateException( + "Form [id=" + id_ + "] already bound in request or session "); + + if (SCOPE_REQUEST.equals(scope)) { + request.setAttribute(id_, this); + } else // session scope + { + Session session = request.getSession(true); + session.setAttribute(id_, this); + } + + } + + /** + * Add another FormListener + * + */ + public synchronized void addFormListener(FormListener formListener) { + formListeners_.add(formListener); + } + + /** + * Add another FormListener + * + */ + public synchronized void removeFormListener(FormListener formListener) { + formListeners_.remove(formListener); + } + + protected final static Request getRequest(Map sitemapObjectModel) { + return (Request) sitemapObjectModel.get( + ObjectModelHelper.REQUEST_OBJECT); + } + + public void setAutoValidate(boolean newAVFlag) { + autoValidateEnabled_ = newAVFlag; + } + + /** + * <pre> + * When the transformer renders a form view, + * it lets the form wrapper know about each referenced model property. + * This allows a precise tracking and can be used for multiple reasons: + * 1) Verify that the client does not temper with the input fields as specified by the + * form view author + * 2) Allow default values to be used for properties which were expected to be send by the client, + * but for some reason were not. A typical example is a check box. When unchecked, the browser + * does not send any request parameter, leaving it to the server to handle the situation. + * This proves to be a very error prone problem when solved on a case by case basis. + * By having a list of expected property references, the model populator can detect + * a checkbox which was not send and set the property value to false. + * + * NOTE: This added functionality is ONLY useful for SESSION scope forms. + * Request scope forms are constructed anew for every request and therefore + * cannot benefit from this extra feature. + * With the high performance CPUs and cheap memory used in today's servers, + * session scope forms are a safe choice. + * + * </pre> + * + */ + public void saveExpectedModelReferenceForView( + String currentFormView, + String ref, + String inputType) { + // if the form view is null, we are not interested in saving any references + if (currentFormView == null) + return; + + FormViewState formViewState = getFormViewState(currentFormView); + formViewState.addModelReferenceAndInputType(ref, inputType); + } + + /** + * When the transformer starts rendering a new form element + * It needs to reset previously saved references for another + * transformation of the same view. + */ + public void clearSavedModelReferences(String currentFormView) { + FormViewState formViewState = getFormViewState(currentFormView); + formViewState.clear(); + } + + /** + * We keep a map of ViewState objects which store + * all references to model properties in a particular form view + * which were rendered by the + * XMLFormTansformer in the most recent transformation. + */ + protected FormViewState getFormViewState(String viewName) { + FormViewState formViewState = + (FormViewState) viewStateMap_.get(viewName); + if (formViewState == null) { + formViewState = new FormViewState(); + viewStateMap_.put(viewName, formViewState); + } + return formViewState; + } + + /** + * Internal class used for keeping state information + * during the life cycle of a form. + * + * <p>Used only for session scoped forms + */ + class FormViewState { private Map modelReferences_ = new HashMap(); - FormViewState() { + FormViewState() { } - /** - * - * @return Map of (String modelPropertyReference, String inputType) pairs - */ - Map getModelReferenceMap() { - return modelReferences_; - } + /** + * + * @return Map of (String modelPropertyReference, String inputType) pairs + */ + Map getModelReferenceMap() { + return modelReferences_; + } - void addModelReferenceAndInputType(String modelPropertyReference, + void addModelReferenceAndInputType(String modelPropertyReference, String inputType) { modelReferences_.put(modelPropertyReference, inputType); - } + } - void clear() { - modelReferences_.clear(); - } - } + void clear() { + modelReferences_.clear(); + } + } - /** the set of violations the model commited during validation */ - private List violations_ = null; + /** the set of violations the model commited during validation */ + private List violations_ = null; - /** another view of the violations_ collection */ - private SortedSet violationsAsSortedSet_ = null; + /** another view of the violations_ collection */ + private SortedSet violationsAsSortedSet_ = null; - /** flag allowing control over automatic validation on populate() */ - private boolean autoValidateEnabled_ = true; + /** flag allowing control over automatic validation on populate() */ + private boolean autoValidateEnabled_ = true; - /** The data model this form encapsulates */ - private Object model_ = null; + /** The data model this form encapsulates */ + private Object model_ = null; - /** The list of FormListeners */ - private Set formListeners_ = new HashSet(); + /** The list of FormListeners */ + private Set formListeners_ = new HashSet(); - /** - * The unique identifier for this form. Used when form is stored in request - * or session for reference by other components + /** + * The unique identifier for this form. Used when form is stored in request + * or session for reference by other components * - * <p> - * TODO: a centralized form registry would be helpful to prevent from id collision - */ - private String id_ = null; - - /** - * The JXPath context associated with the model - * Used to traverse the model with XPath expressions - */ - private JXPathContext jxcontext_ = null; - - /** - * Used to validate the content of the model - * at various phases - * - */ - private Validator validator_ = null; - - /** - * Keeps a state information for - * each form view that has been processed - * - */ - private Map viewStateMap_ = new HashMap(); + * <p> + * TODO: a centralized form registry would be helpful to prevent from id collision + */ + private String id_ = null; + + /** + * The JXPath context associated with the model + * Used to traverse the model with XPath expressions + */ + private JXPathContext jxcontext_ = null; + + /** + * Used to validate the content of the model + * at various phases + * + */ + private Validator validator_ = null; + + /** + * Keeps a state information for + * each form view that has been processed + * + */ + private Map viewStateMap_ = new HashMap(); }