From cvs-return-25110-apmail-cocoon-cvs-archive=cocoon.apache.org@cocoon.apache.org Thu Nov 03 14:46:03 2005 Return-Path: Delivered-To: apmail-cocoon-cvs-archive@www.apache.org Received: (qmail 25040 invoked from network); 3 Nov 2005 14:45:39 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 3 Nov 2005 14:45:39 -0000 Received: (qmail 33920 invoked by uid 500); 3 Nov 2005 14:39:08 -0000 Delivered-To: apmail-cocoon-cvs-archive@cocoon.apache.org Received: (qmail 30336 invoked by uid 500); 3 Nov 2005 14:38:39 -0000 Mailing-List: contact cvs-help@cocoon.apache.org; run by ezmlm Precedence: bulk Reply-To: dev@cocoon.apache.org list-help: list-unsubscribe: List-Post: List-Id: Delivered-To: mailing list cvs@cocoon.apache.org Received: (qmail 27861 invoked by uid 99); 3 Nov 2005 14:36:58 -0000 X-ASF-Spam-Status: No, hits=-9.4 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received: from [209.237.227.194] (HELO minotaur.apache.org) (209.237.227.194) by apache.org (qpsmtpd/0.29) with SMTP; Thu, 03 Nov 2005 06:35:30 -0800 Received: (qmail 12951 invoked by uid 65534); 3 Nov 2005 14:35:10 -0000 Message-ID: <20051103143510.12948.qmail@minotaur.apache.org> Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r330548 [60/132] - in /cocoon/whiteboard/maven2/cocoon-flat-layout: ./ cocoon-ajax-block/ cocoon-ajax-block/api/ cocoon-ajax-block/api/src/ cocoon-ajax-block/api/src/main/ cocoon-ajax-block/api/src/main/java/ cocoon-ajax-block/api/src/main/... Date: Thu, 03 Nov 2005 14:00:48 -0000 To: cvs@cocoon.apache.org From: jheymans@apache.org X-Mailer: svnmailer-1.0.5 X-Virus-Checked: Checked by ClamAV on apache.org X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/transformation/SimpleFormInstanceExtractionTransformer.java URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/transformation/SimpleFormInstanceExtractionTransformer.java?rev=330548&view=auto ============================================================================== --- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/transformation/SimpleFormInstanceExtractionTransformer.java (added) +++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/transformation/SimpleFormInstanceExtractionTransformer.java Thu Nov 3 05:41:06 2005 @@ -0,0 +1,250 @@ +/* + * Copyright 1999-2004 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 java.io.IOException; +import java.util.Map; + +import org.apache.avalon.framework.configuration.Configurable; +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.ServiceException; +import org.apache.avalon.framework.service.ServiceManager; +import org.apache.avalon.framework.service.ServiceSelector; +import org.apache.avalon.framework.service.Serviceable; + +import org.apache.avalon.excalibur.pool.Recyclable; + +import org.apache.cocoon.ProcessingException; +import org.apache.cocoon.components.modules.output.OutputModule; +import org.apache.cocoon.environment.SourceResolver; +import org.apache.cocoon.xml.dom.DocumentWrapper; + +import org.w3c.dom.Document; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; + +/** + * @cocoon.sitemap.component.documentation + * This transformer sieves an incoming stream of xml and extracts a + * document fragment from it depending on a given tag and stores the + * fragment using an OutputModule with a name based an attribute of + * another enclosing tag. Default configuration fires on + * <form-instance/> and uses @name of enclosing <form/> + * tag. Default OutputModule is request-attr. This is usefull in + * conjunction with the SimpleFormTransformer when setting the + * InputModule for it to a chain of request-param and request-attr so + * that the extracted form instance data is used only when no similar + * request parameter exists. + * + * @cocoon.sitemap.component.name simple-form-instance + * @cocoon.sitemap.component.logger sitemap.transformer.simple-form-instance + * + * @author Christian Haul + * @version CVS $Id: SimpleFormInstanceExtractionTransformer.java 30941 2004-07-29 19:56:58Z vgritsenko $ + */ +public class SimpleFormInstanceExtractionTransformer extends AbstractExtractionTransformer + implements Configurable, Serviceable, Recyclable { + + protected class ElementData { + public String uri = null; + public String loc = null; + public String raw = null; + + public ElementData() { + } + + public ElementData(String uri, String loc, String raw) { + this.uri = uri; + this.loc = loc; + this.raw =raw; + } + + public boolean equals(String uri, String loc, String raw) { + + if (!this.uri.equals(uri)) + return false; + if (!this.loc.equals(loc)) + return false; + if (!this.raw.equals(raw)) + return false; + return true; + } + + } + + protected final static String OUTPUT_MODULE_SELECTOR = OutputModule.ROLE+"Selector"; + + ElementData startElement = null; + ElementData nameElement = null; + String qname = "name"; + + String instanceName = null; + boolean nameAsRoot = true; + + String outputModuleName = "request-attr"; + Configuration outputConf = null; + + ServiceManager manager = null; + Map objectModel = null; + + public void configure(Configuration config) throws ConfigurationException { + this.startElement = new ElementData(); + this.startElement.uri = config.getChild("start").getAttribute("uri", ""); + this.startElement.loc = config.getChild("start").getAttribute("local-name", "form-instance"); + this.startElement.raw = config.getChild("start").getAttribute("raw-name", "form-instance"); + + this.nameElement = new ElementData(); + this.nameElement.uri = config.getChild("name").getAttribute("uri", ""); + this.nameElement.loc = config.getChild("name").getAttribute("local-name", "form"); + this.nameElement.raw = config.getChild("name").getAttribute("raw-name", "form"); + this.qname = config.getChild("name").getAttribute("name-attribute", "name"); + + this.nameAsRoot = config.getChild("name-as-root").getValueAsBoolean(this.nameAsRoot); + + this.outputConf = config.getChild("output"); + this.outputModuleName = this.outputConf.getAttribute("name",this.outputModuleName); + } + + public void service(ServiceManager manager) throws ServiceException { + this.manager = manager; + } + + /** Setup the transformer. */ + public void setup(SourceResolver resolver, Map objectModel, String src, Parameters parameters) + throws ProcessingException, SAXException, IOException { + super.setup(resolver, objectModel, src, parameters); + this.objectModel = objectModel; + } + + public void recycle() { + super.recycle(); + this.instanceName = null; + } + + /** + * 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 + * raw names are not available. + * @param a The attributes attached to the element. If there are no + * attributes, it shall be an empty Attributes object. + * @return a 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 + * + * + *

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:

+ *   <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