KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > transformation > SimpleFormTransformer


1 /*
2  * Copyright 1999-2005 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.cocoon.transformation;
17
18 import org.apache.avalon.framework.configuration.Configuration;
19 import org.apache.avalon.framework.configuration.ConfigurationException;
20 import org.apache.avalon.framework.parameters.Parameters;
21 import org.apache.avalon.framework.service.ServiceSelector;
22 import org.apache.avalon.framework.thread.ThreadSafe;
23
24 import org.apache.avalon.excalibur.pool.Recyclable;
25
26 import org.apache.cocoon.ProcessingException;
27 import org.apache.cocoon.acting.ValidatorActionResult;
28 import org.apache.cocoon.transformation.helpers.FormValidatorHelper;
29 import org.apache.cocoon.components.modules.input.InputModule;
30 import org.apache.cocoon.environment.SourceResolver;
31 import org.apache.cocoon.util.HashMap;
32 import org.apache.cocoon.xml.dom.DOMStreamer;
33 import org.apache.commons.lang.BooleanUtils;
34
35 import org.w3c.dom.DocumentFragment JavaDoc;
36 import org.xml.sax.Attributes JavaDoc;
37 import org.xml.sax.SAXException JavaDoc;
38 import org.xml.sax.helpers.AttributesImpl JavaDoc;
39
40 import java.io.IOException JavaDoc;
41 import java.util.Iterator JavaDoc;
42 import java.util.LinkedList JavaDoc;
43 import java.util.List JavaDoc;
44 import java.util.Map JavaDoc;
45
46 /**
47  * @cocoon.sitemap.component.documentation
48  * Eliminates the need for XSP to use FormValidatorAction or HTML forms.
49  * Caveat: Select options need a value attribute to work correctly.
50  *
51  * @cocoon.sitemap.component.name simple-form
52  * @cocoon.sitemap.component.logger sitemap.transformer.simple-form
53  *
54  *
55  * <p>This transformer fills all HTML 4 form elements with values from
56  * an InputModule, e.g. request, with the same name. It handles select
57  * boxes, textareas, checkboxes, radio buttons, password and text
58  * fields, and buttons. Form elements and complete forms can be protected
59  * from substitution by adding an attribute fixed="true" to them.</p>
60  *
61  * <p>In addition, it handles FormValidatorAction results by
62  * selectively omitting &lt;error/&gt; elements. These elements need a
63  * "name" attribute corresponding to the name of the form element, and
64  * either a "when" or a "when-ge" attribute.</p>
65  *
66  * <p>An error element is send down the pipeline if validation results
67  * are available and either the results equals the "when" attribute or
68  * the result is greater or equal to the "when-ge" attribute.</p>
69  *
70  * <p>Names for validation results are "ok", "not-present", "error",
71  * "is-null", "too-small", "too-large", and "no-match" for the similar
72  * named values from ValidatorActionResult.</p>
73  *
74  * <p>There need not to be an "error" element for every form element,
75  * multiple error elements for the same form element may be
76  * present.</p>
77  *
78  * <p><em>Names of error elements are never augmented by prefix, suffix or
79  * form name.</em></p>
80  *
81  * <p>Page parts with multiple occurrences depending on the number of
82  * actual parameters can be enclosed in &lt;repeat on="expr" using="var"/&gt;
83  * elements. <em>expr</em> is used to determine the number of occurrences
84  * and <em>var</em> will be expanded with the ordinary number. Repeat elements
85  * can be nested.</p>
86  *
87  * <p>Example:</p>
88  * <pre>
89  * <repeat on="mult" using="i"><input type="text" name="mult[${i}]"/></repeat>
90  * </pre>
91  * <p>Will include as many input elements as mult parameters are present. Adding
92  * the repeater variable to the elements name is necessary only with structured
93  * parameters or when they should be numbered. See also the <em>strip-number</em>
94  * configuration parameter.</p>
95  *
96  * <p>To use this transformer, add the following to your
97  * transformation pipeline: <pre>
98  * &lt;map:transform type="simple-form"/&gt;
99  * </pre></p>
100  *
101  * <p>Configuration elements:
102  * <table>
103  * <tr><td>input-module</td><td>(String) InputModule configuration,
104  * defaults to an empty configuration and the "request-param" module</td></tr>
105  * <tr><td>fixed-attribute</td><td>(String) Name of the attribute used to
106  * indicate that this element should not be changed. ("fixed")</td></tr>
107  * <tr><td>use-form-name</td><td>(boolean) Add the name of the form to the
108  * name of form elements. Uses default Separator , if default separator is null
109  * or empty, separator is set to "/". ("false")</td></tr>
110  * <tr><td>use-form-name-twice</td><td>(boolean) Add the name of the form twice to the
111  * name of form elements. This is useful when the form instance has no
112  * all enclosing root tag and the form name is used instead <em>and</em> the
113  * form name needs to be used to find the form data. Uses default Separator ,
114  * if default separator is null or empty, separator is set to "/".("false")</td></tr>
115  * <tr><td>separator</td><td>(String) Separator between form name and element name ("/")
116  * </td></tr>
117  * <tr><td>prefix</td><td>(String) Prefix to add to element name for value lookup. No
118  * separator will be added between prefix and rest of the name. Default
119  * is "", when use-form-name is set, defaults to separator.</td></tr>
120  * <tr><td>suffix</td><td>(String) Added to the input element's name. No
121  * separator will be added between rest of the name and suffix. ("")</td></tr>
122  * <tr><td>ignore-validation</td><td>(boolean) If set to true, all error
123  * tags are copied as is regardless of the validation results.("false")</td></tr>
124  * <tr><td>decoration</td><td>(int) Length of decorations around repeat variable. Example:
125  * when using JXPath based module, decoration would be "[" and "]", hence 1. (1)</td></tr>
126  * <tr><td>strip-number</td><td>(boolean) If set to false, element names of repeated
127  * elements will contain the expanded repeater variable. ("true")</td></tr>
128  * </table>
129  * </p>
130  *
131  * <p>Sitemap parameters:
132  * <table>
133  * <tr><td>fixed</td><td>(boolean) Do not change values</td></tr>
134  * <tr><td>prefix</td><td>(String) Added to the input element's name</td></tr>
135  * <tr><td>suffix</td><td>(String) Added to the input element's name</td></tr>
136  * <tr><td>input</td><td>(String) InputModule name</td></tr>
137  * <tr><td>decoration</td><td>(int) Length of decorations around repeat variable.</td></tr>
138  * <tr><td>strip-number</td><td>(boolean) Expanded repeater variable.</td></tr>
139  * </table>
140  * </p>
141  *
142  * <p>Example:<pre>
143  * &lt;input name="user.name" size="50" maxlength="60"/&gt;
144  * &lt;error name="user.name" when-ge="error"&gt;required&lt;/error&gt;
145  * </pre></p>
146  *
147  * @author <a HREF="mailto:haul@apache.org">Christian Haul</a>
148  * @version $Id: SimpleFormTransformer.java 168366 2005-05-05 18:23:18Z vgritsenko $
149  */

