KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > forms > transformation > EffectWidgetReplacingPipe


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.forms.transformation;
17
18 import org.apache.avalon.excalibur.pool.Recyclable;
19
20 import org.apache.cocoon.forms.FormsConstants;
21 import org.apache.cocoon.forms.formmodel.AggregateField;
22 import org.apache.cocoon.forms.formmodel.DataWidget;
23 import org.apache.cocoon.forms.formmodel.Group;
24 import org.apache.cocoon.forms.formmodel.Repeater;
25 import org.apache.cocoon.forms.formmodel.Struct;
26 import org.apache.cocoon.forms.formmodel.Union;
27 import org.apache.cocoon.forms.formmodel.Widget;
28 import org.apache.cocoon.forms.validation.ValidationError;
29 import org.apache.cocoon.forms.validation.ValidationErrorAware;
30 import org.apache.cocoon.i18n.I18nUtils;
31 import org.apache.cocoon.xml.AbstractXMLPipe;
32 import org.apache.cocoon.xml.SaxBuffer;
33 import org.apache.cocoon.xml.XMLUtils;
34
35 import org.apache.commons.jxpath.JXPathException;
36 import org.xml.sax.Attributes JavaDoc;
37 import org.xml.sax.ContentHandler JavaDoc;
38 import org.xml.sax.SAXException JavaDoc;
39 import org.xml.sax.ext.LexicalHandler JavaDoc;
40 import org.xml.sax.helpers.AttributesImpl JavaDoc;
41
42 import java.util.ArrayList JavaDoc;
43 import java.util.HashMap JavaDoc;
44 import java.util.LinkedList JavaDoc;
45 import java.util.List JavaDoc;
46 import java.util.Locale JavaDoc;
47 import java.util.Map JavaDoc;
48
49 /**
50  * The basic operation of this Pipe is that it replaces <code>ft:widget</code>
51  * (in the {@link FormsConstants#TEMPLATE_NS} namespace) tags (having an id attribute)
52  * by the XML representation of the corresponding widget instance.
53  *
54  * <p>These XML fragments (normally all in the {@link FormsConstants#INSTANCE_NS "CForms Instance"}
55  * namespace), can then be translated to a HTML presentation by an XSLT.
56  * This XSLT will then only have to style individual widget, and will not
57  * need to do the whole page layout.
58  *
59  * <p>For more information about the supported tags and their function,
60  * see the user documentation for the forms template transformer.</p>
61  *
62  * @version $Id: EffectWidgetReplacingPipe.java 328136 2005-10-24 19:11:48Z sylvain $
63  */

