From cvs-return-25110-apmail-cocoon-cvs-archive=cocoon.apache.org@cocoon.apache.org Thu Nov 03 14:46:03 2005
Return-Path: This transformer fills all HTML 4 form elements with values from
+ * an InputModule, e.g. request, with the same name. It handles select
+ * boxes, textareas, checkboxes, radio buttons, password and text
+ * fields, and buttons. Form elements and complete forms can be protected
+ * from substitution by adding an attribute fixed="true" to them. In addition, it handles FormValidatorAction results by
+ * selectively omitting <error/> elements. These elements need a
+ * "name" attribute corresponding to the name of the form element, and
+ * either a "when" or a "when-ge" attribute. An error element is send down the pipeline if validation results
+ * are available and either the results equals the "when" attribute or
+ * the result is greater or equal to the "when-ge" attribute. Names for validation results are "ok", "not-present", "error",
+ * "is-null", "too-small", "too-large", and "no-match" for the similar
+ * named values from ValidatorActionResult. There need not to be an "error" element for every form element,
+ * multiple error elements for the same form element may be
+ * present. Names of error elements are never augmented by prefix, suffix or
+ * form name. Page parts with multiple occurrences depending on the number of
+ * actual parameters can be enclosed in <repeat on="expr" using="var"/>
+ * elements. expr is used to determine the number of occurrences
+ * and var will be expanded with the ordinary number. Repeat elements
+ * can be nested. Example: Will include as many input elements as mult parameters are present. Adding
+ * the repeater variable to the elements name is necessary only with structured
+ * parameters or when they should be numbered. See also the strip-number
+ * configuration parameter. To use this transformer, add the following to your
+ * transformation pipeline: boolean
value to signal to start extracting
+ */
+ public boolean startExtracting(String uri, String loc, String raw, Attributes a) {
+ if (this.nameElement.equals(uri,loc,raw)) {
+ this.instanceName = a.getValue(this.qname);
+ }
+ boolean res = this.startElement.equals(uri,loc,raw);
+ return res;
+ }
+
+ /**
+ * Receive notification of the beginning of an element.
+ *
+ * @param uri The Namespace URI, or the empty string if the element has no
+ * Namespace URI or if Namespace
+ * processing is not being performed.
+ * @param loc The local name (without prefix), or the empty string if
+ * Namespace processing is not being performed.
+ * @param raw The raw XML 1.0 name (with prefix), or the empty string if
+ * @return a boolean
value to signal to stop extracting
+ */
+ public boolean endExtracting(String uri, String loc, String raw) {
+ boolean res = this.startElement.equals(uri,loc,raw);
+ return res;
+ }
+
+
+ /**
+ * Start root element and replace it with the instance name.
+ * @see org.apache.cocoon.transformation.AbstractExtractionTransformer#startExtractingDocument(String, String, String, Attributes)
+ */
+ public void startExtractingDocument(String uri, String loc, String raw, Attributes a) throws SAXException {
+ if (this.nameAsRoot) {
+ loc = this.instanceName;
+ if (uri != null && !uri.equals("")) {
+ int pos = raw.indexOf(':');
+ raw = raw.substring(0, pos+1) + this.instanceName;
+ } else {
+ raw = loc;
+ }
+ }
+ this.currentBuilder.startElement(uri,loc,raw,a);
+ }
+
+ /**
+ * End root element and replace it with the instance name.
+ * @see org.apache.cocoon.transformation.AbstractExtractionTransformer#endExtractingDocument(String, String, String)
+ */
+ public void endExtractingDocument(String uri, String loc, String raw) throws SAXException{
+ if(this.nameAsRoot){
+ loc = this.instanceName;
+ if (uri != null && !uri.equals("")) {
+ int pos = raw.indexOf(':');
+ raw = raw.substring(0, pos+1) + this.instanceName;
+ } else {
+ raw = loc;
+ }
+ }
+ this.currentBuilder.endElement(uri, loc, raw);
+ }
+
+
+ /**
+ * Receive notification of the end of the extracted Document.
+ *
+ * @param doc a Document
value
+ */
+ public void handleExtractedDocument(Document doc) {
+
+ ServiceSelector outputSelector = null;
+ OutputModule output = null;
+
+ try {
+ if (getLogger().isDebugEnabled())
+ getLogger().debug("wrote ['"+this.instanceName+"'] to "+output+" using "+outputConf);
+ outputSelector = (ServiceSelector) this.manager.lookup(OUTPUT_MODULE_SELECTOR);
+ if (outputSelector.isSelectable(this.outputModuleName)) {
+ output = (OutputModule) outputSelector.select(this.outputModuleName);
+ }
+ output.setAttribute(outputConf, this.objectModel, this.instanceName, new DocumentWrapper(doc));
+ output.commit(outputConf, this.objectModel);
+ if (getLogger().isDebugEnabled())
+ getLogger().debug("wrote ['"+this.instanceName+"'] to "+output+" using "+outputConf);
+
+ } catch (Exception e) {
+ if (getLogger().isWarnEnabled())
+ getLogger().warn("Problem writing document data: "+e.getMessage());
+ } finally {
+ if (outputSelector != null) {
+ if (output != null) {
+ outputSelector.release(output);
+ output = null;
+ }
+ this.manager.release(outputSelector);
+ }
+ }
+ this.instanceName = null;
+ }
+
+}
Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/transformation/SimpleFormInstanceExtractionTransformer.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/transformation/SimpleFormTransformer.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/transformation/SimpleFormTransformer.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/transformation/SimpleFormTransformer.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/transformation/SimpleFormTransformer.java Thu Nov 3 05:41:06 2005
@@ -0,0 +1,1205 @@
+/*
+ * Copyright 1999-2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.transformation;
+
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.avalon.framework.service.ServiceSelector;
+import org.apache.avalon.framework.thread.ThreadSafe;
+
+import org.apache.avalon.excalibur.pool.Recyclable;
+
+import org.apache.cocoon.ProcessingException;
+import org.apache.cocoon.acting.ValidatorActionResult;
+import org.apache.cocoon.transformation.helpers.FormValidatorHelper;
+import org.apache.cocoon.components.modules.input.InputModule;
+import org.apache.cocoon.environment.SourceResolver;
+import org.apache.cocoon.util.HashMap;
+import org.apache.cocoon.xml.dom.DOMStreamer;
+import org.apache.commons.lang.BooleanUtils;
+
+import org.w3c.dom.DocumentFragment;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @cocoon.sitemap.component.documentation
+ * Eliminates the need for XSP to use FormValidatorAction or HTML forms.
+ * Caveat: Select options need a value attribute to work correctly.
+ *
+ * @cocoon.sitemap.component.name simple-form
+ * @cocoon.sitemap.component.logger sitemap.transformer.simple-form
+ *
+ *
+ *
+ *
+ *
+ * <map:transform type="simple-form"/>
+ *
Configuration elements: + *
input-module | (String) InputModule configuration, + * defaults to an empty configuration and the "request-param" module |
fixed-attribute | (String) Name of the attribute used to + * indicate that this element should not be changed. ("fixed") |
use-form-name | (boolean) Add the name of the form to the + * name of form elements. Uses default Separator , if default separator is null + * or empty, separator is set to "/". ("false") |
use-form-name-twice | (boolean) Add the name of the form twice to the + * name of form elements. This is useful when the form instance has no + * all enclosing root tag and the form name is used instead and the + * form name needs to be used to find the form data. Uses default Separator , + * if default separator is null or empty, separator is set to "/".("false") |
separator | (String) Separator between form name and element name ("/") + * |
prefix | (String) Prefix to add to element name for value lookup. No + * separator will be added between prefix and rest of the name. Default + * is "", when use-form-name is set, defaults to separator. |
suffix | (String) Added to the input element's name. No + * separator will be added between rest of the name and suffix. ("") |
ignore-validation | (boolean) If set to true, all error + * tags are copied as is regardless of the validation results.("false") |
decoration | (int) Length of decorations around repeat variable. Example: + * when using JXPath based module, decoration would be "[" and "]", hence 1. (1) |
strip-number | (boolean) If set to false, element names of repeated + * elements will contain the expanded repeater variable. ("true") |
Sitemap parameters: + *
fixed | (boolean) Do not change values |
prefix | (String) Added to the input element's name |
suffix | (String) Added to the input element's name |
input | (String) InputModule name |
decoration | (int) Length of decorations around repeat variable. |
strip-number | (boolean) Expanded repeater variable. |
Example:
+ * <input name="user.name" size="50" maxlength="60"/> + * <error name="user.name" when-ge="error">required</error> + *+ * + * @author Christian Haul + * @version $Id: SimpleFormTransformer.java 168368 2005-05-05 18:28:46Z vgritsenko $ + */ +public class SimpleFormTransformer extends AbstractSAXTransformer implements Recyclable { + + /** strip numbers from repeated element name attributes */ + private boolean stripNumber = true; + + /** Symbolic names for elements */ + /** unknown element */ + private static final int ELEMENT_DEFAULT = 0; + /** input element */ + private static final int ELEMENT_INPUT = 1; + /** select element */ + private static final int ELEMENT_SELECT = 2; + /** option element */ + private static final int ELEMENT_OPTION = 3; + /** textarea element */ + private static final int ELEMENT_TXTAREA = 4; + /** error element */ + private static final int ELEMENT_ERROR = 5; + /** form element */ + private static final int ELEMENT_FORM = 6; + /** repeat element */ + private static final int ELEMENT_REPEAT = 7; + /** default element as Integer (needed as default in org.apache.cocoon.util.HashMap.get()) */ + private static final Integer defaultElement = new Integer(ELEMENT_DEFAULT); + + /** input type unknown */ + private static final int TYPE_DEFAULT = 0; + /** input type checkbox */ + private static final int TYPE_CHECKBOX = 1; + /** input type radio */ + private static final int TYPE_RADIO = 2; + /** default input type as Integer (needed as default in org.apache.cocoon.util.HashMap.get()) */ + private static final Integer defaultType = new Integer(TYPE_DEFAULT); + + protected static final String INPUT_MODULE_ROLE = InputModule.ROLE; + protected static final String INPUT_MODULE_SELECTOR = INPUT_MODULE_ROLE + "Selector"; + + /** map element name string to symbolic name */ + private static final HashMap elementNames; + /** map input type string to symbolic name */ + private static final HashMap inputTypes; + /** map ValidatorActionResult to name string */ + private static final HashMap validatorResults; + /** map name string to ValidatorActionResult */ + private static final HashMap validatorResultLabel; + + /** setup mapping tables */ + static { + HashMap names = new HashMap(); + names.put("input", new Integer(ELEMENT_INPUT)); + names.put("select", new Integer(ELEMENT_SELECT)); + names.put("option", new Integer(ELEMENT_OPTION)); + names.put("textarea", new Integer(ELEMENT_TXTAREA)); + names.put("error", new Integer(ELEMENT_ERROR)); + names.put("form", new Integer(ELEMENT_FORM)); + names.put("repeat", new Integer(ELEMENT_REPEAT)); + elementNames = names; + names = null; + + names = new HashMap(); + names.put("checkbox", new Integer(TYPE_CHECKBOX)); + names.put("radio", new Integer(TYPE_RADIO)); + inputTypes = names; + names = null; + + names = new HashMap(); + names.put("ok", ValidatorActionResult.OK); + names.put("not-present", ValidatorActionResult.NOTPRESENT); + names.put("error", ValidatorActionResult.ERROR); + names.put("is-null", ValidatorActionResult.ISNULL); + names.put("too-small", ValidatorActionResult.TOOSMALL); + names.put("too-large", ValidatorActionResult.TOOLARGE); + names.put("no-match", ValidatorActionResult.NOMATCH); + validatorResultLabel = names; + + names = new HashMap(); + names.put(ValidatorActionResult.OK, "ok"); + names.put(ValidatorActionResult.NOTPRESENT, "not-present"); + names.put(ValidatorActionResult.ERROR, "error"); + names.put(ValidatorActionResult.ISNULL, "is-null"); + names.put(ValidatorActionResult.TOOSMALL, "too-small"); + names.put(ValidatorActionResult.TOOLARGE, "too-large"); + names.put(ValidatorActionResult.NOMATCH, "no-match"); + validatorResults = names; + names = null; + } + + /** current element's request parameter values */ + protected Object[] values; + + /** current request's validation results (all validated elements) */ + protected Map validationResults; + + /** Should we skip inserting values? */ + private boolean fixed; + /** Is the complete document protected? */ + private boolean documentFixed; + + private String fixedName = "fixed"; + private String prefix; + private String suffix; + private String defaultPrefix; + private String defaultSuffix; + private String separator; + private String formName; + private boolean useFormName; + private boolean useFormNameTwice; + private boolean ignoreValidation; + private int decorationSize = 1; + + private String defaultInput = "request-param"; + private Configuration defaultInputConf; + private Configuration inputConf; + private InputModule input; + private ServiceSelector inputSelector; + private String inputName; + + /** Skip element's content only. Otherwise skip also surrounding element. */ + protected boolean skipChildrenOnly; + + /** Count nested repeat elements. */ + protected int recordingCount; + + /** List of {@link RepeaterStatus} elements keeping track of nested repeat blocks. */ + protected List repeater; + + /** Map of {@link ValueList} to track multiple parameters. */ + protected Map formValues; + + + /** + * Keep track of repeater status. + */ + protected class RepeaterStatus { + public String var = null; + public String expr = null; + public int count = 0; + + public RepeaterStatus(String var, int count, String expr) { + this.var = var; + this.count = count; + this.expr = expr; + } + + public String toString() { + return "[" + this.var + "," + this.expr + "," + this.count + "]"; + } + } + + /** + * Keep track of multiple values. + */ + protected static class ValueList { + private int current = -1; + private Object[] values = null; + + public ValueList(Object[] values) { + this.values = values; + this.current = (values != null && values.length > 0 ? 0 : -1); + } + + public Object getNext() { + Object result = null; + if (this.values != null) { + if (this.current < this.values.length) { + result = this.values[this.current++]; + } + } + return result; + } + } + + + /** + * Constructor. Set the namespace. + */ + public SimpleFormTransformer() { + super.defaultNamespaceURI = ""; + } + + /** set per instance variables to defaults */ + private void reset() { + this.skipChildrenOnly = false; + this.values = null; + this.validationResults = null; + this.documentFixed = false; + this.fixed = false; + this.formName = null; + this.recordingCount = 0; + this.repeater = new LinkedList(); + this.formValues = new HashMap(); + + if (this.inputSelector != null) { + if (this.input != null) + this.inputSelector.release(this.input); + this.manager.release(this.inputSelector); + } + } + + /** + * Avalon Configurable Interface + */ + public void configure(Configuration config) throws ConfigurationException { + super.configure(config); + + this.defaultInputConf = config.getChild("input-module"); + this.defaultInput = this.defaultInputConf.getAttribute("name", this.defaultInput); + this.separator = config.getChild("separator").getValue(this.separator); + this.defaultPrefix = config.getChild("prefix").getValue(this.defaultPrefix); + this.defaultSuffix = config.getChild("suffix").getValue(this.defaultSuffix); + this.fixedName = config.getChild("fixed-attribute").getValue(this.fixedName); + this.useFormName = config.getChild("use-form-name").getValueAsBoolean(this.useFormName); + this.useFormNameTwice = + config.getChild("use-form-name-twice").getValueAsBoolean(this.useFormNameTwice); + this.useFormName = this.useFormName || this.useFormNameTwice; + if (this.useFormName) { + this.separator = + (this.separator == null || this.separator.equals("") ? "/" : this.separator); + this.defaultPrefix = this.separator; + } + this.ignoreValidation = + config.getChild("ignore-validation").getValueAsBoolean(this.ignoreValidation); + this.decorationSize = config.getChild("decoration").getValueAsInteger(this.decorationSize); + this.stripNumber = config.getChild("strip-number").getValueAsBoolean(this.stripNumber); + } + + /** + * Read sitemap parameters and set properties accordingly. + */ + private void evaluateParameters() { + this.documentFixed = this.parameters.getParameterAsBoolean("fixed", false); + this.fixed = this.documentFixed; + this.prefix = this.parameters.getParameter("prefix", this.defaultPrefix); + this.suffix = this.parameters.getParameter("suffix", this.defaultSuffix); + this.inputName = this.parameters.getParameter("input", null); + this.decorationSize = + this.parameters.getParameterAsInteger("decoration", this.decorationSize); + this.stripNumber = this.parameters.getParameterAsBoolean("strip-number", this.stripNumber); + } + + /** + * Setup the next round. + * The instance variables are initialised. + * @param resolver The current SourceResolver + * @param objectModel The objectModel of the environment. + * @param src The value of the src attribute in the sitemap. + * @param par The parameters from the sitemap. + */ + public void setup(SourceResolver resolver, Map objectModel, String src, Parameters par) + throws ProcessingException, SAXException, IOException { + + this.reset(); + + super.setup(resolver, objectModel, src, par); + + if (request == null) { + getLogger().debug("no request object"); + throw new ProcessingException("no request object"); + } + this.evaluateParameters(); + this.setupInputModule(); + + } + + /** + * Setup and obtain reference to the input module. + */ + private void setupInputModule() { + this.inputConf = null; + if (this.ignoreValidation) { + this.validationResults = null; + } else { + this.validationResults = FormValidatorHelper.getResults(this.objectModel); + } + + if (this.inputName == null) { + this.inputName = this.defaultInput; + this.inputConf = this.defaultInputConf; + } + + try { + // obtain input module + this.inputSelector = (ServiceSelector) this.manager.lookup(INPUT_MODULE_SELECTOR); + if (this.inputName != null + && this.inputSelector != null + && this.inputSelector.isSelectable(this.inputName)) { + this.input = (InputModule) this.inputSelector.select(this.inputName); + if (!(this.input instanceof ThreadSafe + && this.inputSelector instanceof ThreadSafe)) { + this.inputSelector.release(this.input); + this.manager.release(this.inputSelector); + this.input = null; + this.inputSelector = null; + } + } else { + if (this.inputName != null) + if (getLogger().isErrorEnabled()) + getLogger().error( + "A problem occurred setting up '" + + this.inputName + + "': Selector is " + + (this.inputSelector != null ? "not " : "") + + "null, Component is " + + (this.inputSelector != null + && this.inputSelector.isSelectable(this.inputName) + ? "known" + : "unknown")); + } + } catch (Exception e) { + if (getLogger().isWarnEnabled()) + getLogger().warn( + "A problem occurred setting up '" + this.inputName + "': " + e.getMessage()); + } + } + + /** + * Recycle this component. + */ + public void recycle() { + reset(); + super.recycle(); + } + + /** + * Generate string representation of attributes. For debug only. + */ + protected String printAttributes(Attributes attr) { + StringBuffer sb = new StringBuffer(); + sb.append('['); + for (int i = 0; i < attr.getLength(); i++) { + sb.append('@').append(attr.getLocalName(i)).append("='").append( + attr.getValue(i)).append( + "' "); + } + sb.append(']'); + return sb.toString(); + } + + /** + * Handle input elements that may have a "checked" attributes, + * i.e. checkbox and radio. + */ + protected void startCheckableElement( + String aName, + String uri, + String name, + String raw, + AttributesImpl attributes) + throws SAXException { + + // @fixed and this.fixed already considered in startInputElement + this.values = this.getValues(aName); + String checked = attributes.getValue("checked"); + String value = attributes.getValue("value"); + boolean found = false; + + if (getLogger().isDebugEnabled()) + getLogger().debug( + "startCheckableElement " + + name + + " attributes " + + this.printAttributes(attributes)); + if (this.values != null) { + if (getLogger().isDebugEnabled()) + getLogger().debug("replacing"); + for (int i = 0; i < this.values.length; i++) { + if (this.values[i].equals(value)) { + found = true; + if (checked == null) { + attributes.addAttribute("", "checked", "checked", "CDATA", ""); + } + break; + } + } + if (!found && checked != null) { + attributes.removeAttribute(attributes.getIndex("checked")); + } + } + this.relayStartElement(uri, name, raw, attributes); + } + + /** + * Handle input elements that may don't have a "checked" + * attributes, e.g. text, password, button. + */ + protected void startNonCheckableElement( + String aName, + String uri, + String name, + String raw, + AttributesImpl attributes) + throws SAXException { + + // @fixed and this.fixed already considered in startInputElement + Object fValue = this.getNextValue(aName); + String value = attributes.getValue("value"); + if (getLogger().isDebugEnabled()) + getLogger().debug( + "startNonCheckableElement " + + name + + " attributes " + + this.printAttributes(attributes)); + if (fValue != null) { + if (getLogger().isDebugEnabled()) + getLogger().debug("replacing"); + if (value != null) { + attributes.setValue(attributes.getIndex("value"), String.valueOf(fValue)); + } else { + attributes.addAttribute("", "value", "value", "CDATA", String.valueOf(fValue)); + } + } + this.relayStartElement(uri, name, raw, attributes); + } + + /** + * Handle input elements. Calls startCheckableElement or + * startNonCheckableElement. + */ + protected void startInputElement(String uri, String name, String raw, Attributes attr) + throws SAXException { + + // @value = request.getParameterValues(@name) + String aName = getName(attr.getValue("name")); + String fixed = attr.getValue(this.fixedName); + + if (getLogger().isDebugEnabled()) + getLogger().debug( + "startInputElement " + name + " attributes " + this.printAttributes(attr)); + if (aName == null || this.fixed || (fixed != null && BooleanUtils.toBoolean(fixed))) { + this.relayStartElement(uri, name, raw, attr); + + } else { + if (getLogger().isDebugEnabled()) + getLogger().debug("replacing"); + + attr = this.normalizeAttributes(attr); + + AttributesImpl attributes = null; + if (attr instanceof AttributesImpl) { + attributes = (AttributesImpl) attr; + } else { + attributes = new AttributesImpl(attr); + } + String type = attributes.getValue("type"); + switch (((Integer) inputTypes.get(type, defaultType)).intValue()) { + case TYPE_CHECKBOX : + case TYPE_RADIO : + this.startCheckableElement(aName, uri, name, raw, attributes); + break; + + case TYPE_DEFAULT : + this.startNonCheckableElement(aName, uri, name, raw, attributes); + break; + } + this.values = null; + } + } + + /** + * Handle select elements. Sets up some instance variables for + * following option elements. + */ + protected void startSelectElement(String uri, String name, String raw, Attributes attr) + throws SAXException { + + // this.values = request.getParameterValues(@name) + String aName = getName(attr.getValue("name")); + String fixed = attr.getValue(this.fixedName); + this.values = null; + if (getLogger().isDebugEnabled()) + getLogger().debug( + "startSelectElement " + name + " attributes " + this.printAttributes(attr)); + if (aName != null && !(this.fixed || (fixed != null && BooleanUtils.toBoolean(fixed)))) { + if (attr.getIndex("multiple") > -1) { + this.values = this.getValues(aName); + } else { + Object val = this.getNextValue(aName); + if (val != null) { + this.values = new Object[1]; + this.values[0] = val; + } else { + this.values = null; + } + } + attr = this.normalizeAttributes(attr); + } + this.relayStartElement(uri, name, raw, attr); + } + + /** + * Handle option elements. Uses instance variables set up by + * startSelectElement. Relies on option having a "value" + * attribute, i.e. does not check following characters if "value" + * is not present. + */ + protected void startOptionElement(String uri, String name, String raw, Attributes attr) + throws SAXException { + + // add @selected if @value in request.getParameterValues(@name) + if (getLogger().isDebugEnabled()) + getLogger().debug( + "startOptionElement " + name + " attributes " + this.printAttributes(attr)); + if (this.values == null || this.fixed) { + this.relayStartElement(uri, name, raw, attr); + } else { + if (getLogger().isDebugEnabled()) + getLogger().debug("replacing"); + AttributesImpl attributes = null; + if (attr instanceof AttributesImpl) { + attributes = (AttributesImpl) attr; + } else { + attributes = new AttributesImpl(attr); + } + String selected = attributes.getValue("selected"); + String value = attributes.getValue("value"); + boolean found = false; + + for (int i = 0; i < this.values.length; i++) { + if (this.values[i].equals(value)) { + found = true; + if (selected == null) { + attributes.addAttribute("", "selected", "selected", "CDATA", ""); + } + break; + } + } + if (!found && selected != null) { + attributes.removeAttribute(attributes.getIndex("selected")); + } + + this.relayStartElement(uri, name, raw, attributes); + } + } + + /** + * Handles textarea elements. Skips nested events if request + * parameter with same name exists. + */ + protected void startTextareaElement(String uri, String name, String raw, Attributes attributes) + throws SAXException { + + String aName = getName(attributes.getValue("name")); + String fixed = attributes.getValue(this.fixedName); + Object value = null; + if (getLogger().isDebugEnabled()) + getLogger().debug( + "startTextareaElement " + name + " attributes " + this.printAttributes(attributes)); + if (aName != null) { + value = this.getNextValue(aName); + } + if (value == null || this.fixed || (fixed != null && BooleanUtils.toBoolean(fixed))) { + this.relayStartElement(uri, name, raw, attributes); + } else { + if (getLogger().isDebugEnabled()) + getLogger().debug("replacing"); + this.relayStartElement(uri, name, raw, this.normalizeAttributes(attributes)); + String valString = String.valueOf(value); + this.characters(valString.toCharArray(), 0, valString.length()); + // well, this doesn't really work out nicely. do it the hard way. + if (this.ignoreEventsCount == 0) + this.skipChildrenOnly = true; + this.ignoreEventsCount++; + } + } + + /** + * Handle error elements. If validation results are available, + * compares validation result for parameter with the same name as + * the "name" attribute with the result names is "when" and + * "when-ge". Drops element and all nested events when error + * condition is not met. + */ + protected void startErrorElement(String uri, String name, String raw, Attributes attr) + throws SAXException { + + if (getLogger().isDebugEnabled()) + getLogger().debug( + "startErrorElement " + name + " attributes " + this.printAttributes(attr)); + if (this.ignoreValidation) { + this.relayStartElement(uri, name, raw, attr); + } else if (this.validationResults == null || this.fixed) { + this.relayStartElement(true, false, uri, name, raw, attr); + } else { + String aName = attr.getValue("name"); + if (aName == null) { + this.relayStartElement(uri, name, raw, attr); + } else { + ValidatorActionResult validation = + FormValidatorHelper.getParamResult(this.objectModel, aName); + String when = attr.getValue("when"); + String when_ge = attr.getValue("when-ge"); + + if ((when != null && when.equals(validatorResults.get(validation))) + || (when_ge != null + && validation.ge( + (ValidatorActionResult) validatorResultLabel.get( + when_ge, + ValidatorActionResult.MAXERROR)))) { + AttributesImpl attributes = null; + if (attr instanceof AttributesImpl) { + attributes = (AttributesImpl) attr; + } else { + attributes = new AttributesImpl(attr); + } + // remove attributes not meant for client + attributes.removeAttribute(attributes.getIndex("name")); + if (when != null) + attributes.removeAttribute(attributes.getIndex("when")); + if (when_ge != null) + attributes.removeAttribute(attributes.getIndex("when-ge")); + this.relayStartElement(uri, name, raw, this.normalizeAttributes(attributes)); + } else { + this.relayStartElement(true, true, uri, name, raw, attr); + } + } + } + } + + /** + * Start processing a form element. Sets protection indicator if attribute + * "fixed" is present and either "true" or "yes". Removes attribute "fixed" + * if present. + * @param uri The namespace of the element. + * @param name The local name of the element. + * @param raw The qualified name of the element. + * @param attr The attributes of the element. + */ + protected void startFormElement(String uri, String name, String raw, Attributes attr) + throws SAXException { + + String fixed = attr.getValue(this.fixedName); + if (this.useFormName) { + this.formName = attr.getValue("name"); + } + if (fixed == null) { + this.relayStartElement(uri, name, raw, attr); + } else { + if (!this.fixed && BooleanUtils.toBoolean(fixed)) { + this.fixed = true; + } + // remove attributes not meant for client + AttributesImpl attributes = null; + if (attr instanceof AttributesImpl) { + attributes = (AttributesImpl) attr; + } else { + attributes = new AttributesImpl(attr); + } + attributes.removeAttribute(attributes.getIndex(this.fixedName)); + this.relayStartElement(uri, name, raw, this.normalizeAttributes(attributes)); + } + } + + /** + * Start recording repeat element contents and push repeat expression and + * variable to repeater stack. Only start recording, if no other recorder is + * currently running. + * + * @param uri + * @param name + * @param raw + * @param attr + * @throws SAXException + */ + protected void startRepeatElement(String uri, String name, String raw, Attributes attr) + throws SAXException { + + if (this.recordingCount == 0) { + if (!(this.fixed || BooleanUtils.toBoolean(attr.getValue(this.fixedName)))) { + RepeaterStatus status = + new RepeaterStatus("${" + attr.getValue("using") + "}", 0, attr.getValue("on")); + this.repeater.add(status); + this.startRecording(); + this.recordingCount++; + } else { + this.relayStartElement(uri, name, raw, attr); + } + } else { + this.relayStartElement(uri, name, raw, attr); + this.recordingCount++; + } + } + + /** + * Stop recording repeat contents and replay required number of times. + * Stop only if outmost repeat element is ending. + * + * @param uri + * @param name + * @param raw + * @throws SAXException + */ + protected void endRepeatElement(String uri, String name, String raw) throws SAXException { + this.recordingCount--; + if (this.recordingCount == 0) { + DocumentFragment fragment = this.endRecording(); + RepeaterStatus status = (RepeaterStatus) this.repeater.get(this.repeater.size() - 1); + Object[] vals = this.getValues(this.getName(status.expr)); + int count = (vals != null ? vals.length : 0); + for (status.count = 1; status.count <= count; status.count++) { + DOMStreamer streamer = new DOMStreamer(this, this); + streamer.stream(fragment); + } + this.repeater.remove(this.repeater.size() - 1); + } else { + this.relayEndElement(uri, name, raw); + if (this.recordingCount < 0) { + this.recordingCount = 0; + } + } + } + + /** + * Start processing elements of our namespace. + * This hook is invoked for each sax event with our namespace. + * @param uri The namespace of the element. + * @param name The local name of the element. + * @param raw The qualified name of the element. + * @param attr The attributes of the element. + */ + public void startTransformingElement(String uri, String name, String raw, Attributes attr) + throws SAXException { + + if (this.ignoreEventsCount == 0 && this.recordingCount == 0) { + switch (((Integer) elementNames.get(name, defaultElement)).intValue()) { + case ELEMENT_INPUT : + this.startInputElement(uri, name, raw, attr); + break; + + case ELEMENT_SELECT : + this.startSelectElement(uri, name, raw, attr); + break; + + case ELEMENT_OPTION : + this.startOptionElement(uri, name, raw, attr); + break; + + case ELEMENT_TXTAREA : + this.startTextareaElement(uri, name, raw, attr); + break; + + case ELEMENT_ERROR : + this.startErrorElement(uri, name, raw, attr); + break; + + case ELEMENT_FORM : + this.startFormElement(uri, name, raw, attr); + break; + + case ELEMENT_REPEAT : + this.startRepeatElement(uri, name, raw, attr); + break; + + default : + this.relayStartElement(uri, name, raw, attr); + } + + } else if (this.recordingCount > 0) { + switch (((Integer) elementNames.get(name, defaultElement)).intValue()) { + case ELEMENT_REPEAT : + this.startRepeatElement(uri, name, raw, attr); + break; + + default : + this.relayStartElement(uri, name, raw, attr); + } + } else { + this.relayStartElement(uri, name, raw, attr); + } + } + + /** + * Start processing elements of our namespace. + * This hook is invoked for each sax event with our namespace. + * @param uri The namespace of the element. + * @param name The local name of the element. + * @param raw The qualified name of the element. + */ + public void endTransformingElement(String uri, String name, String raw) throws SAXException { + + if (this.ignoreEventsCount > 0) { + this.relayEndElement(uri, name, raw); + } else if (this.recordingCount > 0) { + switch (((Integer) elementNames.get(name, defaultElement)).intValue()) { + case ELEMENT_REPEAT : + this.endRepeatElement(uri, name, raw); + break; + + default : + this.relayEndElement(uri, name, raw); + } + } else { + switch (((Integer) elementNames.get(name, defaultElement)).intValue()) { + case ELEMENT_SELECT : + this.values = null; + this.relayEndElement(uri, name, raw); + break; + case ELEMENT_INPUT : + case ELEMENT_OPTION : + case ELEMENT_TXTAREA : + case ELEMENT_ERROR : + this.relayEndElement(uri, name, raw); + break; + case ELEMENT_FORM : + this.fixed = this.documentFixed; + this.formName = null; + this.relayEndElement(uri, name, raw); + break; + + case ELEMENT_REPEAT : + this.endRepeatElement(uri, name, raw); + break; + + default : + this.relayEndElement(uri, name, raw); + } + } + } + + /** + * Remove extra information from element's attributes. Currently only removes + * the repeater variable from the element's name attribute if present. + * + * @param attr + * @return modified attributes + */ + private Attributes normalizeAttributes(Attributes attr) { + Attributes result = attr; + if (this.stripNumber && this.repeater.size() > 0) { + String name = attr.getValue("name"); + if (name != null) { + for (Iterator i = this.repeater.iterator(); i.hasNext();) { + RepeaterStatus status = (RepeaterStatus) i.next(); + int pos = name.indexOf(status.var); + if (pos >= 0) { + AttributesImpl attributes; + if (result instanceof AttributesImpl) { + attributes = (AttributesImpl) result; + } else { + attributes = new AttributesImpl(result); + } + name = + name.substring(0, pos - this.decorationSize) + + name.substring(pos + status.var.length() + this.decorationSize); + attributes.setValue(attributes.getIndex("name"), name); + result = attributes; + } + } + } + } + return result; + } + + /** + * Generate the "real" name of an element for value lookup. + * @param name + * @return "real" name. + */ + private String getName(String name) { + String result = name; + if (this.useFormName && this.formName != null) { + if (this.separator != null) { + if (this.useFormNameTwice) { + result = + this.formName + this.separator + this.formName + this.separator + result; + } else { + result = this.formName + this.separator + result; + } + } else { + if (this.useFormNameTwice) { + result = this.formName + result; + } else { + // does this make sense ? + result = this.formName + this.formName + result; + } + } + } + if (this.prefix != null) { + result = this.prefix + result; + } + if (this.suffix != null) { + result = result + this.prefix; + } + if (this.repeater.size() > 0) { + for (Iterator i = this.repeater.iterator(); i.hasNext();) { + RepeaterStatus status = (RepeaterStatus) i.next(); + int pos = result.indexOf(status.var); + if (pos != -1) { + result = + result.substring(0, pos) + + status.count + + result.substring(pos + status.var.length()); + } + } + } + return result; + } + + /** + * Obtain values from used InputModule if not done already and return the + * next value. If no more values exist, returns null. + * + * @param name + * @return + */ + private Object getNextValue(String name) { + Object result = null; + if (this.formValues.containsKey(name)) { + ValueList vList = (ValueList) this.formValues.get(name); + result = vList.getNext(); + } else { + ValueList vList = new ValueList(this.getValues(name)); + result = vList.getNext(); + this.formValues.put(name, vList); + } + return result; + } + + /** + * Obtain values from the used InputModule. + */ + private Object[] getValues(String name) { + Object[] values = null; + ServiceSelector iputSelector = null; + InputModule iput = null; + try { + if (this.input != null) { + // input module is thread safe + // thus we still have a reference to it + values = input.getAttributeValues(name, this.inputConf, objectModel); + if (getLogger().isDebugEnabled()) + getLogger().debug("cached module " + this.input + + " attribute " + name + + " returns " + values); + } else { + // input was not thread safe + // so acquire it again + iputSelector = (ServiceSelector)this.manager.lookup(INPUT_MODULE_SELECTOR); + if (this.inputName != null + && iputSelector != null + && iputSelector.isSelectable(this.inputName)) { + + iput = (InputModule) iputSelector.select(this.inputName); + } + if (iput != null) { + values = iput.getAttributeValues(name, this.inputConf, objectModel); + } + if (getLogger().isDebugEnabled()) + getLogger().debug( + "fresh module " + iput + " attribute " + name + " returns " + values); + } + } catch (Exception e) { + if (getLogger().isWarnEnabled()) + getLogger().warn( + "A problem occurred acquiring a value from '" + + this.inputName + + "' for '" + + name + + "': " + + e.getMessage()); + } finally { + // release components if necessary + if (iputSelector != null) { + if (iput != null) + iputSelector.release(iput); + this.manager.release(iputSelector); + } + } + + return values; + } + + /** + * Calls the super's method startTransformingElement. + * + * @param uri + * @param name + * @param raw + * @param attr + * @throws SAXException + */ + protected void relayStartElement(String uri, String name, String raw, Attributes attr) + throws SAXException { + this.relayStartElement(false, false, uri, name, raw, attr); + } + + /** + * Calls the super's method startTransformingElement and increments the + * ignoreEventsCount if skip is true. Increment can be done either before + * invoking super's method, so that the element itself is skipped, or afterwards, + * so that only the children are skipped. + * + * @param skip + * @param skipChildrenOnly + * @param uri + * @param name + * @param raw + * @param attr + * @throws SAXException + */ + protected void relayStartElement( + boolean skip, + boolean skipChildrenOnly, + String uri, + String name, + String raw, + Attributes attr) + throws SAXException { + + try { + if (this.ignoreEventsCount > 0) { + this.ignoreEventsCount++; + super.startTransformingElement(uri, name, raw, attr); + } else { + if (skip) + this.skipChildrenOnly = skipChildrenOnly; + if (skip && !skipChildrenOnly) + this.ignoreEventsCount++; + super.startTransformingElement(uri, name, raw, attr); + if (skip && skipChildrenOnly) + this.ignoreEventsCount++; + } + } catch (ProcessingException e) { + throw new SAXException(e); + } catch (IOException e) { + throw new SAXException(e); + } + } + + /** + * Calls the super's method endTransformingElement and decrements the + * ignoreEventsCount if larger than zero. + * + * @param uri + * @param name + * @param raw + * @throws SAXException + */ + protected void relayEndElement(String uri, String name, String raw) throws SAXException { + + if (this.ignoreEventsCount == 1 && this.skipChildrenOnly) + this.ignoreEventsCount--; + try { + super.endTransformingElement(uri, name, raw); + } catch (ProcessingException e) { + throw new SAXException(e); + } catch (IOException e) { + throw new SAXException(e); + } catch (Exception e) { + getLogger().error("exception", e); + } + + if (this.ignoreEventsCount > 0) + this.ignoreEventsCount--; + } + +} Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/transformation/SimpleFormTransformer.java ------------------------------------------------------------------------------ svn:eol-style = native