001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.woody.transformation;
018:
019: import org.apache.avalon.excalibur.pool.Recyclable;
020: import org.apache.cocoon.i18n.I18nUtils;
021: import org.apache.cocoon.woody.Constants;
022: import org.apache.cocoon.woody.formmodel.AggregateField;
023: import org.apache.cocoon.woody.formmodel.Repeater;
024: import org.apache.cocoon.woody.formmodel.Struct;
025: import org.apache.cocoon.woody.formmodel.Union;
026: import org.apache.cocoon.woody.formmodel.Widget;
027: import org.apache.cocoon.woody.validation.ValidationError;
028: import org.apache.cocoon.woody.validation.ValidationErrorAware;
029: import org.apache.cocoon.xml.AbstractXMLPipe;
030: import org.apache.cocoon.xml.SaxBuffer;
031: import org.apache.commons.jxpath.JXPathException;
032:
033: import org.xml.sax.Attributes;
034: import org.xml.sax.ContentHandler;
035: import org.xml.sax.SAXException;
036: import org.xml.sax.ext.LexicalHandler;
037: import org.xml.sax.helpers.AttributesImpl;
038:
039: import java.util.HashMap;
040: import java.util.LinkedList;
041: import java.util.Locale;
042: import java.util.Map;
043:
044: // TODO: Reduce the Element creation and deletion churn by using startElement
045: // and endElement methods which do not create or use Elements on the stack.
046: // The corresponding TODO in the EffectPipe needs to be completed first.
047:
048: /**
049: * The basic operation of this Pipe is that it replaces wt:widget (in the
050: * {@link Constants#WT_NS} namespace) tags (having an id attribute)
051: * by the XML representation of the corresponding widget instance.
052: *
053: * <p>These XML fragments (normally all in the {@link Constants#WI_NS "Woody Instance"} namespace), can
054: * then be translated to a HTML presentation by an XSL. This XSL will then only have to style
055: * individual widget, and will not need to do the whole page layout.
056: *
057: * <p>For more information about the supported tags and their function, see the user documentation
058: * for the woody template transformer.</p>
059: *
060: * @author Timothy Larson
061: * @version CVS $Id: EffectWidgetReplacingPipe.java 433543 2006-08-22 06:22:54Z crossley $
062: */
063: public class EffectWidgetReplacingPipe extends EffectPipe {
064:
065: /**
066: * Form location attribute on <code>wt:form-template</code> element, containing
067: * JXPath expression which should result in Form object.
068: *
069: * @see WoodyPipelineConfig#findForm(String)
070: */
071: private static final String LOCATION = "location";
072:
073: private static final String CLASS = "class";
074: private static final String CONTINUATION_ID = "continuation-id";
075: private static final String FORM_TEMPLATE_EL = "form-template";
076: private static final String NEW = "new";
077: private static final String REPEATER_SIZE = "repeater-size";
078: private static final String REPEATER_WIDGET = "repeater-widget";
079: private static final String REPEATER_WIDGET_LABEL = "repeater-widget-label";
080: private static final String AGGREGATE_WIDGET = "aggregate-widget";
081: private static final String STRUCT = "struct";
082: private static final String STYLING_EL = "styling";
083: private static final String UNION = "union";
084: private static final String VALIDATION_ERROR = "validation-error";
085: private static final String WIDGET_LABEL = "widget-label";
086: private static final String WIDGET = "widget";
087:
088: protected Widget contextWidget;
089: protected LinkedList contextWidgets;
090: protected String widgetId;
091: protected Widget widget;
092: protected Map classes;
093:
094: private final DocHandler docHandler = new DocHandler();
095: private final FormHandler formHandler = new FormHandler();
096: private final NestedHandler nestedHandler = new NestedHandler();
097: private final WidgetLabelHandler widgetLabelHandler = new WidgetLabelHandler();
098: private final WidgetHandler widgetHandler = new WidgetHandler();
099: private final RepeaterSizeHandler repeaterSizeHandler = new RepeaterSizeHandler();
100: private final RepeaterWidgetLabelHandler repeaterWidgetLabelHandler = new RepeaterWidgetLabelHandler();
101: private final RepeaterWidgetHandler repeaterWidgetHandler = new RepeaterWidgetHandler();
102: private final AggregateWidgetHandler aggregateWidgetHandler = new AggregateWidgetHandler();
103: private final StructHandler structHandler = new StructHandler();
104: private final UnionHandler unionHandler = new UnionHandler();
105: private final UnionPassThruHandler unionPassThruHandler = new UnionPassThruHandler();
106: private final NewHandler newHandler = new NewHandler();
107: private final ClassHandler classHandler = new ClassHandler();
108: private final ContinuationIdHandler continuationIdHandler = new ContinuationIdHandler();
109: private final StylingContentHandler stylingHandler = new StylingContentHandler();
110: private final ValidationErrorHandler validationErrorHandler = new ValidationErrorHandler();
111:
112: /**
113: * Map containing all handlers
114: */
115: private final Map templates = new HashMap(12, 1);
116:
117: protected WoodyPipelineConfig pipeContext;
118:
119: /**
120: * Have we encountered a <wi:style> element in a widget ?
121: */
122: protected boolean gotStylingElement;
123:
124: /**
125: * Namespace prefix used for the namespace <code>Constants.WT_NS</code>.
126: */
127: protected String namespacePrefix;
128:
129: public EffectWidgetReplacingPipe() {
130: // Setup map of templates.
131: templates.put(WIDGET, widgetHandler);
132: templates.put(WIDGET_LABEL, widgetLabelHandler);
133: templates.put(REPEATER_WIDGET, repeaterWidgetHandler);
134: templates.put(AGGREGATE_WIDGET, aggregateWidgetHandler);
135: templates.put(REPEATER_SIZE, repeaterSizeHandler);
136: templates
137: .put(REPEATER_WIDGET_LABEL, repeaterWidgetLabelHandler);
138: templates.put(STRUCT, structHandler);
139: templates.put(UNION, unionHandler);
140: templates.put(NEW, newHandler);
141: templates.put(CLASS, classHandler);
142: templates.put(CONTINUATION_ID, continuationIdHandler);
143: templates.put(VALIDATION_ERROR, validationErrorHandler);
144: }
145:
146: private void throwSAXException(String message) throws SAXException {
147: throw new SAXException("EffectWoodyTemplateTransformer: "
148: + message);
149: }
150:
151: public void init(Widget contextWidget,
152: WoodyPipelineConfig pipeContext) {
153: super .init();
154: this .pipeContext = pipeContext;
155:
156: // Attach document handler
157: handler = docHandler;
158:
159: // Initialize widget related variables
160: contextWidgets = new LinkedList();
161: classes = new HashMap();
162: }
163:
164: protected String getWidgetId(Attributes attributes)
165: throws SAXException {
166: String widgetId = attributes.getValue("id");
167: if (widgetId == null || widgetId.length() == 0) {
168: throwSAXException("Missing required widget \"id\" attribute.");
169: }
170: return widgetId;
171: }
172:
173: protected Widget getWidget(String widgetId) throws SAXException {
174: Widget widget = contextWidget.getWidget(widgetId);
175: if (widget == null) {
176: if (contextWidget.getFullyQualifiedId() == null) {
177: throwSAXException("Widget with id \"" + widgetId
178: + "\" does not exist in the form container.");
179: } else {
180: throwSAXException("Widget with id \"" + widgetId
181: + "\" does not exist in the container \""
182: + contextWidget.getFullyQualifiedId() + "\"");
183: }
184: }
185: return widget;
186: }
187:
188: protected void getRepeaterWidget(String handler)
189: throws SAXException {
190: widgetId = getWidgetId(input.attrs);
191: widget = getWidget(widgetId);
192: if (!(widget instanceof Repeater)) {
193: throwWrongWidgetType("RepeaterWidgetLabelHandler",
194: input.loc, "repeater");
195: }
196: }
197:
198: public void throwWrongWidgetType(String pipeName, String element,
199: String widget) throws SAXException {
200: throwSAXException(pipeName + ": Element \"" + element
201: + "\" can only be used for " + widget + " widgets.");
202: }
203:
204: /**
205: * Needed to get things working with JDK 1.3. Can be removed once we
206: * don't support that platform any more.
207: */
208: private ContentHandler getContentHandler() {
209: return this .contentHandler;
210: }
211:
212: /**
213: * Needed to get things working with JDK 1.3. Can be removed once we
214: * don't support that platform any more.
215: */
216: private LexicalHandler getLexicalHandler() {
217: return this .lexicalHandler;
218: }
219:
220: public Handler nestedTemplate() throws SAXException {
221: if (Constants.WT_NS.equals(input.uri)) {
222: // Element in woody template namespace.
223: Handler handler = (Handler) templates.get(input.loc);
224: if (handler != null) {
225: return handler;
226: } else if (FORM_TEMPLATE_EL.equals(input.loc)) {
227: throwSAXException("Element \"form-template\" must not be nested.");
228: return null; // Keep the compiler happy.
229: } else {
230: throwSAXException("Unrecognized template: " + input.loc);
231: return null; // Keep the compiler happy.
232: }
233: } else {
234: // Element not in woody namespace.
235: return nestedHandler;
236: }
237: }
238:
239: //==============================================
240: // Handler classes to transform Woody templates
241: //==============================================
242:
243: protected class DocHandler extends Handler {
244: public Handler process() throws SAXException {
245: switch (event) {
246: case EVENT_SET_DOCUMENT_LOCATOR:
247: return this ;
248: case EVENT_START_PREFIX_MAPPING:
249: // We consume this namespace completely
250: EffectWidgetReplacingPipe.this .namespacePrefix = input.prefix;
251: return this ;
252: case EVENT_ELEMENT:
253: if (Constants.WT_NS.equals(input.uri)) {
254: if (FORM_TEMPLATE_EL.equals(input.loc)) {
255: return formHandler;
256: } else {
257: throwSAXException("Woody template \""
258: + input.loc
259: + "\" not permitted outside \"form-template\"");
260: }
261: } else {
262: return this ;
263: }
264: case EVENT_END_PREFIX_MAPPING:
265: // We consume this namespace completely
266: return this ;
267: default:
268: out.copy();
269: return this ;
270: }
271: }
272: }
273:
274: protected class FormHandler extends Handler {
275: public Handler process() throws SAXException {
276: switch (event) {
277: case EVENT_START_ELEMENT:
278: if (contextWidget != null) {
279: throwSAXException("Detected nested wt:form-template elements, this is not allowed.");
280: }
281: out.startPrefixMapping(Constants.WI_PREFIX,
282: Constants.WI_NS);
283:
284: // ====> Retrieve the form
285: // First look for the form using the location attribute, if any
286: String formJXPath = input.attrs.getValue(LOCATION);
287: if (formJXPath != null) {
288: // remove the location attribute
289: AttributesImpl attrsCopy = new AttributesImpl(
290: input.attrs);
291: attrsCopy.removeAttribute(input.attrs
292: .getIndex(LOCATION));
293: input.attrs = attrsCopy;
294: input.mine = true;
295: }
296: contextWidget = pipeContext.findForm(formJXPath);
297:
298: // ====> Determine the Locale
299: //TODO pull this locale stuff also up in the Config object?
300: String localeAttr = input.attrs.getValue("locale");
301: if (localeAttr != null) { // first use value of locale attribute if any
302: localeAttr = pipeContext.translateText(localeAttr);
303: pipeContext.setLocale(I18nUtils
304: .parseLocale(localeAttr));
305: } else if (pipeContext.getLocaleParameter() != null) { // then use locale specified as transformer parameter, if any
306: pipeContext.setLocale(pipeContext
307: .getLocaleParameter());
308: } else {
309: //TODO pull this locale stuff also up in the Config object?
310: // use locale specified in bizdata supplied for form
311: Object locale = null;
312: try {
313: locale = pipeContext
314: .evaluateExpression("/locale");
315: } catch (JXPathException e) {
316: }
317: if (locale != null) {
318: pipeContext.setLocale((Locale) locale);
319: } else {
320: // final solution: use locale defined in the server machine
321: pipeContext.setLocale(Locale.getDefault());
322: }
323: }
324:
325: String[] namesToTranslate = { "action" };
326: Attributes transAttrs = translateAttributes(
327: input.attrs, namesToTranslate);
328: out.element(Constants.WI_PREFIX, Constants.WI_NS,
329: "form-template", transAttrs);
330: out.startElement();
331: return this ;
332: case EVENT_ELEMENT:
333: return nestedTemplate();
334: case EVENT_END_ELEMENT:
335: out.copy();
336: out.endPrefixMapping(Constants.WI_PREFIX);
337: contextWidget = null;
338: return this ;
339: default:
340: out.copy();
341: return this ;
342: }
343: }
344: }
345:
346: protected class NestedHandler extends Handler {
347: public Handler process() throws SAXException {
348: switch (event) {
349: case EVENT_ELEMENT:
350: return nestedTemplate();
351: default:
352: out.copy();
353: return this ;
354: }
355: }
356: }
357:
358: protected class WidgetLabelHandler extends Handler {
359: public Handler process() throws SAXException {
360: switch (event) {
361: case EVENT_START_ELEMENT:
362: widgetId = getWidgetId(input.attrs);
363: Widget widget = getWidget(widgetId);
364: widget.generateLabel(getContentHandler());
365: widget = null;
366: return this ;
367: case EVENT_ELEMENT:
368: return nullHandler;
369: case EVENT_END_ELEMENT:
370: return this ;
371: default:
372: out.copy();
373: return this ;
374: }
375: }
376: }
377:
378: protected class WidgetHandler extends Handler {
379: public Handler process() throws SAXException {
380: switch (event) {
381: case EVENT_START_ELEMENT:
382: widgetId = getWidgetId(input.attrs);
383: widget = getWidget(widgetId);
384: gotStylingElement = false;
385: out.bufferInit();
386: return this ;
387:
388: case EVENT_ELEMENT:
389: if (Constants.WI_NS.equals(input.uri)
390: && STYLING_EL.equals(input.loc)) {
391: gotStylingElement = true;
392: }
393: return bufferHandler;
394:
395: case EVENT_END_ELEMENT:
396: stylingHandler.recycle();
397: stylingHandler.setSaxFragment(out.getBuffer());
398: stylingHandler.setContentHandler(getContentHandler());
399: stylingHandler.setLexicalHandler(getLexicalHandler());
400: widget.generateSaxFragment(stylingHandler, pipeContext
401: .getLocale());
402: widget = null;
403: out.bufferFini();
404: return this ;
405:
406: default:
407: out.copy();
408: return this ;
409: }
410: }
411: }
412:
413: protected class RepeaterSizeHandler extends Handler {
414: public Handler process() throws SAXException {
415: switch (event) {
416: case EVENT_START_ELEMENT:
417: getRepeaterWidget("RepeaterSizeHandler");
418: ((Repeater) widget).generateSize(getContentHandler());
419: widget = null;
420: return this ;
421: case EVENT_ELEMENT:
422: return nullHandler;
423: case EVENT_END_ELEMENT:
424: return this ;
425: default:
426: out.copy();
427: return this ;
428: }
429: }
430: }
431:
432: protected class RepeaterWidgetLabelHandler extends Handler {
433: public Handler process() throws SAXException {
434: switch (event) {
435: case EVENT_START_ELEMENT:
436: getRepeaterWidget("RepeaterWidgetLabelHandler");
437: String widgetId = input.attrs.getValue("widget-id");
438: if (widgetId == null || widgetId.length() == 0)
439: throwSAXException("Element repeater-widget-label missing required widget-id attribute.");
440: ((Repeater) widget).generateWidgetLabel(widgetId,
441: getContentHandler());
442: widget = null;
443: return this ;
444: case EVENT_ELEMENT:
445: return nullHandler;
446: case EVENT_END_ELEMENT:
447: return this ;
448: default:
449: out.copy();
450: return this ;
451: }
452: }
453: }
454:
455: protected class RepeaterWidgetHandler extends Handler {
456: public Handler process() throws SAXException {
457: switch (event) {
458: case EVENT_START_ELEMENT:
459: getRepeaterWidget("RepeaterWidgetHandler");
460: out.bufferInit();
461: return this ;
462: case EVENT_ELEMENT:
463: return bufferHandler;
464: case EVENT_END_ELEMENT:
465: Repeater repeater = (Repeater) widget;
466: int rowCount = repeater.getSize();
467: handlers.addFirst(handler);
468: handler = nestedHandler;
469: contextWidgets.addFirst(contextWidget);
470: for (int i = 0; i < rowCount; i++) {
471: Repeater.RepeaterRow row = repeater.getRow(i);
472: contextWidget = row;
473: out.getBuffer().toSAX(
474: EffectWidgetReplacingPipe.this );
475: }
476: contextWidget = (Widget) contextWidgets.removeFirst();
477: handler = (Handler) handlers.removeFirst();
478: widget = null;
479: out.bufferFini();
480: return this ;
481: default:
482: out.buffer();
483: return this ;
484: }
485: }
486: }
487:
488: protected class AggregateWidgetHandler extends Handler {
489: public Handler process() throws SAXException {
490: switch (event) {
491: case EVENT_START_ELEMENT:
492: widgetId = getWidgetId(input.attrs);
493: widget = getWidget(widgetId);
494: if (!(widget instanceof AggregateField)) {
495: throwWrongWidgetType("AggregateWidgetHandler",
496: input.loc, "aggregate");
497: }
498: contextWidgets.addFirst(contextWidget);
499: contextWidget = widget;
500: return this ;
501: case EVENT_ELEMENT:
502: return nestedTemplate();
503: case EVENT_END_ELEMENT:
504: contextWidget = (Widget) contextWidgets.removeFirst();
505: return this ;
506: default:
507: out.copy();
508: return this ;
509: }
510: }
511: }
512:
513: protected class StructHandler extends Handler {
514: public Handler process() throws SAXException {
515: switch (event) {
516: case EVENT_START_ELEMENT:
517: widgetId = getWidgetId(input.attrs);
518: widget = getWidget(widgetId);
519: if (!(widget instanceof Struct)) {
520: throwWrongWidgetType("StructHandler", input.loc,
521: "struct");
522: }
523: contextWidgets.addFirst(contextWidget);
524: contextWidget = widget;
525: out.element(Constants.WI_PREFIX, Constants.WI_NS,
526: "struct");
527: out.attributes();
528: out.startElement();
529: return this ;
530: case EVENT_ELEMENT:
531: return nestedTemplate();
532: case EVENT_END_ELEMENT:
533: out.copy();
534: contextWidget = (Widget) contextWidgets.removeFirst();
535: return this ;
536: default:
537: out.copy();
538: return this ;
539: }
540: }
541: }
542:
543: protected class UnionHandler extends Handler {
544: public Handler process() throws SAXException {
545: switch (event) {
546: case EVENT_START_ELEMENT:
547: widgetId = getWidgetId(input.attrs);
548: widget = getWidget(widgetId);
549: if (!(widget instanceof Union)) {
550: throwWrongWidgetType("UnionHandler", input.loc,
551: "union");
552: }
553: contextWidgets.addFirst(contextWidget);
554: contextWidget = widget;
555: out.element(Constants.WI_PREFIX, Constants.WI_NS,
556: "union");
557: out.startElement();
558: return this ;
559: case EVENT_ELEMENT:
560: if (Constants.WT_NS.equals(input.uri)) {
561: if ("case".equals(input.loc)) {
562: String id = input.attrs.getValue("id");
563: if (id == null)
564: throwSAXException("Element \"case\" missing required \"id\" attribute.");
565: String value = (String) contextWidget
566: .getValue();
567: if (id.equals(value != null ? value : "")) {
568: return nestedHandler;
569: } else {
570: return nullHandler;
571: }
572: } else if (FORM_TEMPLATE_EL.equals(input.loc)) {
573: throwSAXException("Element \"form-template\" must not be nested.");
574: } else {
575: throwSAXException("Unrecognized template: "
576: + input.loc);
577: }
578: } else {
579: return unionPassThruHandler;
580: }
581: case EVENT_END_ELEMENT:
582: out.endElement();
583: contextWidget = (Widget) contextWidgets.removeFirst();
584: return this ;
585: default:
586: out.copy();
587: return this ;
588: }
589: }
590: }
591:
592: protected class UnionPassThruHandler extends Handler {
593: public Handler process() throws SAXException {
594: switch (event) {
595: case EVENT_ELEMENT:
596: if (Constants.WT_NS.equals(input.uri)) {
597: if ("case".equals(input.loc)) {
598: if (contextWidget.getValue().equals(
599: input.attrs.getValue("id"))) {
600: return nestedHandler;
601: } else {
602: return nullHandler;
603: }
604: } else if (FORM_TEMPLATE_EL.equals(input.loc)) {
605: throwSAXException("Element \"form-template\" must not be nested.");
606: } else {
607: throwSAXException("Unrecognized template: "
608: + input.loc);
609: }
610: } else {
611: return this ;
612: }
613: default:
614: out.copy();
615: return this ;
616: }
617: }
618: }
619:
620: protected class NewHandler extends Handler {
621: public Handler process() throws SAXException {
622: switch (event) {
623: case EVENT_START_ELEMENT:
624: widgetId = getWidgetId(input.attrs);
625: SaxBuffer classBuffer = (SaxBuffer) classes
626: .get(widgetId);
627: if (classBuffer == null) {
628: throwSAXException("New: Class \"" + widgetId
629: + "\" does not exist.");
630: }
631: handlers.addFirst(handler);
632: handler = nestedHandler;
633: classBuffer.toSAX(EffectWidgetReplacingPipe.this );
634: handler = (Handler) handlers.removeFirst();
635: return this ;
636: case EVENT_ELEMENT:
637: return nullHandler;
638: case EVENT_END_ELEMENT:
639: return this ;
640: default:
641: out.copy();
642: return this ;
643: }
644: }
645: }
646:
647: protected class ClassHandler extends Handler {
648: public Handler process() throws SAXException {
649: switch (event) {
650: case EVENT_START_ELEMENT:
651: widgetId = getWidgetId(input.attrs);
652: out.bufferInit();
653: return this ;
654: case EVENT_ELEMENT:
655: return bufferHandler;
656: case EVENT_END_ELEMENT:
657: classes.put(widgetId, out.getBuffer());
658: out.bufferFini();
659: return this ;
660: default:
661: out.buffer();
662: return this ;
663: }
664: }
665: }
666:
667: protected class ContinuationIdHandler extends Handler {
668: public Handler process() throws SAXException {
669: switch (event) {
670: case EVENT_START_ELEMENT:
671: // Insert the continuation id
672: // FIXME(SW) we could avoid costly JXPath evaluation if we had the objectmodel here.
673: Object idObj = pipeContext
674: .evaluateExpression("$continuation/id");
675: if (idObj == null) {
676: throwSAXException("No continuation found");
677: }
678:
679: String id = idObj.toString();
680: out.element(Constants.WI_PREFIX, Constants.WI_NS,
681: "continuation-id", input.attrs);
682: out.startElement();
683: out.characters(id.toCharArray(), 0, id.length());
684: out.endElement();
685: return this ;
686: case EVENT_END_ELEMENT:
687: return this ;
688: case EVENT_IGNORABLE_WHITESPACE:
689: return this ;
690: default:
691: throwSAXException("ContinuationIdHandler: No content allowed in \"continuation-id\" element");
692: return null; // Keep the compiler happy.
693: }
694: }
695: }
696:
697: /**
698: * This ContentHandler helps in inserting SAX events before the closing tag of the root
699: * element.
700: */
701: protected class StylingContentHandler extends AbstractXMLPipe
702: implements Recyclable {
703: private int elementNesting;
704: private SaxBuffer saxBuffer;
705:
706: public void setSaxFragment(SaxBuffer saxFragment) {
707: saxBuffer = saxFragment;
708: }
709:
710: public void recycle() {
711: super .recycle();
712: elementNesting = 0;
713: saxBuffer = null;
714: }
715:
716: public void startElement(String uri, String loc, String raw,
717: Attributes a) throws SAXException {
718: elementNesting++;
719: super .startElement(uri, loc, raw, a);
720: }
721:
722: public void endElement(String uri, String loc, String raw)
723: throws SAXException {
724: elementNesting--;
725: if (elementNesting == 0 && saxBuffer != null) {
726: if (gotStylingElement) {
727: // Just deserialize
728: saxBuffer.toSAX(getContentHandler());
729: } else {
730: // Insert an enclosing <wi:styling>
731: out.startElement(Constants.WI_NS, STYLING_EL,
732: Constants.WI_PREFIX_COLON + STYLING_EL,
733: Constants.EMPTY_ATTRS);
734: saxBuffer.toSAX(getContentHandler());
735: out.endElement(Constants.WI_NS, STYLING_EL,
736: Constants.WI_PREFIX_COLON + STYLING_EL);
737: }
738: }
739: super .endElement(uri, loc, raw);
740: }
741: }
742:
743: /**
744: * Inserts validation errors (if any) for the Field widgets
745: */
746: protected class ValidationErrorHandler extends Handler {
747: public Handler process() throws SAXException {
748: switch (event) {
749: case EVENT_START_ELEMENT:
750: widgetId = getWidgetId(input.attrs);
751: widget = getWidget(widgetId);
752: out.bufferInit();
753: return this ;
754:
755: case EVENT_ELEMENT:
756: return bufferHandler;
757:
758: case EVENT_END_ELEMENT:
759: if (widget instanceof ValidationErrorAware) {
760: ValidationError error = ((ValidationErrorAware) widget)
761: .getValidationError();
762: if (error != null) {
763: out.startElement(Constants.WI_NS,
764: VALIDATION_ERROR,
765: Constants.WI_PREFIX_COLON
766: + VALIDATION_ERROR,
767: Constants.EMPTY_ATTRS);
768: error.generateSaxFragment(stylingHandler);
769: out.endElement(Constants.WI_NS,
770: VALIDATION_ERROR,
771: Constants.WI_PREFIX_COLON
772: + VALIDATION_ERROR);
773: }
774: }
775: widget = null;
776: out.bufferFini();
777: return this ;
778:
779: default:
780: out.copy();
781: return this ;
782: }
783: }
784: }
785:
786: private Attributes translateAttributes(Attributes attributes,
787: String[] names) {
788: AttributesImpl newAtts = new AttributesImpl(attributes);
789: if (names != null) {
790: for (int i = 0; i < names.length; i++) {
791: String name = names[i];
792: int position = newAtts.getIndex(name);
793: String newValue = pipeContext.translateText(newAtts
794: .getValue(position));
795: newAtts.setValue(position, newValue);
796: }
797: }
798: return newAtts;
799: }
800:
801: // /**
802: // * Replaces JXPath expressions embedded inside #{ and } by their value.
803: // */
804: // private String translateText(String original) {
805: // StringBuffer expression;
806: // StringBuffer translated = new StringBuffer();
807: // StringReader in = new StringReader(original);
808: // int chr;
809: // try {
810: // while ((chr = in.read()) != -1) {
811: // char c = (char) chr;
812: // if (c == '#') {
813: // chr = in.read();
814: // if (chr != -1) {
815: // c = (char) chr;
816: // if (c == '{') {
817: // expression = new StringBuffer();
818: // boolean more = true;
819: // while ( more ) {
820: // more = false;
821: // if ((chr = in.read()) != -1) {
822: // c = (char)chr;
823: // if (c != '}') {
824: // expression.append(c);
825: // more = true;
826: // } else {
827: // translated.append(evaluateExpression(expression.toString()));
828: // }
829: // } else {
830: // translated.append('#').append('{').append(expression);
831: // }
832: // }
833: // }
834: // } else {
835: // translated.append((char) chr);
836: // }
837: // } else {
838: // translated.append(c);
839: // }
840: // }
841: // } catch (IOException ignored) {
842: // ignored.printStackTrace();
843: // }
844: // return translated.toString();
845: // }
846:
847: // private String evaluateExpression(String expression) {
848: // return pipeContext.evaluateExpression(expression).toString();
849: // }
850:
851: public void recycle() {
852: super.recycle();
853: this.contextWidget = null;
854: this.widget = null;
855: this.widgetId = null;
856: this.pipeContext = null;
857: this.namespacePrefix = null;
858: }
859: }
|