Return-Path: Delivered-To: apmail-cocoon-cvs-archive@www.apache.org Received: (qmail 31508 invoked from network); 3 Nov 2005 14:48:02 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 3 Nov 2005 14:48:02 -0000 Received: (qmail 44682 invoked by uid 500); 3 Nov 2005 14:40:45 -0000 Delivered-To: apmail-cocoon-cvs-archive@cocoon.apache.org Received: (qmail 36640 invoked by uid 500); 3 Nov 2005 14:39:25 -0000 Mailing-List: contact cvs-help@cocoon.apache.org; run by ezmlm Precedence: bulk Reply-To: dev@cocoon.apache.org list-help: list-unsubscribe: List-Post: List-Id: Delivered-To: mailing list cvs@cocoon.apache.org Received: (qmail 29826 invoked by uid 99); 3 Nov 2005 14:38:22 -0000 X-ASF-Spam-Status: No, hits=-9.4 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received: from [209.237.227.194] (HELO minotaur.apache.org) (209.237.227.194) by apache.org (qpsmtpd/0.29) with SMTP; Thu, 03 Nov 2005 06:35:59 -0800 Received: (qmail 13248 invoked by uid 65534); 3 Nov 2005 14:35:39 -0000 Message-ID: <20051103143539.13247.qmail@minotaur.apache.org> Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r330548 [101/132] - in /cocoon/whiteboard/maven2/cocoon-flat-layout: ./ cocoon-ajax-block/ cocoon-ajax-block/api/ cocoon-ajax-block/api/src/ cocoon-ajax-block/api/src/main/ cocoon-ajax-block/api/src/main/java/ cocoon-ajax-block/api/src/main... Date: Thu, 03 Nov 2005 14:00:48 -0000 To: cvs@cocoon.apache.org From: jheymans@apache.org X-Mailer: svnmailer-1.0.5 X-Virus-Checked: Checked by ClamAV on apache.org X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/generation/JXMacrosHelper.java URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/generation/JXMacrosHelper.java?rev=330548&view=auto ============================================================================== --- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/generation/JXMacrosHelper.java (added) +++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/generation/JXMacrosHelper.java Thu Nov 3 05:41:06 2005 @@ -0,0 +1,486 @@ +/* + * Copyright 1999-2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.cocoon.forms.generation; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import org.apache.cocoon.ajax.BrowserUpdateTransformer; +import org.apache.cocoon.environment.Request; +import org.apache.cocoon.forms.FormsConstants; +import org.apache.cocoon.forms.FormsRuntimeException; +import org.apache.cocoon.forms.formmodel.Form; +import org.apache.cocoon.forms.formmodel.Repeater; +import org.apache.cocoon.forms.formmodel.Widget; +import org.apache.cocoon.forms.formmodel.tree.Tree; +import org.apache.cocoon.forms.formmodel.tree.TreeWalker; +import org.apache.cocoon.forms.validation.ValidationError; +import org.apache.cocoon.i18n.I18nUtils; +import org.apache.cocoon.xml.AbstractXMLPipe; +import org.apache.cocoon.xml.AttributesImpl; +import org.apache.cocoon.xml.XMLConsumer; +import org.apache.cocoon.xml.XMLUtils; +import org.apache.commons.collections.ArrayStack; +import org.apache.commons.lang.BooleanUtils; +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; + +/** + * Helper class for the implementation of the CForms template language with JXTemplate macros. + * + * @version $Id$ + */ +public class JXMacrosHelper { + + private XMLConsumer cocoonConsumer; + private Request request; + private Locale locale; + private ArrayStack widgetStack = new ArrayStack(); + private ArrayStack pipeStack = new ArrayStack(); + private Map classes; // lazily created + private boolean ajaxRequest; + private boolean ajaxTemplate; + private Set updatedWidgets; + private Set childUpdatedWidgets; + + /** + * Builds and helper object, given the generator's consumer. + * + * @param consumer the generator's consumer + * @return a helper object + */ + public static JXMacrosHelper createHelper(XMLConsumer consumer, Request request, String locale) { + return new JXMacrosHelper(consumer, request, locale); + } + + public JXMacrosHelper(XMLConsumer consumer, Request request, String locale) { + this.cocoonConsumer = consumer; + this.request = request; + this.locale = I18nUtils.parseLocale(locale); + this.ajaxRequest = request.getParameter("cocoon-ajax") != null; + } + + public Form getForm(Form form, String attributeName) { + Form returnForm = form; + // if there hasn't been passed a form object, try to find it in the request + if(returnForm == null) { + returnForm = (Form) this.request.getAttribute(attributeName); + } + if(returnForm != null) { + return returnForm; + } + throw new FormsRuntimeException("The template cannot find a form object"); + } + + public void startForm(Form form, Map attributes) throws SAXException { + + this.updatedWidgets = form.getUpdatedWidgetIds(); + this.childUpdatedWidgets = form.getChildUpdatedWidgetIds(); + + // build attributes + AttributesImpl attrs = new AttributesImpl(); + Iterator iter = attributes.entrySet().iterator(); + while(iter.hasNext()) { + Map.Entry entry = (Map.Entry)iter.next(); + attrs.addCDATAAttribute((String)entry.getKey(), (String)entry.getValue()); + } + + this.ajaxTemplate = "true".equals(attributes.get("ajax")); + + this.cocoonConsumer.startPrefixMapping(FormsConstants.INSTANCE_PREFIX, FormsConstants.INSTANCE_NS); + this.cocoonConsumer.startElement(FormsConstants.INSTANCE_NS, + "form-template", + FormsConstants.INSTANCE_PREFIX_COLON + "form-template", + attrs); + // Push the form at the top of the stack + this.widgetStack.push(Boolean.FALSE); // Not in an updated template + this.widgetStack.push(form); + } + + public void endForm() throws SAXException { + this.widgetStack.pop(); + this.widgetStack.pop(); + this.cocoonConsumer.endElement(FormsConstants.INSTANCE_NS, + "form-template", + FormsConstants.INSTANCE_PREFIX_COLON + "form-template"); + this.cocoonConsumer.endPrefixMapping(FormsConstants.INSTANCE_PREFIX); + + this.ajaxTemplate = false; + this.updatedWidgets = null; + } + + private void startBuReplace(String id) throws SAXException { + AttributesImpl attr = new AttributesImpl(); + attr.addCDATAAttribute("id", id); + this.cocoonConsumer.startElement(BrowserUpdateTransformer.BU_NSURI, "replace", "bu:replace", attr); + } + + private void endBuReplace(String id) throws SAXException { + this.cocoonConsumer.endElement(BrowserUpdateTransformer.BU_NSURI, "replace", "bu:replace"); + } + + protected boolean pushWidget(String path, boolean unused) throws SAXException { + Widget parent = peekWidget(); + if (path == null || path.length() == 0) { + throw new FormsRuntimeException("Missing 'id' attribute on template instruction"); + } + Widget widget = parent.lookupWidget(path); + if (widget == null) { + throw new FormsRuntimeException(parent + " has no child named '" + path + "'", parent.getLocation()); + } + + String id = widget.getFullName(); + // Is there an updated widget at a higher level in the template? + boolean inUpdatedTemplate = ((Boolean)widgetStack.peek(1)).booleanValue(); + + boolean display; + + if (ajaxRequest) { + // An Ajax request. We will send partial updates + if (inUpdatedTemplate) { + // A parent widget has been updated: redisplay this one also + display = true; + } else if (this.updatedWidgets.contains(id)) { + // Widget has been updated. We are now in an updated template section, + // and widgets have to be surrounded with + inUpdatedTemplate = true; + display = true; + } else if (this.childUpdatedWidgets.contains(id)) { + // A child need to be updated + display = true; + } else { + // Doesn't need to be displayed + display = false; + } + } else { + // Not an ajax request + if (ajaxTemplate) { + // Surround all widgets with , which the bu tranformer will use to check structure + // consistency and add an id attribute to its child elements. + inUpdatedTemplate = true; + } + // Display the widget + display = true; + } + + if (display) { + // Widget needs to be displayed, but does it actually allows it? + if (widget.getState().isDisplayingValues()) { + if (inUpdatedTemplate) { + // Updated part of an Ajax template: surround with + startBuReplace(id); + } + } else { + if (ajaxTemplate) { + // Generate a placeholder, so that the page can be updated later + startBuReplace(id); + AttributesImpl attrs = new AttributesImpl(); + attrs.addCDATAAttribute("id", id); + this.cocoonConsumer.startElement(FormsConstants.INSTANCE_NS, "placeholder", FormsConstants.INSTANCE_PREFIX_COLON + "placeholder", attrs); + this.cocoonConsumer.endElement(FormsConstants.INSTANCE_NS, "placeholder", FormsConstants.INSTANCE_PREFIX_COLON + "placeholder"); + endBuReplace(id); + } + // Production finished for this widget + display = false; + } + } + + if (display) { + this.widgetStack.push(BooleanUtils.toBooleanObject(inUpdatedTemplate)); + this.widgetStack.push(widget); + } + + return display; + } + + public Widget peekWidget() { + return (Widget)this.widgetStack.peek(); + } + + public void popWidget() throws SAXException { + Widget widget = (Widget)this.widgetStack.pop(); + boolean inUpdatedTemplate = ((Boolean)this.widgetStack.pop()).booleanValue(); + + if (inUpdatedTemplate) { + // Close the bu:replace + endBuReplace(widget.getFullName()); + } + } + + public boolean pushWidget(String path) throws SAXException { + return pushWidget(path, false); + } + + public boolean pushContainer(String path) throws SAXException { + return pushWidget(path, true); + } + + /** + * Enter a repeater + * + * @param path widget path + * @param ajaxAware distinguishes between <ft:repeater-widget> and <ft:repeater>. + * @return true if the repeater template is to be executed + * @throws SAXException + */ + public boolean pushRepeater(String path, boolean ajaxAware) throws SAXException { + if (!ajaxAware && this.ajaxTemplate) { + throw new IllegalStateException("Cannot use in an Ajax form"); + } + boolean result = pushWidget(path, true); + if (result && !(peekWidget() instanceof Repeater)) { + throw new IllegalArgumentException("Widget " + peekWidget() + " is not a repeater"); + } + return result; + } + + /** + * Get a child widget of a given widget, throwing an exception if no such child exists. + * + * @param currentWidget + * @param id + */ + public Widget getWidget(Widget currentWidget, String path) { + Widget result = currentWidget.lookupWidget(path); + + if (result != null) { + return result; + } + throw new FormsRuntimeException(currentWidget + " has no child named '" + path + "'", currentWidget.getLocation()); + } + + private Repeater getRepeater(Widget currentWidget, String id) { + Widget child = getWidget(currentWidget, id); + if (child instanceof Repeater) { + return (Repeater)child; + } + throw new FormsRuntimeException(child + " is not a repeater", child.getLocation()); + } + + /** + * Generate a widget's SAX fragment, buffering the root element's endElement() + * event so that the template can insert styling information in it. + * + * @param widget + * @param locale + * @throws SAXException + */ + public void generateWidget(Widget widget, Map arguments) throws SAXException { + // Needs to be buffered + RootBufferingPipe pipe = new RootBufferingPipe(this.cocoonConsumer, arguments); + this.pipeStack.push(pipe); + widget.generateSaxFragment(pipe, this.locale); + } + + /** + * Flush the root element name that has been stored in + * {@link #generateWidget(Widget, Locale)}. + * + * @param obj the object that is terminated (widget or validation error) + * @throws SAXException + */ + public void flushRootAndPop() throws SAXException { + ((RootBufferingPipe) pipeStack.pop()).flushRoot(); + popWidget(); + } + + public void flushRoot() throws SAXException { + ((RootBufferingPipe) pipeStack.pop()).flushRoot(); + } + + public void generateWidgetLabel(Widget widget, String id) throws SAXException { + getWidget(widget, id).generateLabel(this.cocoonConsumer); + } + + public void generateRepeaterWidgetLabel(Widget widget, String id, String widgetId) throws SAXException { + // Widget labels are allowed either inside or outside of + Repeater repeater = widget instanceof Repeater ? (Repeater)widget : getRepeater(widget, id); + repeater.generateWidgetLabel(widgetId, this.cocoonConsumer); + } + + public void generateRepeaterSize(Widget widget, String id) throws SAXException { + getRepeater(widget, id).generateSize(this.cocoonConsumer); + } + + private static final String VALIDATION_ERROR = "validation-error"; + + public void generateValidationError(ValidationError error) throws SAXException { + // Needs to be buffered + RootBufferingPipe pipe = new RootBufferingPipe(this.cocoonConsumer); + this.pipeStack.push(pipe); + pipe.startElement(FormsConstants.INSTANCE_NS, VALIDATION_ERROR, FormsConstants.INSTANCE_PREFIX_COLON + VALIDATION_ERROR, XMLUtils.EMPTY_ATTRIBUTES); + error.generateSaxFragment(pipe); + pipe.endElement(FormsConstants.INSTANCE_NS, VALIDATION_ERROR, FormsConstants.INSTANCE_PREFIX_COLON + VALIDATION_ERROR); + } + + public boolean isValidationError(Object object) { + return object instanceof ValidationError; + } + + public void defineClassBody(Form form, String id, Object body) { + // TODO: check that class actually exists in the form + if (this.classes == null) { + this.classes = new HashMap(); + } + + // TODO: check if class doesn't already exist? + this.classes.put(id, body); + } + + public Object getClassBody(String id) { + Object result = this.classes == null ? null : this.classes.get(id); + + if (result == null) { + throw new FormsRuntimeException("No class '" + id + "' has been defined."); + } + return result; + } + + public boolean isSelectedCase(Widget unionWidget, String caseValue) { + String value = (String)unionWidget.getValue(); + return caseValue.equals(value != null ? value : ""); + } + + public TreeWalker createWalker() { + return new TreeWalker((Tree)peekWidget()); + } + + public boolean isVisible(Widget widget) throws SAXException { + boolean visible = widget.getCombinedState().isDisplayingValues(); + + if (!visible) { + // Generate a placeholder it not visible + String id = widget.getRequestParameterName(); + AttributesImpl attrs = new AttributesImpl(); + attrs.addCDATAAttribute("id", id); + this.cocoonConsumer.startElement(BrowserUpdateTransformer.BU_NSURI, "replace", "bu:replace", attrs); + this.cocoonConsumer.startElement(FormsConstants.INSTANCE_NS, "placeholder", FormsConstants.INSTANCE_PREFIX_COLON + "placeholder", attrs); + this.cocoonConsumer.endElement(FormsConstants.INSTANCE_NS, "placeholder", FormsConstants.INSTANCE_PREFIX_COLON + "placeholder"); + this.cocoonConsumer.endElement(BrowserUpdateTransformer.BU_NSURI, "replace", "bu:replace"); + } + + return visible; + } + + public boolean isModified(Widget widget) { + return this.updatedWidgets.contains(widget.getRequestParameterName()); + } + + public boolean generateStyling(Map attributes) throws SAXException { + return generateStyling(this.cocoonConsumer, attributes); + } + + /** + * Generate a <fi:styling> element holding the attributes of a ft:* + * element that are in the "fi:" namespace. + * + * @param attributes the template instruction attributes + * @return true if a <fi:styling> was produced + * @throws SAXException + */ + public static boolean generateStyling(ContentHandler handler, Map attributes) throws SAXException { + AttributesImpl attr = null; + Iterator entries = attributes.entrySet().iterator(); + while(entries.hasNext()) { + Map.Entry entry = (Map.Entry)entries.next(); + String key = (String)entry.getKey(); + + // FIXME: JXTG only gives the local name of attributes, so we can't distinguish namespaces... + if (!"id".equals(key) && !"widget-id".equals(key)) { + if (attr == null) + attr = new AttributesImpl(); + attr.addCDATAAttribute(key, (String)entry.getValue()); + } + } + + if (attr != null) { + // There were some styling attributes + handler.startElement(FormsConstants.INSTANCE_NS, "styling", FormsConstants.INSTANCE_PREFIX_COLON + "styling", attr); + handler.endElement(FormsConstants.INSTANCE_NS, "styling", FormsConstants.INSTANCE_PREFIX_COLON + "styling"); + return true; + } else { + return false; + } + } + + /** + * A SAX pipe that buffers the endElement() event of the root element. + * This is needed by the generator version of the FormsTransformer (see jx-macros.xml). + * + * @version $Id$ + */ + private static class RootBufferingPipe extends AbstractXMLPipe { + private int depth = 0; + + private String rootUri; + private String rootLoc; + private String rootRaw; + private Map arguments; + private boolean forbidStyling = false; + + public RootBufferingPipe(XMLConsumer next) { + this(next, Collections.EMPTY_MAP); + } + + public RootBufferingPipe(XMLConsumer next, Map arguments) { + this.setConsumer(next); + this.arguments = arguments; + } + + public void startElement(String uri, String loc, String raw, Attributes a) + throws SAXException { + super.startElement(uri, loc, raw, a); + if (depth == 0) { + // Root element: keep its description + this.rootUri = uri; + this.rootLoc = loc; + this.rootRaw = raw; + + // And produce fi:styling from attributes + this.forbidStyling = generateStyling(this.contentHandler, arguments); + } + + if (depth == 1 && forbidStyling && + uri.equals(FormsConstants.INSTANCE_NS) && loc.equals("styling")) { + throw new SAXException("Cannot use 'fi:*' attributes and at the same time"); + } + + depth++; + } + + public void endElement(String uri, String loc, String raw) + throws SAXException { + depth--; + if (depth > 0) { + // Propagate all but root element + super.endElement(uri, loc, raw); + } + } + + public void flushRoot() throws SAXException { + if (depth != 0) { + throw new IllegalStateException("Depth is not zero"); + } + super.endElement(this.rootUri, this.rootLoc, this.rootRaw); + } + } +} Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/generation/JXMacrosHelper.java ------------------------------------------------------------------------------ svn:eol-style = native Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/generation/SelectionListFilter.java URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/generation/SelectionListFilter.java?rev=330548&view=auto ============================================================================== --- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/generation/SelectionListFilter.java (added) +++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/generation/SelectionListFilter.java Thu Nov 3 05:41:06 2005 @@ -0,0 +1,69 @@ +/* + * Copyright 2005 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.cocoon.forms.generation; + +import org.apache.cocoon.forms.FormsConstants; +import org.apache.cocoon.xml.AbstractXMLPipe; +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +/** + * A filter for selection lists, that keeps only those items that start with a given filter value. + * + * @since 2.1.8 + * @version $Id: SelectionListFilter.java 326838 2005-10-20 06:26:53Z sylvain $ + */ +public class SelectionListFilter extends AbstractXMLPipe { + + private ContentHandler next; + private int filterDepth = 0; + private int depth = 0; + private String filterValue; + private static final ContentHandler NULL_HANDLER = new DefaultHandler(); + + public SelectionListFilter(String filterValue, ContentHandler next) { + this.next = next; + this.setContentHandler(next); + this.filterValue = filterValue; + } + + public void startElement(String uri, String loc, String raw, Attributes a) throws SAXException { + depth++; + + if (uri.equals(FormsConstants.INSTANCE_NS) && loc.equals("item")) { + String value = a.getValue("value"); + if (!value.startsWith(this.filterValue)) { + filterDepth = depth; + setContentHandler(NULL_HANDLER); + } + } + + super.startElement(uri, loc, raw, a); + } + + public void endElement(String uri, String loc, String raw) throws SAXException { + super.endElement(uri, loc, raw); + + if (depth == filterDepth) { + filterDepth = 0; + setContentHandler(this.next); + } + + depth--; + } +} Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/generation/SelectionListFilter.java ------------------------------------------------------------------------------ svn:eol-style = native Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/generation/SuggestionListGenerator.java URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/generation/SuggestionListGenerator.java?rev=330548&view=auto ============================================================================== --- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/generation/SuggestionListGenerator.java (added) +++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/generation/SuggestionListGenerator.java Thu Nov 3 05:41:06 2005 @@ -0,0 +1,106 @@ +/* + * Copyright 2005 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.cocoon.forms.generation; + +import java.io.IOException; +import java.util.Locale; +import java.util.Map; + +import org.apache.avalon.framework.parameters.Parameters; +import org.apache.avalon.framework.service.ServiceException; +import org.apache.avalon.framework.service.ServiceManager; +import org.apache.cocoon.ProcessingException; +import org.apache.cocoon.components.flow.ContinuationsManager; +import org.apache.cocoon.components.flow.InvalidContinuationException; +import org.apache.cocoon.components.flow.WebContinuation; +import org.apache.cocoon.environment.ObjectModelHelper; +import org.apache.cocoon.environment.Request; +import org.apache.cocoon.environment.SourceResolver; +import org.apache.cocoon.forms.FormsConstants; +import org.apache.cocoon.forms.datatype.SelectionList; +import org.apache.cocoon.forms.formmodel.Field; +import org.apache.cocoon.forms.formmodel.Form; +import org.apache.cocoon.generation.ServiceableGenerator; +import org.apache.cocoon.sitemap.SitemapParameters; +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; + +/** + * A generator for suggestion lists. + * + * @since 2.1.8 + * @version $Id: SuggestionListGenerator.java 326838 2005-10-20 06:26:53Z sylvain $ + */ +public class SuggestionListGenerator extends ServiceableGenerator { + + private ContinuationsManager contManager; + private WebContinuation wk; + private SelectionList list; + private String filter; + private Locale locale; + + public void service(ServiceManager manager) throws ServiceException { + super.service(manager); + this.contManager = (ContinuationsManager)manager.lookup(ContinuationsManager.ROLE); + } + + public void setup(SourceResolver resolver, Map objectModel, String src, Parameters par) throws ProcessingException, SAXException, IOException { + super.setup(resolver, objectModel, src, par); + + Request req = ObjectModelHelper.getRequest(objectModel); + + String continuationId = par.getParameter("continuation-id", req.getParameter("continuation-id")); + String widgetPath = par.getParameter("widget-id", req.getParameter("widget-id")).replace('.', '/'); + String widgetId = widgetPath.replace('/', '.'); + this.filter = par.getParameter("filter", req.getParameter(widgetId)); + + // The interpreter id is the sitemap's URI + String interpreterId = SitemapParameters.getLocation(parameters).getURI(); + wk = this.contManager.lookupWebContinuation(continuationId, interpreterId); + if (wk == null || wk.disposed()) { + throw new InvalidContinuationException("Cannot get continuation for suggestion list"); + } + + Form form = (Form)wk.getAttribute("form"); + if (form == null) { + throw new ProcessingException("No form is attached to the continuation"); + } + + this.locale = form.getLocale(); + + Field field = (Field)form.lookupWidget(widgetPath); + list = field.getSuggestionList(); + if (list == null) { + throw new ProcessingException(field + " has no suggestion list"); + } + } + + public void generate() throws IOException, SAXException, ProcessingException { + super.contentHandler.startDocument(); + super.contentHandler.startPrefixMapping(FormsConstants.INSTANCE_PREFIX, FormsConstants.INSTANCE_NS); + ContentHandler handler; + if (filter == null || filter.length() == 0) { + handler = super.contentHandler; + } else { + handler = new SelectionListFilter(filter, super.contentHandler); + } + list.generateSaxFragment(handler, this.locale); + + super.contentHandler.endPrefixMapping(FormsConstants.INSTANCE_PREFIX); + super.contentHandler.endDocument(); + } + +} Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/generation/SuggestionListGenerator.java ------------------------------------------------------------------------------ svn:eol-style = native Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/transformation/EffectPipe.java URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/transformation/EffectPipe.java?rev=330548&view=auto ============================================================================== --- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/transformation/EffectPipe.java (added) +++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/transformation/EffectPipe.java Thu Nov 3 05:41:06 2005 @@ -0,0 +1,585 @@ +/* + * Copyright 1999-2005 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.cocoon.forms.transformation; + +import org.apache.cocoon.xml.AbstractXMLPipe; +import org.apache.cocoon.xml.SaxBuffer; + +import org.xml.sax.Attributes; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.LocatorImpl; + +import java.util.LinkedList; + +/** + * Base class for XMLPipe's. Allows the structure of the source code of + * the XMLPipe to match the structure of the data being transformed. + * + * @version $Id: EffectPipe.java 289538 2005-09-16 13:46:22Z sylvain $ + */ +public class EffectPipe extends AbstractXMLPipe { + + /** + * Handler interface. Accepts SAX events, can return other handler + * to replace self for further events. + */ + protected interface Handler { + public Handler startDocument() + throws SAXException; + + public void endDocument() + throws SAXException; + + public void startPrefixMapping(String prefix, String uri) + throws SAXException; + + public void endPrefixMapping(String prefix) + throws SAXException; + + public Handler startElement(String uri, String loc, String raw, Attributes attrs) + throws SAXException; + + /** + * Called before startElement, handler can decide what other handler should process + * next startElement. + */ + public Handler nestedElement(String uri, String loc, String raw, Attributes attrs) + throws SAXException; + + public void endElement(String uri, String loc, String raw) + throws SAXException; + + public Handler characters(char ch[], int start, int length) + throws SAXException; + + public Handler ignorableWhitespace(char ch[], int start, int length) + throws SAXException; + + public Handler processingInstruction(String target, String data) + throws SAXException; + + public Handler skippedEntity(String name) + throws SAXException; + + public Handler startDTD(String name, String publicId, String systemId) + throws SAXException; + + public Handler endDTD() + throws SAXException; + + public Handler startEntity(String name) + throws SAXException; + + public Handler endEntity(String name) + throws SAXException; + + public Handler startCDATA() + throws SAXException; + + public Handler endCDATA() + throws SAXException; + + public Handler comment(char c[], int start, int len) + throws SAXException; + } + + /** + * Ignores all events + */ + protected class NullHandler implements Handler { + public Handler startDocument() throws SAXException { + return this; + } + + public void endDocument() throws SAXException { + } + + public void startPrefixMapping(String prefix, String uri) throws SAXException { + } + + public void endPrefixMapping(String prefix) throws SAXException { + } + + public Handler nestedElement(String uri, String loc, String raw, Attributes attrs) throws SAXException { + return this; + } + + public Handler startElement(String uri, String loc, String raw, Attributes attrs) throws SAXException { + return this; + } + + public void endElement(String uri, String loc, String raw) throws SAXException { + } + + public Handler characters(char ch[], int start, int length) throws SAXException { + return this; + } + + public Handler ignorableWhitespace(char ch[], int start, int length) throws SAXException { + return this; + } + + public Handler processingInstruction(String target, String data) throws SAXException { + return this; + } + + public Handler skippedEntity(String name) throws SAXException { + return this; + } + + public Handler startDTD(String name, String publicId, String systemId) throws SAXException { + return this; + } + + public Handler endDTD() throws SAXException { + return this; + } + + public Handler startEntity(String name) throws SAXException { + return this; + } + + public Handler endEntity(String name) throws SAXException { + return this; + } + + public Handler startCDATA() throws SAXException { + return this; + } + + public Handler endCDATA() throws SAXException { + return this; + } + + public Handler comment(char c[], int start, int len) throws SAXException { + return this; + } + } + + /** + * Buffers content into the pipe's SAX buffer. + */ + protected class BufferHandler extends NullHandler { + public Handler startDocument() throws SAXException { + if (buffer != null) buffer.startDocument(); + return this; + } + + public void setDocumentLocator(Locator paramLocator) { + locator = new LocatorImpl(paramLocator); + if (buffer != null) buffer.setDocumentLocator(paramLocator); + } + + public void endDocument() throws SAXException { + if (buffer != null) buffer.endDocument(); + } + + public void startPrefixMapping(String prefix, String uri) throws SAXException { + if (buffer != null) buffer.startPrefixMapping(prefix, uri); + } + + public void endPrefixMapping(String prefix) throws SAXException { + if (buffer != null) buffer.endPrefixMapping(prefix); + } + + public Handler startElement(String uri, String loc, String raw, Attributes attrs) throws SAXException { + if (buffer != null) buffer.startElement(uri, loc, raw, attrs); + return this; + } + + public void endElement(String uri, String loc, String raw) throws SAXException { + if (buffer != null) buffer.endElement(uri, loc, raw); + } + + public Handler characters(char ch[], int start, int length) throws SAXException { + if (buffer != null) buffer.characters(ch, start, length); + return this; + } + + public Handler ignorableWhitespace(char ch[], int start, int length) throws SAXException { + if (buffer != null) buffer.ignorableWhitespace(ch, start, length); + return this; + } + + public Handler processingInstruction(String target, String data) throws SAXException { + if (buffer != null) buffer.processingInstruction(target, data); + return this; + } + + public Handler skippedEntity(String name) throws SAXException { + if (buffer != null) buffer.skippedEntity(name); + return this; + } + + public Handler startDTD(String name, String publicId, String systemId) throws SAXException { + if (buffer != null) buffer.startDTD(name, publicId, systemId); + return this; + } + + public Handler endDTD() throws SAXException { + if (buffer != null) buffer.endDTD(); + return this; + } + + public Handler startEntity(String name) throws SAXException { + if (buffer != null) buffer.startEntity(name); + return this; + } + + public Handler endEntity(String name) throws SAXException { + if (buffer != null) buffer.endEntity(name); + return this; + } + + public Handler startCDATA() throws SAXException { + if (buffer != null) buffer.startCDATA(); + return this; + } + + public Handler endCDATA() throws SAXException { + if (buffer != null) buffer.endCDATA(); + return this; + } + + public Handler comment(char c[], int start, int len) throws SAXException { + if (buffer != null) buffer.comment(c, start, len); + return this; + } + } + + /** + * Copies events over into the contentHandler + */ + protected class CopyHandler extends NullHandler { + public Handler startDocument() throws SAXException { + contentHandler.startDocument(); + return this; + } + + public void endDocument() throws SAXException { + contentHandler.endDocument(); + } + + public void startPrefixMapping(String prefix, String uri) throws SAXException { + contentHandler.startPrefixMapping(prefix, uri); + } + + public void endPrefixMapping(String prefix) throws SAXException { + contentHandler.endPrefixMapping(prefix); + } + + public Handler startElement(String uri, String loc, String raw, Attributes attrs) throws SAXException { + contentHandler.startElement(uri, loc, raw, attrs); + return this; + } + + public void endElement(String uri, String loc, String raw) throws SAXException { + contentHandler.endElement(uri, loc, raw); + } + + public Handler characters(char ch[], int start, int length) throws SAXException { + contentHandler.characters(ch, start, length); + return this; + } + + public Handler ignorableWhitespace(char ch[], int start, int length) throws SAXException { + contentHandler.ignorableWhitespace(ch, start, length); + return this; + } + + public Handler processingInstruction(String target, String data) throws SAXException { + contentHandler.processingInstruction(target, data); + return this; + } + + public Handler skippedEntity(String name) throws SAXException { + contentHandler.skippedEntity(name); + return this; + } + + public Handler startDTD(String name, String publicId, String systemId) throws SAXException { + if (lexicalHandler != null) lexicalHandler.startDTD(name, publicId, systemId); + return this; + } + + public Handler endDTD() throws SAXException { + if (lexicalHandler != null) lexicalHandler.endDTD(); + return this; + } + + public Handler startEntity(String name) throws SAXException { + if (lexicalHandler != null) lexicalHandler.startEntity(name); + return this; + } + + public Handler endEntity(String name) throws SAXException { + if (lexicalHandler != null) lexicalHandler.endEntity(name); + return this; + } + + public Handler startCDATA() throws SAXException { + if (lexicalHandler != null) lexicalHandler.startCDATA(); + return this; + } + + public Handler endCDATA() throws SAXException { + if (lexicalHandler != null) lexicalHandler.endCDATA(); + return this; + } + + public Handler comment(char c[], int start, int len) throws SAXException { + if (lexicalHandler != null) lexicalHandler.comment(c, start, len); + return this; + } + } + + /** + * Throws exception on most events, with the exception of ignorableWhitespace. + */ + protected class ErrorHandler extends NullHandler { + protected String getName() { + return ""; + } + + public Handler startDocument() throws SAXException { + throw new SAXException("Unexpected startDocument in '" + getName() + "' (" + getLocation() + ")"); + } + + public void endDocument() throws SAXException { + throw new SAXException("Unexpected endDocument in '" + getName() + "' (" + getLocation() + ")"); + } + + public void startPrefixMapping(String prefix, String uri) throws SAXException { + throw new SAXException("Unexpected startPrefixMapping in '" + getName() + "' (" + getLocation() + ")"); + } + + public void endPrefixMapping(String prefix) throws SAXException { + throw new SAXException("Unexpected endPrefixMapping in '" + getName() + "' (" + getLocation() + ")"); + } + + public Handler startElement(String uri, String loc, String raw, Attributes attrs) throws SAXException { + throw new SAXException("Unexpected startElement in '" + getName() + "' (" + getLocation() + ")"); + } + + public void endElement(String uri, String loc, String raw) throws SAXException { + throw new SAXException("Unexpected endElement in '" + getName() + "' (" + getLocation() + ")"); + } + + public Handler characters(char ch[], int start, int length) throws SAXException { + throw new SAXException("Unexpected characters in '" + getName() + "' (" + getLocation() + ")"); + } + + public Handler processingInstruction(String target, String data) throws SAXException { + throw new SAXException("Unexpected processingInstruction in '" + getName() + "' (" + getLocation() + ")"); + } + + public Handler skippedEntity(String name) throws SAXException { + throw new SAXException("Unexpected skippedEntity in '" + getName() + "' (" + getLocation() + ")"); + } + + public Handler startDTD(String name, String publicId, String systemId) throws SAXException { + throw new SAXException("Unexpected startDTD in '" + getName() + "' (" + getLocation() + ")"); + } + + public Handler endDTD() throws SAXException { + throw new SAXException("Unexpected endDTD in '" + getName() + "' (" + getLocation() + ")"); + } + + public Handler startEntity(String name) throws SAXException { + throw new SAXException("Unexpected startEntity in '" + getName() + "' (" + getLocation() + ")"); + } + + public Handler endEntity(String name) throws SAXException { + throw new SAXException("Unexpected endEntity in '" + getName() + "' (" + getLocation() + ")"); + } + + public Handler startCDATA() throws SAXException { + throw new SAXException("Unexpected startCDATA in '" + getName() + "' (" + getLocation() + ")"); + } + + public Handler endCDATA() throws SAXException { + throw new SAXException("Unexpected endCDATA in '" + getName() + "' (" + getLocation() + ")"); + } + + public Handler comment(char c[], int start, int len) throws SAXException { + throw new SAXException("Unexpected comment in '" + getName() + "' (" + getLocation() + ")"); + } + } + + protected final Handler hNull = new NullHandler(); + protected final Handler hBuffer = new BufferHandler(); + + private Locator locator; + private LinkedList handlers; + private Handler handler; + + private LinkedList buffers; + private LinkedList locators; + private SaxBuffer buffer; + + + /** + * Initialize the pipe before starting processing + */ + protected void init(Handler top) { + locators = new LinkedList(); + handlers = new LinkedList(); + handler = top; + } + + /** + * Recycle the pipe after processing + */ + public void recycle() { + super.recycle(); + handlers = null; + handler = null; + buffers = null; + buffer = null; + locator = null; + locators = null; + } + + /** + * @return current location (if known) + */ + protected String getLocation() { + if (locator == null) { + return "unknown"; + } + + final String location = " (" + locator.getSystemId() + ":" + + locator.getLineNumber() + ":" + + locator.getColumnNumber() + ")"; + return location; + } + + protected void pushHandler(Handler handler) { + this.handlers.addFirst(this.handler); + this.handler = handler; + } + + protected void popHandler() { + this.handler = (Handler) this.handlers.removeFirst(); + } + + protected void beginBuffer() { + if (this.buffer != null) { + if (this.buffers == null) { + this.buffers = new LinkedList(); + } + this.buffers.addFirst(this.buffer); + } + locators.addFirst(locator); + locator = new LocatorImpl(locator); + this.buffer = new SaxBuffer(); + } + + protected SaxBuffer endBuffer() { + SaxBuffer buffer = this.buffer; + if (this.buffers != null && this.buffers.size() > 0) { + this.buffer = (SaxBuffer) this.buffers.removeFirst(); + } else { + this.buffer = null; + } + locator = (Locator)locators.removeFirst(); + return buffer; + } + + // + // ContentHandler methods + // + + public void setDocumentLocator(Locator locator) { + this.locator = locator; + } + + public void startDocument() throws SAXException { + pushHandler(handler.startDocument()); + } + + public void endDocument() throws SAXException { + handler.endDocument(); + popHandler(); + } + + public void startPrefixMapping(String prefix, String uri) throws SAXException { + handler.startPrefixMapping(prefix, uri); + } + + public void endPrefixMapping(String prefix) throws SAXException { + handler.endPrefixMapping(prefix); + } + + public void startElement(String uri, String loc, String raw, Attributes attrs) throws SAXException { + pushHandler(handler.nestedElement(uri, loc, raw, attrs)); + handler = handler.startElement(uri, loc, raw, attrs); + } + + public void endElement(String uri, String loc, String raw) throws SAXException { + handler.endElement(uri, loc, raw); + popHandler(); + } + + public void characters(char ch[], int start, int len) throws SAXException { + handler = handler.characters(ch, start, len); + } + + public void ignorableWhitespace(char ch[], int start, int len) throws SAXException { + handler = handler.ignorableWhitespace(ch, start, len); + } + + public void processingInstruction(String target, String data) throws SAXException { + handler = handler.processingInstruction(target, data); + } + + public void skippedEntity(String name) throws SAXException { + handler = handler.skippedEntity(name); + } + + // + // LexicalHandler methods + // + + public void startDTD(String name, String publicId, String systemId) throws SAXException { + handler = handler.startDTD(name, publicId, systemId); + } + + public void endDTD() throws SAXException { + handler = handler.endDTD(); + } + + public void startEntity(String name) throws SAXException { + handler = handler.startEntity(name); + } + + public void endEntity(String name) throws SAXException { + handler = handler.endEntity(name); + } + + public void startCDATA() throws SAXException { + handler = handler.startCDATA(); + } + + public void endCDATA() throws SAXException { + handler = handler.endCDATA(); + } + + public void comment(char ch[], int start, int len) throws SAXException { + handler = handler.comment(ch, start, len); + } +} Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/transformation/EffectPipe.java ------------------------------------------------------------------------------ svn:eol-style = native Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/transformation/EffectWidgetReplacingPipe.java URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/transformation/EffectWidgetReplacingPipe.java?rev=330548&view=auto ============================================================================== --- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/transformation/EffectWidgetReplacingPipe.java (added) +++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/transformation/EffectWidgetReplacingPipe.java Thu Nov 3 05:41:06 2005 @@ -0,0 +1,940 @@ +/* + * Copyright 1999-2005 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.cocoon.forms.transformation; + +import org.apache.avalon.excalibur.pool.Recyclable; + +import org.apache.cocoon.forms.FormsConstants; +import org.apache.cocoon.forms.formmodel.AggregateField; +import org.apache.cocoon.forms.formmodel.DataWidget; +import org.apache.cocoon.forms.formmodel.Group; +import org.apache.cocoon.forms.formmodel.Repeater; +import org.apache.cocoon.forms.formmodel.Struct; +import org.apache.cocoon.forms.formmodel.Union; +import org.apache.cocoon.forms.formmodel.Widget; +import org.apache.cocoon.forms.validation.ValidationError; +import org.apache.cocoon.forms.validation.ValidationErrorAware; +import org.apache.cocoon.i18n.I18nUtils; +import org.apache.cocoon.xml.AbstractXMLPipe; +import org.apache.cocoon.xml.SaxBuffer; +import org.apache.cocoon.xml.XMLUtils; + +import org.apache.commons.jxpath.JXPathException; +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; +import org.xml.sax.ext.LexicalHandler; +import org.xml.sax.helpers.AttributesImpl; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +/** + * The basic operation of this Pipe is that it replaces ft:widget + * (in the {@link FormsConstants#TEMPLATE_NS} namespace) tags (having an id attribute) + * by the XML representation of the corresponding widget instance. + * + *

These XML fragments (normally all in the {@link FormsConstants#INSTANCE_NS "CForms Instance"} + * namespace), can then be translated to a HTML presentation by an XSLT. + * This XSLT will then only have to style individual widget, and will not + * need to do the whole page layout. + * + *

For more information about the supported tags and their function, + * see the user documentation for the forms template transformer.

+ * + * @version $Id: EffectWidgetReplacingPipe.java 326838 2005-10-20 06:26:53Z sylvain $ + */ +public class EffectWidgetReplacingPipe extends EffectPipe { + + /** + * Form location attribute on ft:form-template element, containing + * JXPath expression which should result in Form object. + * + * @see FormsPipelineConfig#findForm(String) + */ + private static final String LOCATION = "location"; + + private static final String AGGREGATE_WIDGET = "aggregate-widget"; + private static final String CHOOSE = "choose"; + private static final String CLASS = "class"; + private static final String CONTINUATION_ID = "continuation-id"; + private static final String FORM_TEMPLATE_EL = "form-template"; + private static final String GROUP = "group"; + private static final String NEW = "new"; + private static final String REPEATER_SIZE = "repeater-size"; + private static final String REPEATER_WIDGET = "repeater-widget"; + private static final String REPEATER_WIDGET_LABEL = "repeater-widget-label"; + private static final String STRUCT = "struct"; + private static final String STYLING_EL = "styling"; + private static final String UNION = "union"; + private static final String VALIDATION_ERROR = "validation-error"; + private static final String WIDGET = "widget"; + private static final String WIDGET_LABEL = "widget-label"; + + private final AggregateWidgetHandler hAggregate = new AggregateWidgetHandler(); + private final ChooseHandler hChoose = new ChooseHandler(); + private final ChoosePassThruHandler hChoosePassThru = new ChoosePassThruHandler(); + private final ClassHandler hClass = new ClassHandler(); + private final ContinuationIdHandler hContinuationId = new ContinuationIdHandler(); + private final DocHandler hDocument = new DocHandler(); + private final FormHandler hForm = new FormHandler(); + private final GroupHandler hGroup = new GroupHandler(); + private final NestedHandler hNested = new NestedHandler(); + private final NewHandler hNew = new NewHandler(); + private final RepeaterSizeHandler hRepeaterSize = new RepeaterSizeHandler(); + private final RepeaterWidgetHandler hRepeaterWidget = new RepeaterWidgetHandler(); + private final RepeaterWidgetLabelHandler hRepeaterWidgetLabel = new RepeaterWidgetLabelHandler(); + private final SkipHandler hSkip = new SkipHandler(); + private final StructHandler hStruct = new StructHandler(); + private final StylingContentHandler hStyling = new StylingContentHandler(); + private final UnionHandler hUnion = new UnionHandler(); + private final UnionPassThruHandler hUnionPassThru = new UnionPassThruHandler(); + private final ValidationErrorHandler hValidationError = new ValidationErrorHandler(); + private final WidgetHandler hWidget = new WidgetHandler(); + private final WidgetLabelHandler hWidgetLabel = new WidgetLabelHandler(); + + /** + * Map containing all handlers + */ + protected final Map templates; + + protected FormsPipelineConfig pipeContext; + + /** + * The namespaces and their prefixes + */ + private final List namespaces; + + /** + * True if instance namespace has been mapped to the + * 'fi' prefix. + */ + private boolean hasInstanceNamespace; + + protected Widget contextWidget; + protected LinkedList contextWidgets; + protected LinkedList chooseWidgets; + protected Widget widget; + protected Map classes; + + + public EffectWidgetReplacingPipe() { + namespaces = new ArrayList(5); + // Setup map of templates. + templates = new HashMap(); + templates.put(AGGREGATE_WIDGET, hAggregate); + templates.put(CHOOSE, hChoose); + templates.put(CLASS, hClass); + templates.put(CONTINUATION_ID, hContinuationId); + templates.put(GROUP, hGroup); + templates.put(NEW, hNew); + templates.put(REPEATER_SIZE, hRepeaterSize); + templates.put(REPEATER_WIDGET, hRepeaterWidget); + templates.put(REPEATER_WIDGET_LABEL, hRepeaterWidgetLabel); + templates.put(STRUCT, hStruct); + templates.put(UNION, hUnion); + templates.put(VALIDATION_ERROR, hValidationError); + templates.put(WIDGET, hWidget); + templates.put(WIDGET_LABEL, hWidgetLabel); + } + + public void init(Widget contextWidget, FormsPipelineConfig pipeContext) { + // Document handler is top level handler + super.init(hDocument); + this.pipeContext = pipeContext; + + // Initialize widget related variables + this.contextWidgets = new LinkedList(); + this.chooseWidgets = new LinkedList(); + this.classes = new HashMap(); + } + + public void recycle() { + super.recycle(); + this.contextWidget = null; + this.widget = null; + this.pipeContext = null; + this.namespaces.clear(); + this.hasInstanceNamespace = false; + } + + /** + * Get value of the required attribute + */ + protected String getAttributeValue(String loc, Attributes attrs, String name) throws SAXException { + String value = attrs.getValue(name); + if (value == null) { + throw new SAXException("Element '" + loc + "' missing required '" + name + "' attribute, " + + "at " + getLocation()); + } + return value; + } + + /** + * Get non-empty value of the required attribute + */ + protected String getRequiredAttributeValue(String loc, Attributes attrs, String name) throws SAXException { + String value = attrs.getValue(name); + if (value == null || value.equals("")) { + throw new SAXException("Element '" + loc + "' missing required '" + name + "' attribute, " + + "at " + getLocation()); + } + return value; + } + + /** + * Set the widget by the id attribute + */ + protected void setWidget(String loc, Attributes attrs) throws SAXException { + setWidget(loc, getRequiredAttributeValue(loc, attrs, "id")); + } + + /** + * Set the widget by its path + */ + protected void setWidget(String loc, String path) throws SAXException { + widget = contextWidget.lookupWidget(path); + if (widget == null) { + if (contextWidget.getRequestParameterName().equals("")) { + throw new SAXException("Element '" + loc + "' refers to unexistent widget path '" + path + "', " + + "relative to the form container, at " + getLocation()); + } else { + throw new SAXException("Element '" + loc + "' refers to unexistent widget path '" + path + "', " + + "relative to the '" + contextWidget.getRequestParameterName() + "', " + + "at " + getLocation()); + } + } + } + + /** + * Set typed widget by the id attribute + */ + protected void setTypedWidget(String loc, Attributes attrs, Class wclass, String wname) throws SAXException { + setWidget(loc, attrs); + if (!wclass.isInstance(widget)) { + throw new SAXException("Element '" + loc + "' can only be used with " + wname + " widgets, " + + "at " + getLocation()); + } + } + + protected boolean isVisible(Widget widget) { + return widget.getCombinedState().isDisplayingValues(); + } + + /** + * Needed to get things working with JDK 1.3. Can be removed once we + * don't support that platform any more. + */ + private ContentHandler getContentHandler() { + return this.contentHandler; + } + + /** + * Needed to get things working with JDK 1.3. Can be removed once we + * don't support that platform any more. + */ + private LexicalHandler getLexicalHandler() { + return this.lexicalHandler; + } + + + /** + * Process the SAX event. + * @see org.xml.sax.ContentHandler#startPrefixMapping + */ + public void startPrefixMapping(String prefix, String uri) + throws SAXException { + if (prefix != null) { + this.namespaces.add(new String[] {prefix, uri}); + } + + // Consume template namespace mapping + if (!FormsConstants.TEMPLATE_NS.equals(uri)) { + super.startPrefixMapping(prefix, uri); + } + } + + /** + * Process the SAX event. + * @see org.xml.sax.ContentHandler#endPrefixMapping + */ + public void endPrefixMapping(String prefix) + throws SAXException { + String uri = null; + + if (prefix != null) { + // Find and remove the namespace prefix + boolean found = false; + for (int i = this.namespaces.size() - 1; i >= 0; i--) { + final String[] prefixAndUri = (String[]) this.namespaces.get(i); + if (prefixAndUri[0].equals(prefix)) { + uri = prefixAndUri[1]; + this.namespaces.remove(i); + found = true; + break; + } + } + if (!found) { + throw new SAXException("Namespace for prefix '" + prefix + "' not found."); + } + } + + // Consume template namespace mapping + if (!FormsConstants.TEMPLATE_NS.equals(uri)) { + super.endPrefixMapping(prefix); + } + } + + /** + * @return True if prefix is already mapped into the namespace + */ + protected boolean hasPrefixMapping(String uri, String prefix) { + final int l = this.namespaces.size(); + for (int i = 0; i < l; i++) { + String[] prefixAndUri = (String[]) this.namespaces.get(i); + if (prefixAndUri[0].equals(prefix) && prefixAndUri[1].equals(uri)) { + return true; + } + } + + return false; + } + + // + // Handler classes to transform CForms template elements + // + + protected class NestedHandler extends CopyHandler { + public Handler nestedElement(String uri, String loc, String raw, Attributes attrs) + throws SAXException { + // Is it forms namespace? + if (!FormsConstants.TEMPLATE_NS.equals(uri)) { + return hNested; + } + + Handler handler = (Handler) templates.get(loc); + if (handler == null) { + throw new SAXException("Element '" + loc + "' was not recognized, " + + "at " + getLocation()); + } + + return handler; + } + } + + /** + * Top level handler for the forms template + */ + protected class DocHandler extends CopyHandler { + public Handler nestedElement(String uri, String loc, String raw, Attributes attrs) + throws SAXException { + if (FormsConstants.TEMPLATE_NS.equals(uri)) { + if (!FORM_TEMPLATE_EL.equals(loc)) { + throw new SAXException("Element '" + loc + "' is not permitted outside of " + + "'form-template', at " + getLocation()); + } + + return hForm; + } + + return super.nestedElement(uri, loc, raw, attrs); + } + } + + /** + * ft:form-template element handler. + *
+     * <ft:form-template locale="..." location="...">
+     *   ...
+     * </ft:form-template>
+     * 
+ */ + protected class FormHandler extends NestedHandler { + public Handler startElement(String uri, String loc, String raw, Attributes attrs) + throws SAXException { + if (contextWidget != null) { + throw new SAXException("Element 'form-template' can not be nested, " + + "at " + getLocation()); + } + + AttributesImpl newAttrs = attrs == null || attrs.getLength() == 0? + new AttributesImpl(): + new AttributesImpl(attrs); + + // ====> Retrieve the form + String formLocation = attrs.getValue(LOCATION); + if (formLocation != null) { + // Remove the location attribute + newAttrs.removeAttribute(newAttrs.getIndex(LOCATION)); + } + contextWidget = pipeContext.findForm(formLocation); + + // ====> Check if form visible (and skip it if it's not) + if (!isVisible(contextWidget)) { + return hNull; + } + + // ====> Determine the Locale + // TODO pull this locale stuff also up in the Config object? + String localeAttr = attrs.getValue("locale"); + if (localeAttr != null) { // first use value of locale attribute if any + localeAttr = pipeContext.translateText(localeAttr); + pipeContext.setLocale(I18nUtils.parseLocale(localeAttr)); + } else if (pipeContext.getLocaleParameter() != null) { // then use locale specified as transformer parameter, if any + pipeContext.setLocale(pipeContext.getLocaleParameter()); + } else { + // use locale specified in bizdata supplied for form + Object locale = null; + try { + locale = pipeContext.evaluateExpression("/locale"); + } catch (JXPathException e) {} + if (locale != null) { + pipeContext.setLocale((Locale)locale); + } else { + // final solution: use locale defined in the server machine + pipeContext.setLocale(Locale.getDefault()); + } + } + + // We need to merge input.attrs with possible overruling attributes + // from the pipeContext + pipeContext.addFormAttributes(newAttrs); + String[] namesToTranslate = {"action"}; + Attributes transAttrs = null; + try { + transAttrs = translateAttributes(newAttrs, namesToTranslate); + } catch (RuntimeException e) { + throw new SAXException( e.getMessage() + " " +getLocation()); + } + + hasInstanceNamespace = hasPrefixMapping(FormsConstants.INSTANCE_NS, FormsConstants.INSTANCE_PREFIX); + if (!hasInstanceNamespace) { + getContentHandler().startPrefixMapping(FormsConstants.INSTANCE_PREFIX, FormsConstants.INSTANCE_NS); + } + getContentHandler().startElement(FormsConstants.INSTANCE_NS, "form-template", FormsConstants.INSTANCE_PREFIX_COLON + "form-template", transAttrs); + return this; + } + + public void endElement(String uri, String loc, String raw) + throws SAXException { + getContentHandler().endElement(FormsConstants.INSTANCE_NS, "form-template", FormsConstants.INSTANCE_PREFIX_COLON + "form-template"); + if (!hasInstanceNamespace) { + getContentHandler().endPrefixMapping(FormsConstants.INSTANCE_PREFIX); + } + contextWidget = null; + } + } + + /** + * ft:choose, ft:union use this. + */ + protected class SkipHandler extends NestedHandler { + public Handler startElement(String uri, String loc, String raw, Attributes attrs) + throws SAXException { + return this; + } + + public void endElement(String uri, String loc, String raw) + throws SAXException { + } + } + + // + // Widget Handlers + // + + /** + * Handles ft:widget-label element. + */ + protected class WidgetLabelHandler extends ErrorHandler { + public Handler startElement(String uri, String loc, String raw, Attributes attrs) + throws SAXException { + setWidget(loc, attrs); + widget.generateLabel(getContentHandler()); + return this; + } + + public void endElement(String uri, String loc, String raw) + throws SAXException { + } + } + + /** + * Handles ft:widget element. + */ + protected class WidgetHandler extends NullHandler { + // Widgets can't be nested, so this variable is Ok + private boolean hasStyling; + + public Handler startElement(String uri, String loc, String raw, Attributes attrs) + throws SAXException { + setWidget(loc, attrs); + if (!isVisible(widget)) { + return hNull; + } + + hasStyling = false; + return this; + } + + public Handler nestedElement(String uri, String loc, String raw, Attributes attrs) + throws SAXException { + if (FormsConstants.INSTANCE_NS.equals(uri)) { + if (!STYLING_EL.equals(loc)) { + throw new SAXException("Element '" + loc + "' is not permitted within 'widget', " + + "at " + getLocation()); + } + hasStyling = true; + beginBuffer(); + // Buffer styling elements + return hBuffer; + } + return hNull; + } + + public void endElement(String uri, String loc, String raw) + throws SAXException { + if (hasStyling) { + // Pipe widget XML through the special handler to insert styling element + // before fi:widget end element. + hasStyling = false; + hStyling.recycle(); + hStyling.setSaxFragment(endBuffer()); + hStyling.setContentHandler(getContentHandler()); + hStyling.setLexicalHandler(getLexicalHandler()); + widget.generateSaxFragment(hStyling, pipeContext.getLocale()); + } else { + // Pipe widget XML directly into the output handler + widget.generateSaxFragment(getContentHandler(), pipeContext.getLocale()); + } + widget = null; + } + } + + // + // Repeater Handlers + // + + /** + * Handles ft:repeater-size element. + */ + protected class RepeaterSizeHandler extends ErrorHandler { + public Handler startElement(String uri, String loc, String raw, Attributes attrs) + throws SAXException { + setTypedWidget(loc, attrs, Repeater.class, "repeater"); + ((Repeater) widget).generateSize(getContentHandler()); + widget = null; + return this; + } + + public void endElement(String uri, String loc, String raw) + throws SAXException { + } + } + + /** + * Handles ft:repeater-widget-label element. + */ + protected class RepeaterWidgetLabelHandler extends ErrorHandler { + public Handler startElement(String uri, String loc, String raw, Attributes attrs) + throws SAXException { + setTypedWidget(loc, attrs, Repeater.class, "repeater"); + String path = getRequiredAttributeValue(loc, attrs, "widget-id"); + ((Repeater) widget).generateWidgetLabel(path, getContentHandler()); + widget = null; + return this; + } + + public void endElement(String uri, String loc, String raw) + throws SAXException { + } + } + + /** + * Handles ft:repeater element. + */ + protected class RepeaterWidgetHandler extends BufferHandler { + public Handler startElement(String uri, String loc, String raw, Attributes attrs) + throws SAXException { + setTypedWidget(loc, attrs, Repeater.class, "repeater"); + if (isVisible(widget)) { + beginBuffer(); + return this; + } + return hNull; + } + + public Handler nestedElement(String uri, String loc, String raw, Attributes attrs) + throws SAXException { + return hBuffer; + } + + public void endElement(String uri, String loc, String raw) + throws SAXException { + SaxBuffer buffer = endBuffer(); + final Repeater repeater = (Repeater) widget; + final int rowCount = repeater.getSize(); + pushHandler(hNested); + contextWidgets.addFirst(contextWidget); + for (int i = 0; i < rowCount; i++) { + contextWidget = repeater.getRow(i); + if (isVisible(contextWidget)) { + buffer.toSAX(EffectWidgetReplacingPipe.this); + } + } + contextWidget = (Widget) contextWidgets.removeFirst(); + popHandler(); + widget = null; + } + } + + // + // Grouping widgets Handlers + // + + /** + * Handles ft:group element. + */ + protected class GroupHandler extends NestedHandler { + protected Class getWidgetClass() { + return Group.class; + } + + protected String getWidgetName() { + return "group"; + } + + public Handler startElement(String uri, String loc, String raw, Attributes attrs) + throws SAXException { + setTypedWidget(loc, attrs, getWidgetClass(), getWidgetName()); + if (!isVisible(widget)) { + return hNull; + } + + contextWidgets.addFirst(contextWidget); + contextWidget = widget; + return this; + } + + public void endElement(String uri, String loc, String raw) + throws SAXException { + contextWidget = (Widget) contextWidgets.removeFirst(); + } + } + + /** + * Handles ft:aggregate element. + */ + protected class AggregateWidgetHandler extends GroupHandler { + protected Class getWidgetClass() { + return AggregateField.class; + } + + protected String getWidgetName() { + return "aggregate"; + } + } + + /** + * Handles ft:choose element. + */ + protected class ChooseHandler extends CopyHandler { + public Handler startElement(String uri, String loc, String raw, Attributes attrs) throws SAXException { + setWidget(loc, getRequiredAttributeValue(loc, attrs, "path")); + // TODO: Should instead check for datatype convertable to String. + if (!(widget instanceof DataWidget)) { + throw new SAXException("Element '" + loc + "' can only be used with DataWidget widgets, " + + "at " + getLocation()); + } + // Choose does not change the context widget like Union does. + chooseWidgets.addFirst(widget); + return this; + } + + public Handler nestedElement(String uri, String loc, String raw, Attributes attrs) throws SAXException { + if (FormsConstants.TEMPLATE_NS.equals(uri)) { + if ("when".equals(loc)) { + String testValue = getAttributeValue(loc, attrs, "value"); + String value = (String) ((Widget) chooseWidgets.get(0)).getValue(); + return testValue.equals(value) ? hSkip : hNull; + } + throw new SAXException("Element '" + loc + "' is not permitted within 'choose', " + + "at " + getLocation()); + } + return hChoosePassThru; + } + + public void endElement(String uri, String loc, String raw) throws SAXException { + chooseWidgets.removeFirst(); + } + } + + /** + * Handles ft:choose/ft:when element. + */ + protected class ChoosePassThruHandler extends CopyHandler { + public Handler nestedElement(String uri, String loc, String raw, Attributes attrs) throws SAXException { + if (FormsConstants.TEMPLATE_NS.equals(uri)) { + if ("when".equals(loc)) { + String testValue = getAttributeValue(loc, attrs, "value"); + String value = (String) ((Widget) chooseWidgets.get(0)).getValue(); + return testValue.equals(value)? hSkip: hNull; + } + throw new SAXException("Element '" + loc + "' is not permitted within 'choose', " + + "at " + getLocation()); + } + return this; + } + } + + /** + * Handles ft:struct element. + */ + protected class StructHandler extends GroupHandler { + protected Class getWidgetClass() { + return Struct.class; + } + + protected String getWidgetName() { + return "struct"; + } + } + + /** + * Handles ft:union element. + */ + protected class UnionHandler extends GroupHandler { + protected Class getWidgetClass() { + return Union.class; + } + + protected String getWidgetName() { + return "union"; + } + + public Handler nestedElement(String uri, String loc, String raw, Attributes attrs) + throws SAXException { + if (FormsConstants.TEMPLATE_NS.equals(uri)) { + if ("case".equals(loc)) { + String id = getAttributeValue(loc, attrs, "id"); + String value = (String) contextWidget.getValue(); + if (id.equals(value != null ? value : "")) { + return hSkip; + } + return hNull; + } + throw new SAXException("Element '" + loc + "' is not permitted within 'union', " + + "at " + getLocation()); + } + return hUnionPassThru; + } + } + + /** + * Handles ft:union/ft:case element. + */ + protected class UnionPassThruHandler extends CopyHandler { + public Handler nestedElement(String uri, String loc, String raw, Attributes attrs) + throws SAXException { + if (FormsConstants.TEMPLATE_NS.equals(uri)) { + if ("case".equals(loc)) { + if (contextWidget.getValue().equals(attrs.getValue("id"))) { + return hSkip; + } + return hNull; + } + throw new SAXException("Element '" + loc + "' is not permitted within 'union', " + + "at " + getLocation()); + } + return this; + } + } + + /** + * Handles ft:new element. + */ + protected class NewHandler extends CopyHandler { + public Handler startElement(String uri, String loc, String raw, Attributes attrs) + throws SAXException { + String id = getRequiredAttributeValue(loc, attrs, "id"); + SaxBuffer buffer = (SaxBuffer) classes.get(id); + if (buffer == null) { + throw new SAXException("New: Class '" + id + "' does not exist, " + + "at " + getLocation()); + } + pushHandler(hNested); + buffer.toSAX(EffectWidgetReplacingPipe.this); + popHandler(); + return this; + } + + public Handler nestedElement(String uri, String loc, String raw, Attributes attrs) + throws SAXException { + return hNull; + } + + public void endElement(String uri, String loc, String raw) + throws SAXException { + } + } + + /** + * Handles ft:class element. + *
+     * <ft:class id="...">
+     *   ...
+     * </ft:class>
+     * 
+ */ + protected class ClassHandler extends BufferHandler { + // FIXME What if is nested within ? + private String widgetPath; + + public Handler startElement(String uri, String loc, String raw, Attributes attrs) + throws SAXException { + widgetPath = getRequiredAttributeValue(loc, attrs, "id"); + beginBuffer(); + return this; + } + + public Handler nestedElement(String uri, String loc, String raw, Attributes attrs) + throws SAXException { + return hBuffer; + } + + public void endElement(String uri, String loc, String raw) throws SAXException { + classes.put(widgetPath, endBuffer()); + } + } + + /** + * Handles ft:continuation-id element. + *
+     * <ft:continuation-id/>
+     * 
+ */ + protected class ContinuationIdHandler extends ErrorHandler { + protected String getName() { + return "continuation-id"; + } + + public Handler startElement(String uri, String loc, String raw, Attributes attrs) + throws SAXException { + // Insert the continuation id + // FIXME(SW) we could avoid costly JXPath evaluation if we had the objectmodel here. + Object idObj = pipeContext.evaluateExpression("$cocoon/continuation/id"); + if (idObj == null) { + throw new SAXException("No continuation found"); + } + + String id = idObj.toString(); + getContentHandler().startElement(FormsConstants.INSTANCE_NS, "continuation-id", FormsConstants.INSTANCE_PREFIX_COLON + "continuation-id", attrs); + getContentHandler().characters(id.toCharArray(), 0, id.length()); + getContentHandler().endElement(FormsConstants.INSTANCE_NS, "continuation-id", FormsConstants.INSTANCE_PREFIX_COLON + "continuation-id"); + return this; + } + + public void endElement(String uri, String loc, String raw) throws SAXException { + } + } + + /** + * This ContentHandler helps in inserting SAX events before the closing tag of the root + * element. + */ + protected class StylingContentHandler extends AbstractXMLPipe + implements Recyclable { + + private int elementNesting; + private SaxBuffer styling; + + public void setSaxFragment(SaxBuffer saxFragment) { + styling = saxFragment; + } + + public void recycle() { + super.recycle(); + elementNesting = 0; + styling = null; + } + + public void startElement(String uri, String loc, String raw, Attributes a) + throws SAXException { + elementNesting++; + super.startElement(uri, loc, raw, a); + } + + public void endElement(String uri, String loc, String raw) + throws SAXException { + elementNesting--; + if (elementNesting == 0) { + styling.toSAX(getContentHandler()); + } + super.endElement(uri, loc, raw); + } + } + + /** + * Inserts validation errors (if any) for the Field widgets + */ + protected class ValidationErrorHandler extends NullHandler { + public Handler startElement(String uri, String loc, String raw, Attributes attrs) + throws SAXException { + setWidget(loc, attrs); + return this; + } + + public Handler nestedElement(String uri, String loc, String raw, Attributes attrs) + throws SAXException { + return hNull; + } + + public void endElement(String uri, String loc, String raw) + throws SAXException { + if (widget instanceof ValidationErrorAware) { + ValidationError error = ((ValidationErrorAware)widget).getValidationError(); + if (error != null) { + getContentHandler().startElement(FormsConstants.INSTANCE_NS, VALIDATION_ERROR, FormsConstants.INSTANCE_PREFIX_COLON + VALIDATION_ERROR, XMLUtils.EMPTY_ATTRIBUTES); + error.generateSaxFragment(hStyling); + getContentHandler().endElement(FormsConstants.INSTANCE_NS, VALIDATION_ERROR, FormsConstants.INSTANCE_PREFIX_COLON + VALIDATION_ERROR); + } + } + widget = null; + } + } + + + + private Attributes translateAttributes(Attributes attributes, String[] names) { + AttributesImpl newAtts = new AttributesImpl(attributes); + if (names!= null) { + for (int i = 0; i < names.length; i++) { + String name = names[i]; + int position = newAtts.getIndex(name); + String newValue = pipeContext.translateText(newAtts.getValue(position)); + if(position>-1) + newAtts.setValue(position, newValue); + else + throw new RuntimeException("Attribute \""+name+"\" not present!"); + } + } + return newAtts; + } +} Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/transformation/EffectWidgetReplacingPipe.java ------------------------------------------------------------------------------ svn:eol-style = native