64 public class EffectWidgetReplacingPipe extends EffectPipe {
65
66     /**
67      * Form location attribute on <code>ft:form-template</code> element, containing
68      * JXPath expression which should result in Form object.
69      *
70      * @see FormsPipelineConfig#findForm(String)
71      */

72     private static final String JavaDoc LOCATION = "location";
73
74     private static final String JavaDoc AGGREGATE_WIDGET = "aggregate-widget";
75     private static final String JavaDoc CHOOSE = "choose";
76     private static final String JavaDoc CLASS = "class";
77     private static final String JavaDoc CONTINUATION_ID = "continuation-id";
78     private static final String JavaDoc FORM_TEMPLATE_EL = "form-template";
79     private static final String JavaDoc GROUP = "group";
80     private static final String JavaDoc NEW = "new";
81     private static final String JavaDoc REPEATER = "repeater";
82     private static final String JavaDoc REPEATER_ROWS = "repeater-rows";
83     private static final String JavaDoc REPEATER_SIZE = "repeater-size";
84     private static final String JavaDoc REPEATER_WIDGET = "repeater-widget";
85     private static final String JavaDoc REPEATER_WIDGET_LABEL = "repeater-widget-label";
86     private static final String JavaDoc STRUCT = "struct";
87     private static final String JavaDoc STYLING_EL = "styling";
88     private static final String JavaDoc UNION = "union";
89     private static final String JavaDoc VALIDATION_ERROR = "validation-error";
90     private static final String JavaDoc WIDGET = "widget";
91     private static final String JavaDoc WIDGET_LABEL = "widget-label";
92
93     private final AggregateWidgetHandler hAggregate = new AggregateWidgetHandler();
94     private final ChooseHandler hChoose = new ChooseHandler();
95     private final ChoosePassThruHandler hChoosePassThru = new ChoosePassThruHandler();
96     private final ClassHandler hClass = new ClassHandler();
97     private final ContinuationIdHandler hContinuationId = new ContinuationIdHandler();
98     private final DocHandler hDocument = new DocHandler();
99     private final FormHandler hForm = new FormHandler();
100     private final GroupHandler hGroup = new GroupHandler();
101     private final NestedHandler hNested = new NestedHandler();
102     private final NewHandler hNew = new NewHandler();
103     private final RepeaterSizeHandler hRepeaterSize = new RepeaterSizeHandler();
104     private final RepeaterHandler hRepeater = new RepeaterHandler();
105     private final RepeaterRowsHandler hRepeaterRows = new RepeaterRowsHandler();
106     private final RepeaterWidgetHandler hRepeaterWidget = new RepeaterWidgetHandler();
107     private final RepeaterWidgetLabelHandler hRepeaterWidgetLabel = new RepeaterWidgetLabelHandler();
108     private final SkipHandler hSkip = new SkipHandler();
109     private final StructHandler hStruct = new StructHandler();
110     private final StylingContentHandler hStyling = new StylingContentHandler();
111     private final UnionHandler hUnion = new UnionHandler();
112     private final UnionPassThruHandler hUnionPassThru = new UnionPassThruHandler();
113     private final ValidationErrorHandler hValidationError = new ValidationErrorHandler();
114     private final WidgetHandler hWidget = new WidgetHandler();
115     private final WidgetLabelHandler hWidgetLabel = new WidgetLabelHandler();
116
117     /**
118      * Map containing all handlers
119      */

120     protected final Map JavaDoc templates;
121
122     protected FormsPipelineConfig pipeContext;
123
124     /**
125      * The namespaces and their prefixes
126      */

127     private final List JavaDoc namespaces;
128
129     /**
130      * True if instance namespace has been mapped to the
131      * 'fi' prefix.
132      */

133     private boolean hasInstanceNamespace;
134
135     protected Widget contextWidget;
136     protected LinkedList JavaDoc contextWidgets;
137     protected LinkedList JavaDoc chooseWidgets;
138     protected Widget widget;
139     protected Map JavaDoc classes;
140
141
142     public EffectWidgetReplacingPipe() {
143         namespaces = new ArrayList JavaDoc(5);
144         // Setup map of templates.
145
templates = new HashMap JavaDoc();
146         templates.put(AGGREGATE_WIDGET, hAggregate);
147         templates.put(CHOOSE, hChoose);
148         templates.put(CLASS, hClass);
149         templates.put(CONTINUATION_ID, hContinuationId);
150         templates.put(GROUP, hGroup);
151         templates.put(NEW, hNew);
152         templates.put(REPEATER, hRepeater);
153         templates.put(REPEATER_ROWS, hRepeaterRows);
154         templates.put(REPEATER_SIZE, hRepeaterSize);
155         templates.put(REPEATER_WIDGET, hRepeaterWidget);
156         templates.put(REPEATER_WIDGET_LABEL, hRepeaterWidgetLabel);
157         templates.put(STRUCT, hStruct);
158         templates.put(UNION, hUnion);
159         templates.put(VALIDATION_ERROR, hValidationError);
160         templates.put(WIDGET, hWidget);
161         templates.put(WIDGET_LABEL, hWidgetLabel);
162     }
163
164     public void init(Widget contextWidget, FormsPipelineConfig pipeContext) {
165         // Document handler is top level handler
166
super.init(hDocument);
167         this.pipeContext = pipeContext;
168
169         // Initialize widget related variables
170
this.contextWidgets = new LinkedList JavaDoc();
171         this.chooseWidgets = new LinkedList JavaDoc();
172         this.classes = new HashMap JavaDoc();
173     }
174
175     public void recycle() {
176         super.recycle();
177         this.contextWidget = null;
178         this.widget = null;
179         this.pipeContext = null;
180         this.namespaces.clear();
181         this.hasInstanceNamespace = false;
182     }
183
184     /**
185      * Get value of the required attribute
186      */

187     protected String JavaDoc getAttributeValue(String JavaDoc loc, Attributes JavaDoc attrs, String JavaDoc name) throws SAXException JavaDoc {
188         String JavaDoc value = attrs.getValue(name);
189         if (value == null) {
190             throw new SAXException JavaDoc("Element '" + loc + "' missing required '" + name + "' attribute, " +
191                                    "at " + getLocation());
192         }
193         return value;
194     }
195
196     /**
197      * Get non-empty value of the required attribute
198      */

199     protected String JavaDoc getRequiredAttributeValue(String JavaDoc loc, Attributes JavaDoc attrs, String JavaDoc name) throws SAXException JavaDoc {
200         String JavaDoc value = attrs.getValue(name);
201         if (value == null || value.equals("")) {
202             throw new SAXException JavaDoc("Element '" + loc + "' missing required '" + name + "' attribute, " +
203                                    "at " + getLocation());
204         }
205         return value;
206     }
207
208     /**
209      * Set the widget by the id attribute
210      */

211     protected void setWidget(String JavaDoc loc, Attributes JavaDoc attrs) throws SAXException JavaDoc {
212         setWidget(loc, getRequiredAttributeValue(loc, attrs, "id"));
213     }
214
215     /**
216      * Set the widget by its path
217      */

218     protected void setWidget(String JavaDoc loc, String JavaDoc path) throws SAXException JavaDoc {
219         widget = contextWidget.lookupWidget(path);
220         if (widget == null) {
221             if (contextWidget.getRequestParameterName().equals("")) {
222                 throw new SAXException JavaDoc("Element '" + loc + "' refers to unexistent widget path '" + path + "', " +
223                                        "relative to the form container, at " + getLocation());
224             } else {
225                 throw new SAXException JavaDoc("Element '" + loc + "' refers to unexistent widget path '" + path + "', " +
226                                        "relative to the '" + contextWidget.getRequestParameterName() + "', " +
227                                        "at " + getLocation());
228             }
229         }
230     }
231
232     /**
233      * Set typed widget by the id attribute
234      */

235     protected void setTypedWidget(String JavaDoc loc, Attributes JavaDoc attrs, Class JavaDoc wclass, String JavaDoc wname) throws SAXException JavaDoc {
236         setWidget(loc, attrs);
237         if (!wclass.isInstance(widget)) {
238             throw new SAXException JavaDoc("Element '" + loc + "' can only be used with " + wname + " widgets, " +
239                                    "at " + getLocation());
240         }
241     }
242
243     protected boolean isVisible(Widget widget) {
244         return widget.getCombinedState().isDisplayingValues();
245     }
246
247     /**
248      * Needed to get things working with JDK 1.3. Can be removed once we
249      * don't support that platform any more.
250      */

251     private ContentHandler JavaDoc getContentHandler() {
252         return this.contentHandler;
253     }
254
255     /**
256      * Needed to get things working with JDK 1.3. Can be removed once we
257      * don't support that platform any more.
258      */

259     private LexicalHandler JavaDoc getLexicalHandler() {
260         return this.lexicalHandler;
261     }
262
263
264     /**
265      * Process the SAX event.
266      * @see org.xml.sax.ContentHandler#startPrefixMapping
267      */

268     public void startPrefixMapping(String JavaDoc prefix, String JavaDoc uri)
269     throws SAXException JavaDoc {
270         if (prefix != null) {
271             this.namespaces.add(new String JavaDoc[] {prefix, uri});
272         }
273
274         // Consume template namespace mapping
275
if (!FormsConstants.TEMPLATE_NS.equals(uri)) {
276             super.startPrefixMapping(prefix, uri);
277         }
278     }
279
280     /**
281      * Process the SAX event.
282      * @see org.xml.sax.ContentHandler#endPrefixMapping
283      */

284     public void endPrefixMapping(String JavaDoc prefix)
285     throws SAXException JavaDoc {
286         String JavaDoc uri = null;
287
288         if (prefix != null) {
289             // Find and remove the namespace prefix
290
boolean found = false;
291             for (int i = this.namespaces.size() - 1; i >= 0; i--) {
292                 final String JavaDoc[] prefixAndUri = (String JavaDoc[]) this.namespaces.get(i);
293                 if (prefixAndUri[0].equals(prefix)) {
294                     uri = prefixAndUri[1];
295                     this.namespaces.remove(i);
296                     found = true;
297                     break;
298                 }
299             }
300             if (!found) {
301                 throw new SAXException JavaDoc("Namespace for prefix '" + prefix + "' not found.");
302             }
303         }
304
305         // Consume template namespace mapping
306
if (!FormsConstants.TEMPLATE_NS.equals(uri)) {
307             super.endPrefixMapping(prefix);
308         }
309     }
310
311     /**
312      * @return True if prefix is already mapped into the namespace
313      */

314     protected boolean hasPrefixMapping(String JavaDoc uri, String JavaDoc prefix) {
315         final int l = this.namespaces.size();
316         for (int i = 0; i < l; i++) {
317             String JavaDoc[] prefixAndUri = (String JavaDoc[]) this.namespaces.get(i);
318             if (prefixAndUri[0].equals(prefix) && prefixAndUri[1].equals(uri)) {
319                 return true;
320             }
321         }
322
323         return false;
324     }
325
326     //
327
// Handler classes to transform CForms template elements
328
//
329

330     protected class NestedHandler extends CopyHandler {
331         public Handler JavaDoc nestedElement(String JavaDoc uri, String JavaDoc loc, String JavaDoc raw, Attributes JavaDoc attrs)
332         throws SAXException JavaDoc {
333             // Is it forms namespace?
334
if (!FormsConstants.TEMPLATE_NS.equals(uri)) {
335                 return hNested;
336             }
337
338             Handler JavaDoc handler = (Handler JavaDoc) templates.get(loc);
339             if (handler == null) {
340                 throw new SAXException JavaDoc("Element '" + loc + "' was not recognized, " +
341                                        "at " + getLocation());
342             }
343
344             return handler;
345         }
346     }
347
348     /**
349      * Top level handler for the forms template
350      */

351     protected class DocHandler extends CopyHandler {
352         public Handler JavaDoc nestedElement(String JavaDoc uri, String JavaDoc loc, String JavaDoc raw, Attributes JavaDoc attrs)
353         throws SAXException JavaDoc {
354             if (FormsConstants.TEMPLATE_NS.equals(uri)) {
355                 if (!FORM_TEMPLATE_EL.equals(loc)) {
356                     throw new SAXException JavaDoc("Element '" + loc + "' is not permitted outside of " +
357                                            "'form-template', at " + getLocation());
358                 }
359
360                 return hForm;
361             }
362
363             return super.nestedElement(uri, loc, raw, attrs);
364         }
365     }
366
367     /**
368      * <code>ft:form-template</code> element handler.
369      * <pre>
370      * &lt;ft:form-template locale="..." location="..."&gt;
371      * ...
372      * &lt;/ft:form-template&gt;
373      * </pre>
374      */

375     protected class FormHandler extends NestedHandler {
376         public Handler JavaDoc startElement(String JavaDoc uri, String JavaDoc loc, String JavaDoc raw, Attributes JavaDoc attrs)
377         throws SAXException JavaDoc {
378             if (contextWidget != null) {
379                 throw new SAXException JavaDoc("Element 'form-template' can not be nested, " +
380                                        "at " + getLocation());
381             }
382
383             AttributesImpl JavaDoc newAttrs = attrs == null || attrs.getLength() == 0?
384                     new AttributesImpl JavaDoc():
385                     new AttributesImpl JavaDoc(attrs);
386
387             // ====> Retrieve the form
388
String JavaDoc formLocation = attrs.getValue(LOCATION);
389             if (formLocation != null) {
390                 // Remove the location attribute
391
newAttrs.removeAttribute(newAttrs.getIndex(LOCATION));
392             }
393             contextWidget = pipeContext.findForm(formLocation);
394
395             // ====> Check if form visible (and skip it if it's not)
396
if (!isVisible(contextWidget)) {
397                 return hNull;
398             }
399
400             // ====> Determine the Locale
401
// TODO pull this locale stuff also up in the Config object?
402
String JavaDoc localeAttr = attrs.getValue("locale");
403             if (localeAttr != null) { // first use value of locale attribute if any
404
localeAttr = pipeContext.translateText(localeAttr);
405                 pipeContext.setLocale(I18nUtils.parseLocale(localeAttr));
406             } else if (pipeContext.getLocaleParameter() != null) { // then use locale specified as transformer parameter, if any
407
pipeContext.setLocale(pipeContext.getLocaleParameter());
408             } else {
409                 // use locale specified in bizdata supplied for form
410
Object JavaDoc locale = null;
411                 try {
412                     locale = pipeContext.evaluateExpression("/locale");
413                 } catch (JXPathException e) {}
414                 if (locale != null) {
415                     pipeContext.setLocale((Locale JavaDoc)locale);
416                 } else {
417                     // final solution: use locale defined in the server machine
418
pipeContext.setLocale(Locale.getDefault());
419                 }
420             }
421
422             // We need to merge input.attrs with possible overruling attributes
423
// from the pipeContext
424
pipeContext.addFormAttributes(newAttrs);
425             String JavaDoc[] namesToTranslate = {"action"};
426             Attributes JavaDoc transAttrs = null;
427             try {
428                 transAttrs = translateAttributes(newAttrs, namesToTranslate);
429             } catch (RuntimeException JavaDoc e) {
430                 throw new SAXException JavaDoc( e.getMessage() + " " +getLocation());
431             }
432             
433             hasInstanceNamespace = hasPrefixMapping(FormsConstants.INSTANCE_NS, FormsConstants.INSTANCE_PREFIX);
434             if (!hasInstanceNamespace) {
435                 getContentHandler().startPrefixMapping(FormsConstants.INSTANCE_PREFIX, FormsConstants.INSTANCE_NS);
436             }
437             getContentHandler().startElement(FormsConstants.INSTANCE_NS, "form-template", FormsConstants.INSTANCE_PREFIX_COLON + "form-template", transAttrs);
438             return this;
439         }
440
441         public void endElement(String JavaDoc uri, String JavaDoc loc, String JavaDoc raw)
442         throws SAXException JavaDoc {
443             getContentHandler().endElement(FormsConstants.INSTANCE_NS, "form-template", FormsConstants.INSTANCE_PREFIX_COLON + "form-template");
444             if (!hasInstanceNamespace) {
445                 getContentHandler().endPrefixMapping(FormsConstants.INSTANCE_PREFIX);
446             }
447             contextWidget = null;
448         }
449     }
450
451     /**
452      * <code>ft:choose</code>, <code>ft:union</code> use this.
453      */

454     protected class SkipHandler extends NestedHandler {
455         public Handler JavaDoc startElement(String JavaDoc uri, String JavaDoc loc, String JavaDoc raw, Attributes JavaDoc attrs)
456         throws SAXException JavaDoc {
457             return this;
458         }
459
460         public void endElement(String JavaDoc uri, String JavaDoc loc, String JavaDoc raw)
461         throws SAXException JavaDoc {
462         }
463     }
464
465     //
466
// Widget Handlers
467
//
468

469     /**
470      * Handles <code>ft:widget-label</code> element.
471      */

472     protected class WidgetLabelHandler extends ErrorHandler {
473         public Handler JavaDoc startElement(String JavaDoc uri, String JavaDoc loc, String JavaDoc raw, Attributes JavaDoc attrs)
474         throws SAXException JavaDoc {
475             setWidget(loc, attrs);
476             widget.generateLabel(getContentHandler());
477             return this;
478         }
479
480         public void endElement(String JavaDoc uri, String JavaDoc loc, String JavaDoc raw)
481         throws SAXException JavaDoc {
482         }
483     }
484
485     /**
486      * Handles <code>ft:widget</code> element.
487      */

488     protected class WidgetHandler extends NullHandler {
489         // Widgets can't be nested, so this variable is Ok
490
private boolean hasStyling;
491
492         public Handler JavaDoc startElement(String JavaDoc uri, String JavaDoc loc, String JavaDoc raw, Attributes JavaDoc attrs)
493         throws SAXException JavaDoc {
494             setWidget(loc, attrs);
495             if (!isVisible(widget)) {
496                 return hNull;
497             }
498
499             hasStyling = false;
500             return this;
501         }
502
503         public Handler JavaDoc nestedElement(String JavaDoc uri, String JavaDoc loc, String JavaDoc raw, Attributes JavaDoc attrs)
504         throws SAXException JavaDoc {
505             if (FormsConstants.INSTANCE_NS.equals(uri)) {
506                 if (!STYLING_EL.equals(loc)) {
507                     throw new SAXException JavaDoc("Element '" + loc + "' is not permitted within 'widget', " +
508                                            "at " + getLocation());
509                 }
510                 hasStyling = true;
511                 beginBuffer();
512                 // Buffer styling elements
513
return hBuffer;
514             }
515             return hNull;
516         }
517
518         public void endElement(String JavaDoc uri, String JavaDoc loc, String JavaDoc raw)
519         throws SAXException JavaDoc {
520             if (hasStyling) {
521                 // Pipe widget XML through the special handler to insert styling element
522
// before fi:widget end element.
523
hasStyling = false;
524                 hStyling.recycle();
525                 hStyling.setSaxFragment(endBuffer());
526                 hStyling.setContentHandler(getContentHandler());
527                 hStyling.setLexicalHandler(getLexicalHandler());
528                 widget.generateSaxFragment(hStyling, pipeContext.getLocale());
529             } else {
530                 // Pipe widget XML directly into the output handler
531
widget.generateSaxFragment(getContentHandler(), pipeContext.getLocale());
532             }
533             widget = null;
534         }
535     }
536
537     //
538
// Repeater Handlers
539
//
540

541     /**
542      * Handles <code>ft:repeater-size</code> element.
543      */

544     protected class RepeaterSizeHandler extends ErrorHandler {
545         public Handler JavaDoc startElement(String JavaDoc uri, String JavaDoc loc, String JavaDoc raw, Attributes JavaDoc attrs)
546         throws SAXException JavaDoc {
547             setTypedWidget(loc, attrs, Repeater.class, "repeater");
548             ((Repeater) widget).generateSize(getContentHandler());
549             widget = null;
550             return this;
551         }
552
553         public void endElement(String JavaDoc uri, String JavaDoc loc, String JavaDoc raw)
554         throws SAXException JavaDoc {
555         }
556     }
557
558     /**
559      * Handles <code>ft:repeater-widget-label</code> element.
560      */

561     protected class RepeaterWidgetLabelHandler extends ErrorHandler {
562         public Handler JavaDoc startElement(String JavaDoc uri, String JavaDoc loc, String JavaDoc raw, Attributes JavaDoc attrs)
563         throws SAXException JavaDoc {
564             Repeater repeater;
565             if (contextWidget instanceof Repeater) {
566                 repeater = (Repeater)contextWidget;
567             } else {
568                 setTypedWidget(loc, attrs, Repeater.class, "repeater");
569                 repeater = (Repeater)widget;
570                 widget = null;
571             }
572             String JavaDoc path = getRequiredAttributeValue(loc, attrs, "widget-id");
573             repeater.generateWidgetLabel(path, getContentHandler());
574             return this;
575         }
576
577         public void endElement(String JavaDoc uri, String JavaDoc loc, String JavaDoc raw)
578         throws SAXException JavaDoc {
579         }
580     }
581
582     /**
583      * Handles <code>ft:repeater</code> element. Should contain repeater-rows
584      */

585     protected class RepeaterHandler extends NestedHandler {
586         protected Class JavaDoc getWidgetClass() {
587             return Repeater.class;
588         }
589
590         protected String JavaDoc getWidgetName() {
591             return "repeater";
592         }
593
594         public Handler JavaDoc startElement(String JavaDoc uri, String JavaDoc loc, String JavaDoc raw, Attributes JavaDoc attrs)
595         throws SAXException JavaDoc {
596             setTypedWidget(loc, attrs, getWidgetClass(), getWidgetName());
597             if (!isVisible(widget)) {
598                 return hNull;
599             }
600
601             contextWidgets.addFirst(contextWidget);
602             contextWidget = widget;
603             return this;
604         }
605
606         public void endElement(String JavaDoc uri, String JavaDoc loc, String JavaDoc raw)
607         throws SAXException JavaDoc {
608             contextWidget = (Widget) contextWidgets.removeFirst();
609         }
610     }
611
612     /**
613      * Handles <code>ft:repeater-rows</code> element.
614      */

615     protected class RepeaterRowsHandler extends BufferHandler {
616         public Handler JavaDoc startElement(String JavaDoc uri, String JavaDoc loc, String JavaDoc raw, Attributes JavaDoc attrs)
617         throws SAXException JavaDoc {
618             if (!(contextWidget instanceof Repeater)) {
619                 throw new SAXException JavaDoc("<repeater-rows> cannot be used with " + contextWidget + ", at " + getLocation());
620             }
621             beginBuffer();
622             return this;
623         }
624
625         public Handler JavaDoc nestedElement(String JavaDoc uri, String JavaDoc loc, String JavaDoc raw, Attributes JavaDoc attrs)
626         throws SAXException JavaDoc {
627             return hBuffer;
628         }
629
630         public void endElement(String JavaDoc uri, String JavaDoc loc, String JavaDoc raw)
631         throws SAXException JavaDoc {
632             SaxBuffer buffer = endBuffer();
633             final