150 public class SimpleFormTransformer extends AbstractSAXTransformer implements Recyclable {
151
152     /** strip numbers from repeated element name attributes */
153     private boolean stripNumber = true;
154
155     /** Symbolic names for elements */
156     /** unknown element */
157     private static final int ELEMENT_DEFAULT = 0;
158     /** input element */
159     private static final int ELEMENT_INPUT = 1;
160     /** select element */
161     private static final int ELEMENT_SELECT = 2;
162     /** option element */
163     private static final int ELEMENT_OPTION = 3;
164     /** textarea element */
165     private static final int ELEMENT_TXTAREA = 4;
166     /** error element */
167     private static final int ELEMENT_ERROR = 5;
168     /** form element */
169     private static final int ELEMENT_FORM = 6;
170     /** repeat element */
171     private static final int ELEMENT_REPEAT = 7;
172     /** default element as Integer (needed as default in org.apache.cocoon.util.HashMap.get()) */
173     private static final Integer JavaDoc defaultElement = new Integer JavaDoc(ELEMENT_DEFAULT);
174
175     /** input type unknown */
176     private static final int TYPE_DEFAULT = 0;
177     /** input type checkbox */
178     private static final int TYPE_CHECKBOX = 1;
179     /** input type radio */
180     private static final int TYPE_RADIO = 2;
181     /** default input type as Integer (needed as default in org.apache.cocoon.util.HashMap.get()) */
182     private static final Integer JavaDoc defaultType = new Integer JavaDoc(TYPE_DEFAULT);
183
184     protected static final String JavaDoc INPUT_MODULE_ROLE = InputModule.ROLE;
185     protected static final String JavaDoc INPUT_MODULE_SELECTOR = INPUT_MODULE_ROLE + "Selector";
186
187     /** map element name string to symbolic name */
188     private static final HashMap elementNames;
189     /** map input type string to symbolic name */
190     private static final HashMap inputTypes;
191     /** map ValidatorActionResult to name string */
192     private static final HashMap validatorResults;
193     /** map name string to ValidatorActionResult */
194     private static final HashMap validatorResultLabel;
195
196     /** setup mapping tables */
197     static {
198         HashMap names = new HashMap();
199         names.put("input", new Integer JavaDoc(ELEMENT_INPUT));
200         names.put("select", new Integer JavaDoc(ELEMENT_SELECT));
201         names.put("option", new Integer JavaDoc(ELEMENT_OPTION));
202         names.put("textarea", new Integer JavaDoc(ELEMENT_TXTAREA));
203         names.put("error", new Integer JavaDoc(ELEMENT_ERROR));
204         names.put("form", new Integer JavaDoc(ELEMENT_FORM));
205         names.put("repeat", new Integer JavaDoc(ELEMENT_REPEAT));
206         elementNames = names;
207         names = null;
208
209         names = new HashMap();
210         names.put("checkbox", new Integer JavaDoc(TYPE_CHECKBOX));
211         names.put("radio", new Integer JavaDoc(TYPE_RADIO));
212         inputTypes = names;
213         names = null;
214
215         names = new HashMap();
216         names.put("ok", ValidatorActionResult.OK);
217         names.put("not-present", ValidatorActionResult.NOTPRESENT);
218         names.put("error", ValidatorActionResult.ERROR);
219         names.put("is-null", ValidatorActionResult.ISNULL);
220         names.put("too-small", ValidatorActionResult.TOOSMALL);
221         names.put("too-large", ValidatorActionResult.TOOLARGE);
222         names.put("no-match", ValidatorActionResult.NOMATCH);
223         validatorResultLabel = names;
224
225         names = new HashMap();
226         names.put(ValidatorActionResult.OK, "ok");
227         names.put(ValidatorActionResult.NOTPRESENT, "not-present");
228         names.put(ValidatorActionResult.ERROR, "error");
229         names.put(ValidatorActionResult.ISNULL, "is-null");
230         names.put(ValidatorActionResult.TOOSMALL, "too-small");
231         names.put(ValidatorActionResult.TOOLARGE, "too-large");
232         names.put(ValidatorActionResult.NOMATCH, "no-match");
233         validatorResults = names;
234         names = null;
235     }
236
237     /** current element's request parameter values */
238     protected Object JavaDoc[] values;
239
240     /** current request's validation results (all validated elements) */
241     protected Map validationResults;
242
243     /** Should we skip inserting values? */
244     private boolean fixed;
245     /** Is the complete document protected? */
246     private boolean documentFixed;
247
248     private String JavaDoc fixedName = "fixed";
249     private String JavaDoc prefix;
250     private String JavaDoc suffix;
251     private String JavaDoc defaultPrefix;
252     private String JavaDoc defaultSuffix;
253     private String JavaDoc separator;
254     private String JavaDoc formName;
255     private boolean useFormName;
256     private boolean useFormNameTwice;
257     private boolean ignoreValidation;
258     private int decorationSize = 1;
259
260     private String JavaDoc defaultInput = "request-param";
261     private Configuration defaultInputConf;
262     private Configuration inputConf;
263     private InputModule input;
264     private ServiceSelector inputSelector;
265     private String JavaDoc inputName;
266
267     /** Skip element's content only. Otherwise skip also surrounding element. */
268     protected boolean skipChildrenOnly;
269
270     /** Count nested repeat elements. */
271     protected int recordingCount;
272
273     /** List of {@link RepeaterStatus} elements keeping track of nested repeat blocks. */
274     protected List JavaDoc repeater;
275
276     /** Map of {@link ValueList} to track multiple parameters. */
277     protected Map formValues;
278
279     /**
280      * Keep track of repeater status.
281      */

282     protected static class RepeaterStatus {
283         public String JavaDoc var = null;
284         public String JavaDoc expr = null;
285         public int count = 0;
286
287         public RepeaterStatus(String JavaDoc var, int count, String JavaDoc expr) {
288             this.var = var;
289             this.count = count;
290             this.expr = expr;
291         }
292
293         public String JavaDoc toString() {
294             return "[" + this.var + "," + this.expr + "," + this.count + "]";
295         }
296     }
297
298     /**
299      * Keep track of multiple values.
300      */

301     protected static class ValueList {
302         private int current = -1;
303         private Object JavaDoc[] values = null;
304
305         public ValueList(Object JavaDoc[] values) {
306             this.values = values;
307             this.current = (values != null && values.length > 0 ? 0 : -1);
308         }
309
310         public Object JavaDoc getNext() {
311             Object JavaDoc result = null;
312             if (this.values != null) {
313                 if (this.current < this.values.length) {
314                     result = this.values[this.current++];
315                 }
316             }
317             return result;
318         }
319     }
320
321     public SimpleFormTransformer() {
322         this.defaultNamespaceURI = "";
323     }
324
325     /** set per instance variables to defaults */
326     private void reset() {
327         this.skipChildrenOnly = false;
328         this.values = null;
329         this.validationResults = null;
330         this.documentFixed = false;
331         this.fixed = false;
332         this.formName = null;
333         this.recordingCount = 0;
334         this.repeater = new LinkedList JavaDoc();
335         this.formValues = new HashMap();
336
337         if (this.inputSelector != null) {
338             if (this.input != null)
339                 this.inputSelector.release(this.input);
340             this.manager.release(this.inputSelector);
341         }
342     }
343
344     /**
345      * Avalon Configurable Interface
346      */

347     public void configure(Configuration config) throws ConfigurationException {
348         super.configure(config);
349
350         this.defaultInputConf = config.getChild("input-module");
351         this.defaultInput = this.defaultInputConf.getAttribute("name", this.defaultInput);
352         this.separator = config.getChild("separator").getValue(this.separator);
353         this.defaultPrefix = config.getChild("prefix").getValue(this.defaultPrefix);
354         this.defaultSuffix = config.getChild("suffix").getValue(this.defaultSuffix);
355         this.fixedName = config.getChild("fixed-attribute").getValue(this.fixedName);
356         this.useFormName = config.getChild("use-form-name").getValueAsBoolean(this.useFormName);
357         this.useFormNameTwice =
358             config.getChild("use-form-name-twice").getValueAsBoolean(this.useFormNameTwice);
359         this.useFormName = this.useFormName || this.useFormNameTwice;
360         if (this.useFormName) {
361             this.separator =
362                 (this.separator == null || this.separator.equals("") ? "/" : this.separator);
363             this.defaultPrefix = this.separator;
364         }
365         this.ignoreValidation =
366             config.getChild("ignore-validation").getValueAsBoolean(this.ignoreValidation);
367         this.decorationSize = config.getChild("decoration").getValueAsInteger(this.decorationSize);
368         this.stripNumber = config.getChild("strip-number").getValueAsBoolean(this.stripNumber);
369     }
370
371     /**
372      * Read sitemap parameters and set properties accordingly.
373      */

374     private void evaluateParameters() {
375         this.documentFixed = this.parameters.getParameterAsBoolean("fixed", false);
376         this.fixed = this.documentFixed;
377         this.prefix = this.parameters.getParameter("prefix", this.defaultPrefix);
378         this.suffix = this.parameters.getParameter("suffix", this.defaultSuffix);
379         this.inputName = this.parameters.getParameter("input", null);
380         this.decorationSize =
381             this.parameters.getParameterAsInteger("decoration", this.decorationSize);
382         this.stripNumber = this.parameters.getParameterAsBoolean("strip-number", this.stripNumber);
383     }
384
385     /**
386      * Setup the next round.
387      * The instance variables are initialised.
388      * @param resolver The current SourceResolver
389      * @param objectModel The objectModel of the environment.
390      * @param src The value of the src attribute in the sitemap.
391      * @param par The parameters from the sitemap.
392      */

393     public void setup(SourceResolver resolver, Map objectModel, String JavaDoc src, Parameters par)
394         throws ProcessingException, SAXException JavaDoc, IOException JavaDoc {
395
396         this.reset();
397
398         super.setup(resolver, objectModel, src, par);
399
400         if (request == null) {
401             getLogger().debug("no request object");
402             throw new ProcessingException("no request object");
403         }
404         this.evaluateParameters();
405         this.setupInputModule();
406
407     }
408
409     /**
410      * Setup and obtain reference to the input module.
411      */

412     private void setupInputModule() {
413         this.inputConf = null;
414         if (this.ignoreValidation) {
415             this.validationResults = null;
416         } else {
417             this.validationResults = FormValidatorHelper.getResults(this.objectModel);
418         }
419
420         if (this.inputName == null) {
421             this.inputName = this.defaultInput;
422             this.inputConf = this.defaultInputConf;
423         }
424
425         try {
426             // obtain input module
427
this.inputSelector = (ServiceSelector) this.manager.lookup(INPUT_MODULE_SELECTOR);
428             if (this.inputName != null
429                 && this.inputSelector != null
430                 && this.inputSelector.isSelectable(this.inputName)) {
431                 this.input = (InputModule) this.inputSelector.select(this.inputName);
432                 if (!(this.input instanceof ThreadSafe
433                     && this.inputSelector instanceof ThreadSafe)) {
434                     this.inputSelector.release(this.input);
435                     this.manager.release(this.inputSelector);
436                     this.input = null;
437                     this.inputSelector = null;
438                 }
439             } else {
440                 if (this.inputName != null)
441                     if (getLogger().isErrorEnabled())
442                         getLogger().error(
443                             "A problem occurred setting up '"
444                                 + this.inputName
445                                 + "': Selector is "
446                                 + (this.inputSelector != null ? "not " : "")
447                                 + "null, Component is "
448                                 + (this.inputSelector != null
449                                     && this.inputSelector.isSelectable(this.inputName)
450                                         ? "known"
451                                         : "unknown"));
452             }
453         } catch (Exception JavaDoc e) {
454             if (getLogger().isWarnEnabled())
455                 getLogger().warn(
456                     "A problem occurred setting up '" + this.inputName + "': " + e.getMessage());
457         }
458     }
459
460     /**
461      * Recycle this component.
462      */

463     public void recycle() {
464         reset();
465         super.recycle();
466     }
467
468     /**
469      * Generate string representation of attributes. For debug only.
470      */

471     protected String JavaDoc printAttributes(Attributes JavaDoc attr) {
472         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
473         sb.append('[');
474         for (int i = 0; i < attr.getLength(); i++) {
475             sb.append('@').append(attr.getLocalName(i)).append("='").append(
476                 attr.getValue(i)).append(
477                 "' ");
478         }
479         sb.append(']');
480         return sb.toString();
481     }
482
483     /**
484      * Handle input elements that may have a "checked" attributes,
485      * i.e. checkbox and radio.
486      */

487     protected void startCheckableElement(
488         String JavaDoc aName,
489         String JavaDoc uri,
490         String JavaDoc name,
491         String JavaDoc raw,
492         AttributesImpl JavaDoc attributes)
493         throws SAXException JavaDoc {
494
495         // @fixed and this.fixed already considered in startInputElement
496
this.values = this.getValues(aName);
497         String JavaDoc checked = attributes.getValue("checked");
498         String JavaDoc value = attributes.getValue("value");
499         boolean found = false;
500
501         if (getLogger().isDebugEnabled())
502             getLogger().debug(
503                 "startCheckableElement "
504                     + name
505                     + " attributes "
506                     + this.printAttributes(attributes));
507         if (this.values != null) {
508             if (getLogger().isDebugEnabled())
509                 getLogger().debug("replacing");
510             for (int i = 0; i < this.values.length; i++) {
511                 if (this.values[i].equals(value)) {
512                     found = true;
513                     if (checked == null) {
514                         attributes.addAttribute("", "checked", "checked", "CDATA", "");
515                     }
516                     break;
517                 }
518             }
519             if (!found && checked != null) {
520                 attributes.removeAttribute(attributes.getIndex("checked"));
521             }
522         }
523         this.relayStartElement(uri, name, raw, attributes);
524     }
525
526     /**
527      * Handle input elements that may don't have a "checked"
528      * attributes, e.g. text, password, button.
529      */

530     protected void startNonCheckableElement(
531         String JavaDoc aName,
532         String JavaDoc uri,
533         String JavaDoc name,
534         String JavaDoc raw,
535         AttributesImpl JavaDoc attributes)
536         throws SAXException JavaDoc {
537
538         // @fixed and this.fixed already considered in startInputElement
539
Object JavaDoc fValue = this.getNextValue(aName);
540         String JavaDoc value = attributes.getValue("value");
541         if (getLogger().isDebugEnabled())
542             getLogger().debug(
543                 "startNonCheckableElement "
544                     + name
545                     + " attributes "
546                     + this.printAttributes(attributes));
547         if (fValue != null) {
548             if (getLogger().isDebugEnabled())
549                 getLogger().debug("replacing");
550             if (value != null) {
551                 attributes.setValue(attributes.getIndex("value"), String.valueOf(fValue));
552             } else {
553                 attributes.addAttribute("", "value", "value", "CDATA", String.valueOf(fValue));
554             }
555         }
556         this.relayStartElement(uri, name, raw, attributes);
557     }
558
559     /**
560      * Handle input elements. Calls startCheckableElement or
561      * startNonCheckableElement.
562      */

563     protected void startInputElement(String JavaDoc uri, String JavaDoc name, String JavaDoc raw, Attributes JavaDoc attr)
564         throws SAXException JavaDoc {
565
566         // @value = request.getParameterValues(@name)
567
String JavaDoc aName = getName(attr.getValue("name"));
568         String JavaDoc fixed = attr.getValue(this.fixedName);
569
570         if (getLogger().isDebugEnabled())
571             getLogger().debug(
572                 "startInputElement " + name + " attributes " + this.printAttributes(attr));
573         if (aName == null || this.fixed || (fixed != null && BooleanUtils.toBoolean(fixed))) {
574             this.relayStartElement(uri, name, raw, attr);
575
576         } else {
577             if (getLogger().isDebugEnabled())
578                 getLogger().debug("replacing");
579
580             attr = this.normalizeAttributes(attr);
581
582             AttributesImpl JavaDoc attributes = null;
583             if (attr instanceof AttributesImpl JavaDoc) {
584                 attributes = (AttributesImpl JavaDoc) attr;
585             } else {
586                 attributes = new AttributesImpl JavaDoc(attr);
587             }
588             String JavaDoc type = attributes.getValue("type");
589             switch (((Integer JavaDoc) inputTypes.get(type, defaultType)).intValue()) {
590                 case TYPE_CHECKBOX :
591                 case TYPE_RADIO :
592                     this.startCheckableElement(aName, uri, name, raw, attributes);
593                     break;
594
595                 case TYPE_DEFAULT :
596                     this.startNonCheckableElement(aName, uri, name, raw, attributes);
597                     break;
598             }
599             this.values = null;
600         }
601     }
602
603     /**
604      * Handle select elements. Sets up some instance variables for
605      * following option elements.
606      */

607     protected void startSelectElement(String JavaDoc uri, String JavaDoc name, String JavaDoc raw, Attributes JavaDoc attr)
608         throws SAXException JavaDoc {
609
610         // this.values = request.getParameterValues(@name)
611
String JavaDoc aName = getName(attr.getValue("name"));
612         String JavaDoc fixed = attr.getValue(this.fixedName);
613         this.values = null;
614         if (getLogger().isDebugEnabled())
615             getLogger().debug(
616                 "startSelectElement " + name + " attributes " + this.printAttributes(attr));
617         if (aName != null && !(this.fixed || (fixed != null && BooleanUtils.toBoolean(fixed)))) {
618             if (attr.getIndex("multiple") > -1) {
619                 this.values = this.getValues(aName);
620             } else {
621                 Object JavaDoc val = this.getNextValue(aName);
622                 if (val != null) {
623                     this.values = new Object JavaDoc[1];
624                     this.values[0] = val;
625                 } else {
626                     this.values = null;
627                 }
628             }
629             attr = this.normalizeAttributes(attr);
630         }
631         this.relayStartElement(uri, name, raw, attr);
632     }
633
634     /**
635      * Handle option elements. Uses instance variables set up by
636      * startSelectElement. Relies on option having a "value"
637      * attribute, i.e. does not check following characters if "value"
638      * is not present.
639      */

640     protected void startOptionElement(String JavaDoc uri, String JavaDoc name, String JavaDoc raw, Attributes JavaDoc attr)
641         throws SAXException JavaDoc {
642
643         // add @selected if @value in request.getParameterValues(@name)
644
if (getLogger().isDebugEnabled())
645             getLogger().debug(
646                 "startOptionElement " + name + " attributes " + this.printAttributes(attr));
647         if (this.values == null || this.fixed) {
648             this.relayStartElement(uri, name, raw, attr);
649         } else {
650             if (getLogger().isDebugEnabled())
651                 getLogger().debug("replacing");
652             AttributesImpl JavaDoc attributes = null;
653             if (attr instanceof AttributesImpl JavaDoc) {
654                 attributes = (AttributesImpl JavaDoc) attr;
655             } else {
656                 attributes = new AttributesImpl JavaDoc(attr);
657             }
658             String JavaDoc selected = attributes.getValue("selected");
659             String JavaDoc value = attributes.getValue("value");
660             boolean found = false;
661
662             for (int i = 0; i < this.values.length; i++) {
663                 if (this.values[i].equals(value)) {
664                     found = true;
665                     if (selected == null) {
666                         attributes.addAttribute("", "selected", "selected", "CDATA", "");
667                     }
668                     break;
669                 }
670             }
671             if (!found && selected != null) {
672                 attributes.removeAttribute(attributes.getIndex("selected"));
673             }
674
675             this.relayStartElement(uri, name, raw, attributes);
676         }
677     }
678
679     /**
680      * Handles textarea elements. Skips nested events if request
681      * parameter with same name exists.
682      */

683     protected void startTextareaElement(String JavaDoc uri, String JavaDoc name, String JavaDoc raw, Attributes JavaDoc attributes)
684         throws SAXException JavaDoc {
685
686         String JavaDoc aName = getName(attributes.getValue("name"));
687         String JavaDoc fixed = attributes.getValue(this.fixedName);
688         Object JavaDoc value = null;
689         if (getLogger().isDebugEnabled())
690             getLogger().debug(
691                 "startTextareaElement " + name + " attributes " + this.printAttributes(attributes));
692         if (aName != null) {
693             value = this.getNextValue(aName);
694         }
695         if (value == null || this.fixed || (fixed != null && BooleanUtils.toBoolean(fixed))) {
696             this.relayStartElement(uri, name, raw, attributes);
697         } else {
698             if (getLogger().isDebugEnabled())
699                 getLogger().debug("replacing");
700             this.relayStartElement(uri, name, raw, this.normalizeAttributes(attributes));
701             String JavaDoc valString = String.valueOf(value);
702             this.characters(valString.toCharArray(), 0, valString.length());
703             // well, this doesn't really work out nicely. do it the hard way.
704
if (this.ignoreEventsCount == 0)
705                 this.skipChildrenOnly = true;
706             this.ignoreEventsCount++;
707         }
708     }
709
710     /**
711      * Handle error elements. If validation results are available,
712      * compares validation result for parameter with the same name as
713      * the "name" attribute with the result names is "when" and
714      * "when-ge". Drops element and all nested events when error
715      * condition is not met.
716      */

717     protected void startErrorElement(String JavaDoc uri, String JavaDoc name, String JavaDoc raw, Attributes JavaDoc attr)
718         throws SAXException JavaDoc {
719
720         if (getLogger().isDebugEnabled())
721             getLogger().debug(
722                 "startErrorElement " + name + " attributes " + this.printAttributes(attr));
723         if (this.ignoreValidation) {
724             this.relayStartElement(uri, name, raw, attr);
725         } else if (this.validationResults == null || this.fixed) {
726             this.relayStartElement(true, false, uri, name, raw, attr);
727         } else {
728             String JavaDoc aName = attr.getValue("name");
729             if (aName == null) {
730                 this.relayStartElement(uri, name, raw, attr);
731             } else {
732                 ValidatorActionResult validation =
733                     FormValidatorHelper.getParamResult(this.objectModel, aName);
734                 String JavaDoc when = attr.getValue("when");
735                 String JavaDoc when_ge = attr.getValue("when-ge");
736
737                 if ((when != null && when.equals(validatorResults.get(validation)))
738                     || (when_ge != null
739                         && validation.ge(
740                             (ValidatorActionResult) validatorResultLabel.get(
741                                 when_ge,
742                                 ValidatorActionResult.MAXERROR)))) {
743                     AttributesImpl JavaDoc attributes = null;
744                     if (attr instanceof AttributesImpl JavaDoc) {
745                         attributes = (AttributesImpl JavaDoc) attr;
746                     } else {
747                         attributes = new AttributesImpl JavaDoc(attr);
748                     }
749                     // remove attributes not meant for client
750
attributes.removeAttribute(attributes.getIndex("name"));
751                     if (when != null)
752                         attributes.removeAttribute(attributes.getIndex("when"));
753                     if (when_ge != null)
754                         attributes.removeAttribute(attributes.getIndex("when-ge"));
755                     this.relayStartElement(uri, name, raw, this.normalizeAttributes(attributes));
756                 } else {
757                     this.relayStartElement(true, true, uri, name, raw, attr);
758                 }
759             }
760         }
761     }
762
763     /**
764      * Start processing a form element. Sets protection indicator if attribute
765      * "fixed" is present and either "true" or "yes". Removes attribute "fixed"
766      * if present.
767      * @param uri The namespace of the element.
768      * @param name The local name of the element.
769      * @param raw The qualified name of the element.
770      * @param attr The attributes of the element.
771      */

772     protected void startFormElement(String JavaDoc uri, String JavaDoc name, String JavaDoc raw, Attributes JavaDoc attr)
773         throws SAXException JavaDoc {
774
775         String JavaDoc fixed = attr.getValue(this.fixedName);
776         if (this.useFormName) {
777             this.formName = attr.getValue("name");
778         }
779         if (fixed == null) {
780             this.relayStartElement(uri, name, raw, attr);
781         } else {
782             if (!this.fixed && BooleanUtils.toBoolean(fixed)) {
783                 this.fixed = true;
784             }
785             // remove attributes not meant for client
786
AttributesImpl JavaDoc attributes = null;
787             if (attr instanceof AttributesImpl JavaDoc) {
788                 attributes = (AttributesImpl JavaDoc) attr;
789             } else {
790                 attributes = new AttributesImpl JavaDoc(attr);
791             }
792             attributes.removeAttribute(attributes.getIndex(this.fixedName));
793             this.relayStartElement(uri, name, raw, this.normalizeAttributes(attributes));
794         }
795     }
796
797     /**
798      * Start recording repeat element contents and push repeat expression and
799      * variable to repeater stack. Only start recording, if no other recorder is
800      * currently running.
801      *
802      * @param uri
803      * @param name
804      * @param raw
805      * @param attr
806      * @throws SAXException
807      */

808     protected void startRepeatElement(String JavaDoc uri, String JavaDoc name, String JavaDoc raw, Attributes JavaDoc attr)
809         throws SAXException JavaDoc {
810
811         if (this.recordingCount == 0) {
812             if (!(this.fixed || BooleanUtils.toBoolean(attr.getValue(this.fixedName)))) {
813                 RepeaterStatus status =
814                     new RepeaterStatus("${" + attr.getValue("using") + "}", 0, attr.getValue("on"));
815                 this.repeater.add(status);
816                 this.startRecording();
817                 this.recordingCount++;
818             } else {
819                 this.relayStartElement(uri, name, raw, attr);
820             }
821         } else {
822             this.relayStartElement(uri, name, raw, attr);
823             this.recordingCount++;
824         }
825     }
826
827     /**
828      * Stop recording repeat contents and replay required number of times.
829      * Stop only if outmost repeat element is ending.
830      *
831      * @param uri
832      * @param name
833      * @param raw
834      * @throws SAXException
835      */

836     protected void endRepeatElement(String JavaDoc uri, String JavaDoc name, String JavaDoc raw) throws SAXException JavaDoc {
837         this.recordingCount--;
838         if (this.recordingCount == 0) {
839             DocumentFragment JavaDoc fragment = this.endRecording();
840             RepeaterStatus status = (RepeaterStatus) this.repeater.get(this.repeater.size() - 1);
841             Object JavaDoc[] vals = this.getValues(this.getName(status.expr));
842             int count = (vals != null ? vals.length : 0);
843             for (status.count = 1; status.count <= count; status.count++) {
844                 DOMStreamer streamer = new DOMStreamer(this, this);
845                 streamer.stream(fragment);
846             }
847             this.repeater.remove(this.repeater.size() - 1);
848         } else {
849             this.relayEndElement(uri, name, raw);
850             if (this.recordingCount < 0) {
851                 this.recordingCount = 0;
852             }
853         }
854     }
855
856     /**
857      * Start processing elements of our namespace.
858      * This hook is invoked for each sax event with our namespace.
859      * @param uri The namespace of the element.
860      * @param name The local name of the element.
861      * @param raw The qualified name of the element.
862      * @param attr The attributes of the element.
863      */

864     public void startTransformingElement(String JavaDoc uri, String JavaDoc name, String JavaDoc raw, Attributes JavaDoc attr)
865         throws SAXException JavaDoc {
866
867         if (this.ignoreEventsCount == 0 && this.recordingCount == 0) {
868             switch (((Integer JavaDoc) elementNames.get(name, defaultElement)).intValue()) {
869                 case ELEMENT_INPUT :
870                     this.startInputElement(uri, name, raw, attr);
871                     break;
872
873                 case ELEMENT_SELECT :
874                     this.startSelectElement(uri, name, raw, attr);
875                     break;
876
877                 case ELEMENT_OPTION :
878                     this.startOptionElement(uri, name, raw, attr);
879                     break;
880
881                 case ELEMENT_TXTAREA :
882                     this.startTextareaElement(uri, name, raw, attr);
883                     break;
884
885                 case ELEMENT_ERROR :
886                     this.startErrorElement(uri, name, raw, attr);
887                     break;
888
889                 case ELEMENT_FORM :
890                     this.startFormElement(uri, name, raw, attr);
891                     break;
892
893                 case ELEMENT_REPEAT :
894                     this.startRepeatElement(uri, name, raw, attr);
895                     break;
896
897                 default :
898                     this.relayStartElement(uri, name, raw, attr);
899             }
900
901         } else if (this.recordingCount > 0) {
902             switch (((Integer JavaDoc) elementNames.get(name, defaultElement)).intValue()) {
903                 case ELEMENT_REPEAT :
904                     this.startRepeatElement(uri, name, raw, attr);
905                     break;
906
907                 default :
908                     this.relayStartElement(uri, name, raw, attr);
909             }
910         } else {
911             this.relayStartElement(uri, name, raw, attr);
912         }
913     }
914
915     /**
916      * Start processing elements of our namespace.
917      * This hook is invoked for each sax event with our namespace.
918      * @param uri The namespace of the element.
919      * @param name The local name of the element.
920      * @param raw The qualified name of the element.
921      */

922     public void endTransformingElement(String JavaDoc uri, String JavaDoc name, String JavaDoc raw) throws SAXException JavaDoc {
923
924         if (this.ignoreEventsCount > 0) {
925             this.relayEndElement(uri, name, raw);
926         } else if (this.recordingCount > 0) {
927             switch (((Integer JavaDoc) elementNames.get(name, defaultElement)).intValue()) {
928                 case ELEMENT_REPEAT :
929                     this.endRepeatElement(uri, name, raw);
930                     break;
931
932                 default :
933                     this.relayEndElement(uri, name, raw);
934             }
935         } else {
936             switch (((Integer JavaDoc) elementNames.get(name, defaultElement)).intValue()) {
937                 case ELEMENT_SELECT :
938                     this.values = null;
939                     this.relayEndElement(uri, name, raw);
940                     break;
941                 case ELEMENT_INPUT :
942                 case ELEMENT_OPTION :
943                 case ELEMENT_TXTAREA :
944                 case ELEMENT_ERROR :
945                     this.relayEndElement(uri, name, raw);
946                     break;
947                 case ELEMENT_FORM :
948                     this.fixed = this.documentFixed;
949                     this.formName = null;
950                     this.relayEndElement(uri, name, raw);
951                     break;
952
953                 case ELEMENT_REPEAT :
954                     this.endRepeatElement(uri, name, raw);
955                     break;
956
957                 default :
958                     this.relayEndElement(uri, name, raw);
959             }
960         }
961     }
962
963     /**
964      * Remove extra information from element's attributes. Currently only removes
965      * the repeater variable from the element's name attribute if present.
966      *
967      * @param attr
968      * @return modified attributes
969      */

970     private Attributes JavaDoc normalizeAttributes(Attributes JavaDoc attr) {
971         Attributes JavaDoc result = attr;
972         if (this.stripNumber && this.repeater.size() > 0) {
973             String JavaDoc name = attr.getValue("name");
974             if (name != null) {
975                 for (Iterator JavaDoc i = this.repeater.iterator(); i.hasNext();) {
976                     RepeaterStatus status = (RepeaterStatus) i.next();
977                     int pos = name.indexOf(status.var);
978                     if (pos >= 0) {
979                         AttributesImpl JavaDoc attributes;
980                         if (result instanceof AttributesImpl JavaDoc) {
981                             attributes = (AttributesImpl JavaDoc) result;
982                         } else {
983                             attributes = new AttributesImpl JavaDoc(result);
984                         }
985                         name =
986                             name.substring(0, pos - this.decorationSize)
987                                 + name.substring(pos + status.var.length() + this.decorationSize);
988                         attributes.setValue(attributes.getIndex("name"), name);
989                         result = attributes;
990                     }
991                 }
992             }
993         }
994         return result;
995     }
996
997     /**
998      * Generate the "real" name of an element for value lookup.
999      * @param name
1000     * @return "real" name.
1001     */

1002    private String JavaDoc getName(String JavaDoc name) {
1003        String JavaDoc result = name;
1004        if (this.useFormName && this.formName != null) {
1005            if (this.separator != null) {
1006                if (this.useFormNameTwice) {
1007                    result =
1008                        this.formName + this.separator + this.formName + this.separator + result;
1009                } else {
1010                    result = this.formName + this.separator + result;
1011                }
1012            } else {
1013                if (this.useFormNameTwice) {
1014                    result = this.formName + result;
1015                } else {
1016                    // does this make sense ?
1017
result = this.formName + this.formName + result;
1018                }
1019            }
1020        }
1021        if (this.prefix != null) {
1022            result = this.prefix + result;
1023        }
1024        if (this.suffix != null) {
1025            result = result + this.prefix;
1026        }
1027        if (this.repeater.size() > 0) {
1028            for (Iterator JavaDoc i = this.repeater.iterator(); i.hasNext();) {
1029                RepeaterStatus status = (RepeaterStatus) i.next();
1030                int pos = result.indexOf(status.var);
1031                if (pos != -1) {
1032                    result =
1033                        result.substring(0, pos)
1034                            + status.count
1035                            + result.substring(pos + status.var.length());
1036                }
1037            }
1038        }
1039        return result;
1040    }
1041
1042    /**
1043     * Obtain values from used InputModule if not done already and return the
1044     * next value. If no more values exist, returns null.
1045     *
1046     * @param name
1047     * @return
1048     */

1049    private Object JavaDoc getNextValue(String JavaDoc name) {
1050        Object JavaDoc result = null;
1051        if (this.formValues.containsKey(name)) {
1052            ValueList vList = (ValueList) this.formValues.get(name);
1053            result = vList.getNext();
1054        } else {
1055            ValueList vList = new ValueList(this.getValues(name));
1056            result = vList.getNext();
1057            this.formValues.put(name, vList);
1058        }
1059        return result;
1060    }
1061
1062    /**
1063     * Obtain values from the used InputModule.
1064     */

1065    private Object JavaDoc[] getValues(String JavaDoc name) {
1066        Object JavaDoc[] values = null;
1067        ServiceSelector iputSelector = null;
1068        InputModule iput = null;
1069        try {
1070            if (this.input != null) {
1071                // input module is thread safe
1072
// thus we still have a reference to it
1073
values = input.getAttributeValues(name, this.inputConf, objectModel);
1074                if (getLogger().isDebugEnabled())
1075                    getLogger().debug("cached module " + this.input
1076                                      + " attribute " + name
1077                                      + " returns " + values);
1078            } else {
1079                // input was not thread safe
1080
// so acquire it again
1081
iputSelector = (ServiceSelector)this.manager.lookup(INPUT_MODULE_SELECTOR);
1082                if (this.inputName != null
1083                    && iputSelector != null
1084                    && iputSelector.isSelectable(this.inputName)) {
1085
1086                    iput = (InputModule) iputSelector.select(this.inputName);
1087                }
1088                if (iput != null) {
1089                    values = iput.getAttributeValues(name, this.inputConf, objectModel);
1090                }
1091                if (getLogger().isDebugEnabled())
1092                    getLogger().debug(
1093                        "fresh module " + iput + " attribute " + name + " returns " + values);
1094            }
1095        } catch (Exception JavaDoc e) {
1096            if (getLogger().isWarnEnabled())
1097                getLogger().warn(
1098                    "A problem occurred acquiring a value from '"
1099                        + this.inputName
1100                        + "' for '"
1101                        + name
1102                        + "': "
1103                        + e.getMessage());
1104        } finally {
1105            // release components if necessary
1106
if (iputSelector != null) {
1107                if (iput != null)
1108                    iputSelector.release(iput);
1109                this.manager.release(iputSelector);
1110            }
1111        }
1112
1113        return values;
1114    }
1115
1116    /**
1117     * Calls the super's method startTransformingElement.
1118     *
1119     * @param uri
1120     * @param name
1121     * @param raw
1122     * @param attr
1123     * @throws SAXException
1124     */

1125    protected void relayStartElement(String JavaDoc uri, String JavaDoc name, String JavaDoc raw, Attributes JavaDoc attr)
1126        throws SAXException JavaDoc {
1127        this.relayStartElement(false, false, uri, name, raw, attr);
1128    }
1129
1130    /**
1131     * Calls the super's method startTransformingElement and increments the
1132     * ignoreEventsCount if skip is true. Increment can be done either before
1133     * invoking super's method, so that the element itself is skipped, or afterwards,
1134     * so that only the children are skipped.
1135     *
1136     * @param skip
1137     * @param skipChildrenOnly
1138     * @param uri
1139     * @param name
1140     * @param raw
1141     * @param attr
1142     * @throws SAXException
1143     */

1144    protected void relayStartElement(
1145        boolean skip,
1146        boolean skipChildrenOnly,
1147        String JavaDoc uri,
1148        String JavaDoc name,
1149        String JavaDoc raw,
1150        Attributes JavaDoc attr)
1151        throws SAXException JavaDoc {
1152
1153        try {
1154            if (this.ignoreEventsCount > 0) {
1155                this.ignoreEventsCount++;
1156                super.startTransformingElement(uri, name, raw, attr);
1157            } else {
1158                if (skip)
1159                    this.skipChildrenOnly = skipChildrenOnly;
1160                if (skip && !skipChildrenOnly)
1161                    this.ignoreEventsCount++;
1162                super.startTransformingElement(uri, name, raw, attr);
1163                if (skip && skipChildrenOnly)
1164                    this.ignoreEventsCount++;
1165            }
1166        } catch (ProcessingException e) {
1167            throw new SAXException JavaDoc(e);
1168        } catch (IOException JavaDoc e) {
1169            throw new SAXException JavaDoc(e);
1170        }
1171    }
1172
1173    /**
1174     * Calls the super's method endTransformingElement and decrements the
1175     * ignoreEventsCount if larger than zero.
1176     *
1177     * @param uri
1178     * @param name
1179     * @param raw
1180     * @throws SAXException
1181     */

1182    protected void relayEndElement(String JavaDoc uri, String JavaDoc name, String JavaDoc raw) throws SAXException JavaDoc {
1183
1184        if (this.ignoreEventsCount == 1 && this.skipChildrenOnly)
1185            this.ignoreEventsCount--;
1186        try {
1187            super.endTransformingElement(uri, name, raw);
1188        } catch (ProcessingException e) {
1189            throw new SAXException JavaDoc(e);
1190        } catch (IOException JavaDoc e) {
1191            throw new SAXException JavaDoc(e);
1192        } catch (Exception JavaDoc e) {
1193            getLogger().error("exception", e);
1194        }
1195
1196        if (this.ignoreEventsCount > 0)
1197            this.ignoreEventsCount--;
1198    }
1199
1200}
1201
Popular Tags