0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017: package org.apache.cocoon.forms.transformation;
0018:
0019: import org.apache.cocoon.forms.FormsConstants;
0020: import org.apache.cocoon.forms.event.ValueChangedListenerEnabled;
0021: import org.apache.cocoon.forms.formmodel.AggregateField;
0022: import org.apache.cocoon.forms.formmodel.DataWidget;
0023: import org.apache.cocoon.forms.formmodel.Group;
0024: import org.apache.cocoon.forms.formmodel.Repeater;
0025: import org.apache.cocoon.forms.formmodel.Struct;
0026: import org.apache.cocoon.forms.formmodel.Union;
0027: import org.apache.cocoon.forms.formmodel.Widget;
0028: import org.apache.cocoon.forms.validation.ValidationError;
0029: import org.apache.cocoon.forms.validation.ValidationErrorAware;
0030: import org.apache.cocoon.i18n.I18nUtils;
0031: import org.apache.cocoon.xml.AbstractXMLPipe;
0032: import org.apache.cocoon.xml.AttributesImpl;
0033: import org.apache.cocoon.xml.SaxBuffer;
0034: import org.apache.cocoon.xml.XMLUtils;
0035:
0036: import org.apache.commons.jxpath.JXPathException;
0037: import org.xml.sax.Attributes;
0038: import org.xml.sax.ContentHandler;
0039: import org.xml.sax.SAXException;
0040: import org.xml.sax.ext.LexicalHandler;
0041:
0042: import java.util.ArrayList;
0043: import java.util.HashMap;
0044: import java.util.LinkedList;
0045: import java.util.List;
0046: import java.util.Locale;
0047: import java.util.Map;
0048:
0049: /**
0050: * The basic operation of this Pipe is that it replaces <code>ft:widget</code>
0051: * (in the {@link FormsConstants#TEMPLATE_NS} namespace) tags (having an id attribute)
0052: * by the XML representation of the corresponding widget instance.
0053: *
0054: * <p>These XML fragments (normally all in the {@link FormsConstants#INSTANCE_NS "CForms Instance"}
0055: * namespace), can then be translated to a HTML presentation by an XSLT.
0056: * This XSLT will then only have to style individual widget, and will not
0057: * need to do the whole page layout.
0058: *
0059: * <p>For more information about the supported tags and their function,
0060: * see the user documentation for the forms template transformer.</p>
0061: *
0062: * @version $Id: EffectWidgetReplacingPipe.java 449149 2006-09-23 03:58:05Z crossley $
0063: */
0064: public class EffectWidgetReplacingPipe extends EffectPipe {
0065:
0066: /**
0067: * Form location attribute on <code>ft:form-template</code> element, containing
0068: * JXPath expression which should result in Form object.
0069: *
0070: * @see FormsPipelineConfig#findForm(String)
0071: */
0072: private static final String LOCATION = "location";
0073:
0074: private static final String AGGREGATE_WIDGET = "aggregate-widget";
0075: private static final String CHOOSE = "choose";
0076: private static final String CLASS = "class";
0077: private static final String CONTINUATION_ID = "continuation-id";
0078: private static final String FORM_TEMPLATE_EL = "form-template";
0079: private static final String GROUP = "group";
0080: private static final String NEW = "new";
0081: private static final String REPEATER = "repeater";
0082: private static final String REPEATER_ROWS = "repeater-rows";
0083: private static final String REPEATER_SIZE = "repeater-size";
0084: private static final String REPEATER_WIDGET = "repeater-widget";
0085: private static final String REPEATER_WIDGET_LABEL = "repeater-widget-label";
0086: private static final String STRUCT = "struct";
0087: private static final String STYLING_EL = "styling";
0088: private static final String UNION = "union";
0089: private static final String VALIDATION_ERROR = "validation-error";
0090: private static final String WIDGET = "widget";
0091: private static final String WIDGET_LABEL = "widget-label";
0092:
0093: private final AggregateWidgetHandler hAggregate = new AggregateWidgetHandler();
0094: private final ChooseHandler hChoose = new ChooseHandler();
0095: protected final ChoosePassThruHandler hChoosePassThru = new ChoosePassThruHandler();
0096: private final ClassHandler hClass = new ClassHandler();
0097: private final ContinuationIdHandler hContinuationId = new ContinuationIdHandler();
0098: private final DocHandler hDocument = new DocHandler();
0099: protected final FormHandler hForm = new FormHandler();
0100: private final GroupHandler hGroup = new GroupHandler();
0101: protected final NestedHandler hNested = new NestedHandler();
0102: private final NewHandler hNew = new NewHandler();
0103: private final RepeaterSizeHandler hRepeaterSize = new RepeaterSizeHandler();
0104: private final RepeaterHandler hRepeater = new RepeaterHandler();
0105: private final RepeaterRowsHandler hRepeaterRows = new RepeaterRowsHandler();
0106: private final RepeaterWidgetHandler hRepeaterWidget = new RepeaterWidgetHandler();
0107: private final RepeaterWidgetLabelHandler hRepeaterWidgetLabel = new RepeaterWidgetLabelHandler();
0108: protected final SkipHandler hSkip = new SkipHandler();
0109: private final StructHandler hStruct = new StructHandler();
0110: protected final StylingContentHandler hStyling = new StylingContentHandler();
0111: private final UnionHandler hUnion = new UnionHandler();
0112: protected final UnionPassThruHandler hUnionPassThru = new UnionPassThruHandler();
0113: private final ValidationErrorHandler hValidationError = new ValidationErrorHandler();
0114: private final WidgetHandler hWidget = new WidgetHandler();
0115: private final WidgetLabelHandler hWidgetLabel = new WidgetLabelHandler();
0116:
0117: /**
0118: * Map containing all handlers
0119: */
0120: protected final Map templates;
0121:
0122: protected FormsPipelineConfig pipeContext;
0123:
0124: /**
0125: * The namespaces and their prefixes
0126: */
0127: private final List namespaces;
0128:
0129: /**
0130: * True if instance namespace has been mapped to the
0131: * 'fi' prefix.
0132: */
0133: protected boolean hasInstanceNamespace;
0134:
0135: protected Widget contextWidget;
0136: protected LinkedList contextWidgets;
0137: protected LinkedList chooseWidgets;
0138: protected Widget widget;
0139: protected Map classes;
0140:
0141: public EffectWidgetReplacingPipe() {
0142: namespaces = new ArrayList(5);
0143: // Setup map of templates.
0144: templates = new HashMap();
0145: templates.put(AGGREGATE_WIDGET, hAggregate);
0146: templates.put(CHOOSE, hChoose);
0147: templates.put(CLASS, hClass);
0148: templates.put(CONTINUATION_ID, hContinuationId);
0149: templates.put(GROUP, hGroup);
0150: templates.put(NEW, hNew);
0151: templates.put(REPEATER, hRepeater);
0152: templates.put(REPEATER_ROWS, hRepeaterRows);
0153: templates.put(REPEATER_SIZE, hRepeaterSize);
0154: templates.put(REPEATER_WIDGET, hRepeaterWidget);
0155: templates.put(REPEATER_WIDGET_LABEL, hRepeaterWidgetLabel);
0156: templates.put(STRUCT, hStruct);
0157: templates.put(UNION, hUnion);
0158: templates.put(VALIDATION_ERROR, hValidationError);
0159: templates.put(WIDGET, hWidget);
0160: templates.put(WIDGET_LABEL, hWidgetLabel);
0161: }
0162:
0163: public void init(Widget contextWidget,
0164: FormsPipelineConfig pipeContext) {
0165: // Document handler is top level handler
0166: super .init(hDocument);
0167: this .pipeContext = pipeContext;
0168:
0169: // Initialize widget related variables
0170: this .contextWidgets = new LinkedList();
0171: this .chooseWidgets = new LinkedList();
0172: this .classes = new HashMap();
0173: }
0174:
0175: public void recycle() {
0176: super .recycle();
0177: this .contextWidget = null;
0178: this .widget = null;
0179: this .pipeContext = null;
0180: this .namespaces.clear();
0181: this .hasInstanceNamespace = false;
0182: }
0183:
0184: /**
0185: * Get value of the required attribute
0186: */
0187: protected String getAttributeValue(String loc, Attributes attrs,
0188: String name) throws SAXException {
0189: String value = attrs.getValue(name);
0190: if (value == null) {
0191: throw new SAXException("Element '" + loc
0192: + "' missing required '" + name + "' attribute, "
0193: + "at " + getLocation());
0194: }
0195: return value;
0196: }
0197:
0198: /**
0199: * Get non-empty value of the required attribute
0200: */
0201: protected String getRequiredAttributeValue(String loc,
0202: Attributes attrs, String name) throws SAXException {
0203: String value = attrs.getValue(name);
0204: if (value == null || value.length() == 0) {
0205: throw new SAXException("Element '" + loc
0206: + "' missing required '" + name + "' attribute, "
0207: + "at " + getLocation());
0208: }
0209: return value;
0210: }
0211:
0212: /**
0213: * Set the widget by the id attribute
0214: */
0215: protected void setWidget(String loc, Attributes attrs)
0216: throws SAXException {
0217: setWidget(loc, getRequiredAttributeValue(loc, attrs, "id"));
0218: }
0219:
0220: /**
0221: * Set the widget by its path
0222: */
0223: protected void setWidget(String loc, String path)
0224: throws SAXException {
0225: widget = contextWidget.lookupWidget(path);
0226: if (widget == null) {
0227: if (contextWidget.getRequestParameterName().length() == 0) {
0228: throw new SAXException("Element '" + loc
0229: + "' refers to unexistent widget path '" + path
0230: + "', " + "relative to the form container, at "
0231: + getLocation());
0232: } else {
0233: throw new SAXException("Element '" + loc
0234: + "' refers to unexistent widget path '" + path
0235: + "', " + "relative to the '"
0236: + contextWidget.getRequestParameterName()
0237: + "', " + "at " + getLocation());
0238: }
0239: }
0240: }
0241:
0242: /**
0243: * Set typed widget by the id attribute
0244: */
0245: protected void setTypedWidget(String loc, Attributes attrs,
0246: Class wclass, String wname) throws SAXException {
0247: setWidget(loc, attrs);
0248: if (!wclass.isInstance(widget)) {
0249: throw new SAXException("Element '" + loc
0250: + "' can only be used with " + wname + " widgets, "
0251: + "at " + getLocation());
0252: }
0253: }
0254:
0255: protected boolean isVisible(Widget widget) {
0256: return widget.getCombinedState().isDisplayingValues();
0257: }
0258:
0259: /**
0260: * Needed to get things working with JDK 1.3. Can be removed once we
0261: * don't support that platform any more.
0262: */
0263: protected ContentHandler getContentHandler() {
0264: return this .contentHandler;
0265: }
0266:
0267: /**
0268: * Needed to get things working with JDK 1.3. Can be removed once we
0269: * don't support that platform any more.
0270: */
0271: protected LexicalHandler getLexicalHandler() {
0272: return this .lexicalHandler;
0273: }
0274:
0275: /**
0276: * Process the SAX event.
0277: * @see org.xml.sax.ContentHandler#startPrefixMapping
0278: */
0279: public void startPrefixMapping(String prefix, String uri)
0280: throws SAXException {
0281: if (prefix != null) {
0282: this .namespaces.add(new String[] { prefix, uri });
0283: }
0284:
0285: // Consume template namespace mapping
0286: if (!FormsConstants.TEMPLATE_NS.equals(uri)) {
0287: super .startPrefixMapping(prefix, uri);
0288: }
0289: }
0290:
0291: /**
0292: * Process the SAX event.
0293: * @see org.xml.sax.ContentHandler#endPrefixMapping
0294: */
0295: public void endPrefixMapping(String prefix) throws SAXException {
0296: String uri = null;
0297:
0298: if (prefix != null) {
0299: // Find and remove the namespace prefix
0300: boolean found = false;
0301: for (int i = this .namespaces.size() - 1; i >= 0; i--) {
0302: final String[] prefixAndUri = (String[]) this .namespaces
0303: .get(i);
0304: if (prefixAndUri[0].equals(prefix)) {
0305: uri = prefixAndUri[1];
0306: this .namespaces.remove(i);
0307: found = true;
0308: break;
0309: }
0310: }
0311: if (!found) {
0312: throw new SAXException("Namespace for prefix '"
0313: + prefix + "' not found.");
0314: }
0315: }
0316:
0317: // Consume template namespace mapping
0318: if (!FormsConstants.TEMPLATE_NS.equals(uri)) {
0319: super .endPrefixMapping(prefix);
0320: }
0321: }
0322:
0323: /**
0324: * @return True if prefix is already mapped into the namespace
0325: */
0326: protected boolean hasPrefixMapping(String uri, String prefix) {
0327: final int l = this .namespaces.size();
0328: for (int i = 0; i < l; i++) {
0329: String[] prefixAndUri = (String[]) this .namespaces.get(i);
0330: if (prefixAndUri[0].equals(prefix)
0331: && prefixAndUri[1].equals(uri)) {
0332: return true;
0333: }
0334: }
0335:
0336: return false;
0337: }
0338:
0339: //
0340: // Handler classes to transform CForms template elements
0341: //
0342:
0343: protected class NestedHandler extends CopyHandler {
0344: public Handler nestedElement(String uri, String loc,
0345: String raw, Attributes attrs) throws SAXException {
0346: // Is it forms namespace?
0347: if (!FormsConstants.TEMPLATE_NS.equals(uri)) {
0348: return hNested;
0349: }
0350:
0351: Handler handler = (Handler) templates.get(loc);
0352: if (handler == null) {
0353: throw new SAXException("Element '" + loc
0354: + "' was not recognized, " + "at "
0355: + getLocation());
0356: }
0357:
0358: return handler;
0359: }
0360: }
0361:
0362: /**
0363: * Top level handler for the forms template
0364: */
0365: protected class DocHandler extends CopyHandler {
0366: public Handler nestedElement(String uri, String loc,
0367: String raw, Attributes attrs) throws SAXException {
0368: if (FormsConstants.TEMPLATE_NS.equals(uri)) {
0369: if (!FORM_TEMPLATE_EL.equals(loc)) {
0370: throw new SAXException("Element '" + loc
0371: + "' is not permitted outside of "
0372: + "'form-template', at " + getLocation());
0373: }
0374: return hForm;
0375: }
0376:
0377: return super .nestedElement(uri, loc, raw, attrs);
0378: }
0379: }
0380:
0381: /**
0382: * <code>ft:form-template</code> element handler.
0383: * <pre>
0384: * <ft:form-template locale="..." location="...">
0385: * ...
0386: * </ft:form-template>
0387: * </pre>
0388: */
0389: protected class FormHandler extends NestedHandler {
0390: public Handler startElement(String uri, String loc, String raw,
0391: Attributes attrs) throws SAXException {
0392: if (contextWidget != null) {
0393: throw new SAXException(
0394: "Element 'form-template' can not be nested, "
0395: + "at " + getLocation());
0396: }
0397:
0398: AttributesImpl newAttrs = attrs == null
0399: || attrs.getLength() == 0 ? new AttributesImpl()
0400: : new AttributesImpl(attrs);
0401:
0402: // ====> Retrieve the form
0403: String formLocation = attrs.getValue(LOCATION);
0404: if (formLocation != null) {
0405: // Remove the location attribute
0406: newAttrs.removeAttribute(newAttrs.getIndex(LOCATION));
0407: }
0408: contextWidget = pipeContext.findForm(formLocation);
0409:
0410: // ====> Check if form visible (and skip it if it's not)
0411: if (!isVisible(contextWidget)) {
0412: return hNull;
0413: }
0414:
0415: // set some general attributes
0416: // top-level widget-containers like forms might have their id set to ""
0417: // for those the @id should not be included.
0418: if (contextWidget.getId().length() != 0
0419: && newAttrs.getValue("id") == null) {
0420: newAttrs.addCDATAAttribute("id", contextWidget
0421: .getRequestParameterName());
0422: }
0423:
0424: // Add the "state" attribute
0425: if (newAttrs.getValue("state") == null) {
0426: newAttrs.addCDATAAttribute("state", contextWidget
0427: .getCombinedState().getName());
0428: }
0429:
0430: // Add the "listening" attribute is the value has change listeners
0431: if (contextWidget instanceof ValueChangedListenerEnabled
0432: && ((ValueChangedListenerEnabled) contextWidget)
0433: .hasValueChangedListeners()
0434: && newAttrs.getValue("listening") == null) {
0435: newAttrs.addCDATAAttribute("listening", "true");
0436: }
0437:
0438: // ====> Determine the Locale
0439: // TODO pull this locale stuff also up in the Config object?
0440: String localeAttr = attrs.getValue("locale");
0441: if (localeAttr != null) { // first use value of locale attribute if any
0442: localeAttr = pipeContext.translateText(localeAttr);
0443: pipeContext
0444: .setLocale(I18nUtils.parseLocale(localeAttr));
0445: } else if (pipeContext.getLocaleParameter() != null) { // then use locale specified as transformer parameter, if any
0446: pipeContext.setLocale(pipeContext.getLocaleParameter());
0447: } else {
0448: // use locale specified in bizdata supplied for form
0449: Object locale = null;
0450: try {
0451: locale = pipeContext.evaluateExpression("/locale");
0452: } catch (JXPathException e) {
0453: }
0454: if (locale != null) {
0455: pipeContext.setLocale((Locale) locale);
0456: } else {
0457: // final solution: use locale defined in the server machine
0458: pipeContext.setLocale(Locale.getDefault());
0459: }
0460: }
0461:
0462: // We need to merge input.attrs with possible overruling attributes
0463: // from the pipeContext
0464: pipeContext.addFormAttributes(newAttrs);
0465: String[] namesToTranslate = { "action" };
0466: Attributes transAttrs = null;
0467: try {
0468: transAttrs = translateAttributes(newAttrs,
0469: namesToTranslate);
0470: } catch (RuntimeException e) {
0471: throw new SAXException(e.getMessage() + " "
0472: + getLocation());
0473: }
0474:
0475: hasInstanceNamespace = hasPrefixMapping(
0476: FormsConstants.INSTANCE_NS,
0477: FormsConstants.INSTANCE_PREFIX);
0478: if (!hasInstanceNamespace) {
0479: getContentHandler().startPrefixMapping(
0480: FormsConstants.INSTANCE_PREFIX,
0481: FormsConstants.INSTANCE_NS);
0482: }
0483: getContentHandler().startElement(
0484: FormsConstants.INSTANCE_NS,
0485: "form-template",
0486: FormsConstants.INSTANCE_PREFIX_COLON
0487: + "form-template", transAttrs);
0488: return this ;
0489: }
0490:
0491: public void endElement(String uri, String loc, String raw)
0492: throws SAXException {
0493: getContentHandler().endElement(
0494: FormsConstants.INSTANCE_NS,
0495: "form-template",
0496: FormsConstants.INSTANCE_PREFIX_COLON
0497: + "form-template");
0498: if (!hasInstanceNamespace) {
0499: getContentHandler().endPrefixMapping(
0500: FormsConstants.INSTANCE_PREFIX);
0501: }
0502: contextWidget = null;
0503: }
0504: }
0505:
0506: /**
0507: * <code>ft:choose</code>, <code>ft:union</code> use this.
0508: */
0509: protected class SkipHandler extends NestedHandler {
0510: public Handler startElement(String uri, String loc, String raw,
0511: Attributes attrs) throws SAXException {
0512: return this ;
0513: }
0514:
0515: public void endElement(String uri, String loc, String raw)
0516: throws SAXException {
0517: }
0518: }
0519:
0520: //
0521: // Widget Handlers
0522: //
0523:
0524: /**
0525: * Handles <code>ft:widget-label</code> element.
0526: */
0527: protected class WidgetLabelHandler extends ErrorHandler {
0528: public Handler startElement(String uri, String loc, String raw,
0529: Attributes attrs) throws SAXException {
0530: setWidget(loc, attrs);
0531: widget.generateLabel(getContentHandler());
0532: return this ;
0533: }
0534:
0535: public void endElement(String uri, String loc, String raw)
0536: throws SAXException {
0537: }
0538: }
0539:
0540: /**
0541: * Handles <code>ft:widget</code> element.
0542: */
0543: protected class WidgetHandler extends NullHandler {
0544: // Widgets can't be nested, so this variable is Ok
0545: private boolean hasStyling;
0546:
0547: public Handler startElement(String uri, String loc, String raw,
0548: Attributes attrs) throws SAXException {
0549: setWidget(loc, attrs);
0550: if (!isVisible(widget)) {
0551: return hNull;
0552: }
0553:
0554: hasStyling = false;
0555: return this ;
0556: }
0557:
0558: public Handler nestedElement(String uri, String loc,
0559: String raw, Attributes attrs) throws SAXException {
0560: if (FormsConstants.INSTANCE_NS.equals(uri)) {
0561: if (!STYLING_EL.equals(loc)) {
0562: throw new SAXException("Element '" + loc
0563: + "' is not permitted within 'widget', "
0564: + "at " + getLocation());
0565: }
0566: hasStyling = true;
0567: beginBuffer();
0568: // Buffer styling elements
0569: return hBuffer;
0570: }
0571: return hNull;
0572: }
0573:
0574: public void endElement(String uri, String loc, String raw)
0575: throws SAXException {
0576: if (hasStyling) {
0577: // Pipe widget XML through the special handler to insert styling element
0578: // before fi:widget end element.
0579: hasStyling = false;
0580: hStyling.recycle();
0581: hStyling.setSaxFragment(endBuffer());
0582: hStyling.setContentHandler(getContentHandler());
0583: hStyling.setLexicalHandler(getLexicalHandler());
0584: widget.generateSaxFragment(hStyling, pipeContext
0585: .getLocale());
0586: } else {
0587: // Pipe widget XML directly into the output handler
0588: widget.generateSaxFragment(getContentHandler(),
0589: pipeContext.getLocale());
0590: }
0591: widget = null;
0592: }
0593: }
0594:
0595: //
0596: // Repeater Handlers
0597: //
0598:
0599: /**
0600: * Handles <code>ft:repeater-size</code> element.
0601: */
0602: protected class RepeaterSizeHandler extends ErrorHandler {
0603: public Handler startElement(String uri, String loc, String raw,
0604: Attributes attrs) throws SAXException {
0605: setTypedWidget(loc, attrs, Repeater.class, "repeater");
0606: ((Repeater) widget).generateSize(getContentHandler());
0607: widget = null;
0608: return this ;
0609: }
0610:
0611: public void endElement(String uri, String loc, String raw)
0612: throws SAXException {
0613: }
0614: }
0615:
0616: /**
0617: * Handles <code>ft:repeater-widget-label</code> element.
0618: */
0619: protected class RepeaterWidgetLabelHandler extends ErrorHandler {
0620: public Handler startElement(String uri, String loc, String raw,
0621: Attributes attrs) throws SAXException {
0622: Repeater repeater;
0623: if (contextWidget instanceof Repeater) {
0624: repeater = (Repeater) contextWidget;
0625: } else {
0626: setTypedWidget(loc, attrs, Repeater.class, "repeater");
0627: repeater = (Repeater) widget;
0628: widget = null;
0629: }
0630: String path = getRequiredAttributeValue(loc, attrs,
0631: "widget-id");
0632: repeater.generateWidgetLabel(path, getContentHandler());
0633: return this ;
0634: }
0635:
0636: public void endElement(String uri, String loc, String raw)
0637: throws SAXException {
0638: }
0639: }
0640:
0641: /**
0642: * Handles <code>ft:repeater</code> element. Should contain repeater-rows
0643: */
0644: protected class RepeaterHandler extends NestedHandler {
0645: protected Class getWidgetClass() {
0646: return Repeater.class;
0647: }
0648:
0649: protected String getWidgetName() {
0650: return "repeater";
0651: }
0652:
0653: public Handler startElement(String uri, String loc, String raw,
0654: Attributes attrs) throws SAXException {
0655: setTypedWidget(loc, attrs, getWidgetClass(),
0656: getWidgetName());
0657: if (!isVisible(widget)) {
0658: return hNull;
0659: }
0660:
0661: contextWidgets.addFirst(contextWidget);
0662: contextWidget = widget;
0663: return this ;
0664: }
0665:
0666: public void endElement(String uri, String loc, String raw)
0667: throws SAXException {
0668: contextWidget = (Widget) contextWidgets.removeFirst();
0669: }
0670: }
0671:
0672: /**
0673: * Handles <code>ft:repeater-rows</code> element.
0674: */
0675: protected class RepeaterRowsHandler extends BufferHandler {
0676: public Handler startElement(String uri, String loc, String raw,
0677: Attributes attrs) throws SAXException {
0678: if (!(contextWidget instanceof Repeater)) {
0679: throw new SAXException(
0680: "<repeater-rows> cannot be used with "
0681: + contextWidget + ", at "
0682: + getLocation());
0683: }
0684: beginBuffer();
0685: return this ;
0686: }
0687:
0688: public Handler nestedElement(String uri, String loc,
0689: String raw, Attributes attrs) throws SAXException {
0690: return hBuffer;
0691: }
0692:
0693: public void endElement(String uri, String loc, String raw)
0694: throws SAXException {
0695: SaxBuffer buffer = endBuffer();
0696: final Repeater repeater = (Repeater) contextWidget;
0697: final int rowCount = repeater.getSize();
0698: pushHandler(hNested);
0699: contextWidgets.addFirst(contextWidget);
0700: for (int i = 0; i < rowCount; i++) {
0701: contextWidget = repeater.getRow(i);
0702: if (isVisible(contextWidget)) {
0703: buffer.toSAX(EffectWidgetReplacingPipe.this );
0704: }
0705: }
0706: contextWidget = (Widget) contextWidgets.removeFirst();
0707: popHandler();
0708: widget = null;
0709: }
0710: }
0711:
0712: /**
0713: * Handles <code>ft:repeater-widget</code> element: a single element for both the repeater and its rows
0714: */
0715: protected class RepeaterWidgetHandler extends BufferHandler {
0716: public Handler startElement(String uri, String loc, String raw,
0717: Attributes attrs) throws SAXException {
0718: setTypedWidget(loc, attrs, Repeater.class, "repeater");
0719: if (isVisible(widget)) {
0720: beginBuffer();
0721: return this ;
0722: }
0723: return hNull;
0724: }
0725:
0726: public Handler nestedElement(String uri, String loc,
0727: String raw, Attributes attrs) throws SAXException {
0728: return hBuffer;
0729: }
0730:
0731: public void endElement(String uri, String loc, String raw)
0732: throws SAXException {
0733: SaxBuffer buffer = endBuffer();
0734: final Repeater repeater = (Repeater) widget;
0735: final int rowCount = repeater.getSize();
0736: pushHandler(hNested);
0737: contextWidgets.addFirst(contextWidget);
0738: for (int i = 0; i < rowCount; i++) {
0739: contextWidget = repeater.getRow(i);
0740: if (isVisible(contextWidget)) {
0741: buffer.toSAX(EffectWidgetReplacingPipe.this );
0742: }
0743: }
0744: contextWidget = (Widget) contextWidgets.removeFirst();
0745: popHandler();
0746: widget = null;
0747: }
0748: }
0749:
0750: //
0751: // Grouping widgets Handlers
0752: //
0753:
0754: /**
0755: * Handles <code>ft:group</code> element.
0756: */
0757: protected class GroupHandler extends NestedHandler {
0758: protected Class getWidgetClass() {
0759: return Group.class;
0760: }
0761:
0762: protected String getWidgetName() {
0763: return "group";
0764: }
0765:
0766: public Handler startElement(String uri, String loc, String raw,
0767: Attributes attrs) throws SAXException {
0768: setTypedWidget(loc, attrs, getWidgetClass(),
0769: getWidgetName());
0770: if (!isVisible(widget)) {
0771: return hNull;
0772: }
0773:
0774: contextWidgets.addFirst(contextWidget);
0775: contextWidget = widget;
0776: return this ;
0777: }
0778:
0779: public void endElement(String uri, String loc, String raw)
0780: throws SAXException {
0781: contextWidget = (Widget) contextWidgets.removeFirst();
0782: }
0783: }
0784:
0785: /**
0786: * Handles <code>ft:aggregate</code> element.
0787: */
0788: protected class AggregateWidgetHandler extends GroupHandler {
0789: protected Class getWidgetClass() {
0790: return AggregateField.class;
0791: }
0792:
0793: protected String getWidgetName() {
0794: return "aggregate";
0795: }
0796: }
0797:
0798: /**
0799: * Handles <code>ft:choose</code> element.
0800: */
0801: protected class ChooseHandler extends CopyHandler {
0802: public Handler startElement(String uri, String loc, String raw,
0803: Attributes attrs) throws SAXException {
0804: setWidget(loc,
0805: getRequiredAttributeValue(loc, attrs, "path"));
0806: // TODO: Should instead check for datatype convertable to String.
0807: if (!(widget instanceof DataWidget)) {
0808: throw new SAXException(
0809: "Element '"
0810: + loc
0811: + "' can only be used with DataWidget widgets, "
0812: + "at " + getLocation());
0813: }
0814: // Choose does not change the context widget like Union does.
0815: chooseWidgets.addFirst(widget);
0816: return this ;
0817: }
0818:
0819: public Handler nestedElement(String uri, String loc,
0820: String raw, Attributes attrs) throws SAXException {
0821: if (FormsConstants.TEMPLATE_NS.equals(uri)) {
0822: if ("when".equals(loc)) {
0823: String testValue = getAttributeValue(loc, attrs,
0824: "value");
0825: String value = (String) ((Widget) chooseWidgets
0826: .get(0)).getValue();
0827: return testValue.equals(value) ? hSkip : hNull;
0828: }
0829: throw new SAXException("Element '" + loc
0830: + "' is not permitted within 'choose', "
0831: + "at " + getLocation());
0832: }
0833: return hChoosePassThru;
0834: }
0835:
0836: public void endElement(String uri, String loc, String raw)
0837: throws SAXException {
0838: chooseWidgets.removeFirst();
0839: }
0840: }
0841:
0842: /**
0843: * Handles <code>ft:choose/ft:when</code> element.
0844: */
0845: protected class ChoosePassThruHandler extends CopyHandler {
0846: public Handler nestedElement(String uri, String loc,
0847: String raw, Attributes attrs) throws SAXException {
0848: if (FormsConstants.TEMPLATE_NS.equals(uri)) {
0849: if ("when".equals(loc)) {
0850: String testValue = getAttributeValue(loc, attrs,
0851: "value");
0852: String value = (String) ((Widget) chooseWidgets
0853: .get(0)).getValue();
0854: return testValue.equals(value) ? hSkip : hNull;
0855: }
0856: throw new SAXException("Element '" + loc
0857: + "' is not permitted within 'choose', "
0858: + "at " + getLocation());
0859: }
0860: return this ;
0861: }
0862: }
0863:
0864: /**
0865: * Handles <code>ft:struct</code> element.
0866: */
0867: protected class StructHandler extends GroupHandler {
0868: protected Class getWidgetClass() {
0869: return Struct.class;
0870: }
0871:
0872: protected String getWidgetName() {
0873: return "struct";
0874: }
0875: }
0876:
0877: /**
0878: * Handles <code>ft:union</code> element.
0879: */
0880: protected class UnionHandler extends GroupHandler {
0881: protected Class getWidgetClass() {
0882: return Union.class;
0883: }
0884:
0885: protected String getWidgetName() {
0886: return "union";
0887: }
0888:
0889: public Handler nestedElement(String uri, String loc,
0890: String raw, Attributes attrs) throws SAXException {
0891: if (FormsConstants.TEMPLATE_NS.equals(uri)) {
0892: if ("case".equals(loc)) {
0893: String id = getAttributeValue(loc, attrs, "id");
0894: String value = (String) contextWidget.getValue();
0895: if (id.equals(value != null ? value : "")) {
0896: return hSkip;
0897: }
0898: return hNull;
0899: }
0900: throw new SAXException("Element '" + loc
0901: + "' is not permitted within 'union', " + "at "
0902: + getLocation());
0903: }
0904: return hUnionPassThru;
0905: }
0906: }
0907:
0908: /**
0909: * Handles <code>ft:union/ft:case</code> element.
0910: */
0911: protected class UnionPassThruHandler extends CopyHandler {
0912: public Handler nestedElement(String uri, String loc,
0913: String raw, Attributes attrs) throws SAXException {
0914: if (FormsConstants.TEMPLATE_NS.equals(uri)) {
0915: if ("case".equals(loc)) {
0916: if (contextWidget.getValue().equals(
0917: attrs.getValue("id"))) {
0918: return hSkip;
0919: }
0920: return hNull;
0921: }
0922: throw new SAXException("Element '" + loc
0923: + "' is not permitted within 'union', " + "at "
0924: + getLocation());
0925: }
0926: return this ;
0927: }
0928: }
0929:
0930: /**
0931: * Handles <code>ft:new</code> element.
0932: */
0933: protected class NewHandler extends CopyHandler {
0934: public Handler startElement(String uri, String loc, String raw,
0935: Attributes attrs) throws SAXException {
0936: String id = getRequiredAttributeValue(loc, attrs, "id");
0937: SaxBuffer buffer = (SaxBuffer) classes.get(id);
0938: if (buffer == null) {
0939: throw new SAXException("New: Class '" + id
0940: + "' does not exist, " + "at " + getLocation());
0941: }
0942: pushHandler(hNested);
0943: buffer.toSAX(EffectWidgetReplacingPipe.this );
0944: popHandler();
0945: return this ;
0946: }
0947:
0948: public Handler nestedElement(String uri, String loc,
0949: String raw, Attributes attrs) throws SAXException {
0950: return hNull;
0951: }
0952:
0953: public void endElement(String uri, String loc, String raw)
0954: throws SAXException {
0955: }
0956: }
0957:
0958: /**
0959: * Handles <code>ft:class</code> element.
0960: * <pre>
0961: * <ft:class id="...">
0962: * ...
0963: * </ft:class>
0964: * </pre>
0965: */
0966: protected class ClassHandler extends BufferHandler {
0967: // FIXME What if <class> is nested within <class>?
0968: private String widgetPath;
0969:
0970: public Handler startElement(String uri, String loc, String raw,
0971: Attributes attrs) throws SAXException {
0972: widgetPath = getRequiredAttributeValue(loc, attrs, "id");
0973: beginBuffer();
0974: return this ;
0975: }
0976:
0977: public Handler nestedElement(String uri, String loc,
0978: String raw, Attributes attrs) throws SAXException {
0979: return hBuffer;
0980: }
0981:
0982: public void endElement(String uri, String loc, String raw)
0983: throws SAXException {
0984: classes.put(widgetPath, endBuffer());
0985: }
0986: }
0987:
0988: /**
0989: * Handles <code>ft:continuation-id</code> element.
0990: * <pre>
0991: * <ft:continuation-id/>
0992: * </pre>
0993: */
0994: protected class ContinuationIdHandler extends ErrorHandler {
0995: protected String getName() {
0996: return "continuation-id";
0997: }
0998:
0999: public Handler startElement(String uri, String loc, String raw,
1000: Attributes attrs) throws SAXException {
1001: // Insert the continuation id
1002: // FIXME(SW) we could avoid costly JXPath evaluation if we had the objectmodel here.
1003: Object idObj = pipeContext
1004: .evaluateExpression("$cocoon/continuation/id");
1005: if (idObj == null) {
1006: throw new SAXException("No continuation found");
1007: }
1008:
1009: String id = idObj.toString();
1010: getContentHandler().startElement(
1011: FormsConstants.INSTANCE_NS,
1012: "continuation-id",
1013: FormsConstants.INSTANCE_PREFIX_COLON
1014: + "continuation-id", attrs);
1015: getContentHandler().characters(id.toCharArray(), 0,
1016: id.length());
1017: getContentHandler().endElement(
1018: FormsConstants.INSTANCE_NS,
1019: "continuation-id",
1020: FormsConstants.INSTANCE_PREFIX_COLON
1021: + "continuation-id");
1022: return this ;
1023: }
1024:
1025: public void endElement(String uri, String loc, String raw)
1026: throws SAXException {
1027: }
1028: }
1029:
1030: /**
1031: * This ContentHandler helps in inserting SAX events before the closing tag of the root
1032: * element.
1033: */
1034: protected class StylingContentHandler extends AbstractXMLPipe {
1035:
1036: private int elementNesting;
1037: private SaxBuffer styling;
1038:
1039: public void setSaxFragment(SaxBuffer saxFragment) {
1040: styling = saxFragment;
1041: }
1042:
1043: public void recycle() {
1044: super .recycle();
1045: elementNesting = 0;
1046: styling = null;
1047: }
1048:
1049: public void startElement(String uri, String loc, String raw,
1050: Attributes a) throws SAXException {
1051: elementNesting++;
1052: super .startElement(uri, loc, raw, a);
1053: }
1054:
1055: public void endElement(String uri, String loc, String raw)
1056: throws SAXException {
1057: elementNesting--;
1058: if (elementNesting == 0) {
1059: styling.toSAX(getContentHandler());
1060: }
1061: super .endElement(uri, loc, raw);
1062: }
1063: }
1064:
1065: /**
1066: * Inserts validation errors (if any) for the Field widgets
1067: */
1068: protected class ValidationErrorHandler extends NullHandler {
1069: public Handler startElement(String uri, String loc, String raw,
1070: Attributes attrs) throws SAXException {
1071: setWidget(loc, attrs);
1072: return this ;
1073: }
1074:
1075: public Handler nestedElement(String uri, String loc,
1076: String raw, Attributes attrs) throws SAXException {
1077: return hNull;
1078: }
1079:
1080: public void endElement(String uri, String loc, String raw)
1081: throws SAXException {
1082: if (widget instanceof ValidationErrorAware) {
1083: ValidationError error = ((ValidationErrorAware) widget)
1084: .getValidationError();
1085: if (error != null) {
1086: getContentHandler().startElement(
1087: FormsConstants.INSTANCE_NS,
1088: VALIDATION_ERROR,
1089: FormsConstants.INSTANCE_PREFIX_COLON
1090: + VALIDATION_ERROR,
1091: XMLUtils.EMPTY_ATTRIBUTES);
1092: error.generateSaxFragment(getContentHandler());
1093: getContentHandler().endElement(
1094: FormsConstants.INSTANCE_NS,
1095: VALIDATION_ERROR,
1096: FormsConstants.INSTANCE_PREFIX_COLON
1097: + VALIDATION_ERROR);
1098: }
1099: }
1100: widget = null;
1101: }
1102: }
1103:
1104: private Attributes translateAttributes(Attributes attributes,
1105: String[] names) {
1106: AttributesImpl newAtts = new AttributesImpl(attributes);
1107: if (names != null) {
1108: for (int i = 0; i < names.length; i++) {
1109: String name = names[i];
1110: int position = newAtts.getIndex(name);
1111: String newValue = pipeContext.translateText(newAtts
1112: .getValue(position));
1113: if (position > -1)
1114: newAtts.setValue(position, newValue);
1115: else
1116: throw new RuntimeException("Attribute \"" + name
1117: + "\" not present!");
1118: }
1119: }
1120: return newAtts;
1121: }
1122: }
|