Return-Path: Delivered-To: apmail-cocoon-cvs-archive@www.apache.org Received: (qmail 30077 invoked from network); 3 Nov 2005 14:47:40 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 3 Nov 2005 14:47:40 -0000 Received: (qmail 42652 invoked by uid 500); 3 Nov 2005 14:40:20 -0000 Delivered-To: apmail-cocoon-cvs-archive@cocoon.apache.org Received: (qmail 36606 invoked by uid 500); 3 Nov 2005 14:39:24 -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 29835 invoked by uid 99); 3 Nov 2005 14:38:23 -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:36:00 -0800 Received: (qmail 13256 invoked by uid 65534); 3 Nov 2005 14:35:39 -0000 Message-ID: <20051103143539.13255.qmail@minotaur.apache.org> Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r330548 [102/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/transformation/FormsPipelineConfig.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/FormsPipelineConfig.java?rev=330548&view=auto ============================================================================== --- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/transformation/FormsPipelineConfig.java (added) +++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/transformation/FormsPipelineConfig.java Thu Nov 3 05:41:06 2005 @@ -0,0 +1,388 @@ +/* + * 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.framework.parameters.Parameters; + +import org.apache.cocoon.components.flow.FlowHelper; +import org.apache.cocoon.components.flow.WebContinuation; +import org.apache.cocoon.environment.ObjectModelHelper; +import org.apache.cocoon.environment.Request; +import org.apache.cocoon.environment.Session; +import org.apache.cocoon.forms.formmodel.Form; +import org.apache.cocoon.i18n.I18nUtils; +import org.apache.cocoon.util.Deprecation; + +import org.apache.commons.jxpath.JXPathContext; +import org.apache.commons.jxpath.JXPathException; +import org.apache.commons.jxpath.Variables; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; + +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +/** + * @version $Id: FormsPipelineConfig.java 289538 2005-09-16 13:46:22Z sylvain $ + */ +public class FormsPipelineConfig { + + /** + * Default key under which the Cocoon Forms form instance is stored in the JXPath context. + */ + public static final String CFORMSKEY = "CocoonFormsInstance"; + + /** + * Name of the request attribute under which the Cocoon Form is stored (optional). */ + private final String attributeName; + + /** + * Pointer to the current request object. + */ + private final Request request; + + /** + * Initialized jxpathcontext to evaluate passed expressions with. + */ + private final JXPathContext jxpathContext; + + /** + * Containts locale specified as a parameter to the transformer, if any. + */ + private final Locale localeParameter; + + /** + * The locale currently used by the transformer. + */ + private Locale locale; + + /** + * Value for the action attribute of the form. + */ + private String formAction; + + /** + * Value for the method attribute of the form. + */ + private String formMethod; + + + private FormsPipelineConfig(JXPathContext jxpc, Request req, Locale localeParam, + String attName, String actionExpression, String method) { + this.attributeName = attName; + this.request = req; + this.jxpathContext =jxpc; + this.localeParameter = localeParam; + this.formAction = translateText(actionExpression); + this.formMethod = method; + } + + /** + * Creates and initializes a FormsPipelineConfig object based on the passed + * arguments of the setup() of the specific Pipeline-component. + * + * @param objectModel the objectmodel as passed in the setup() + * @param parameters the parameters as passed in the setup() + * @return an instance of FormsPipelineConfig initialized according to the + * settings in the sitemap. + */ + public static FormsPipelineConfig createConfig(Map objectModel, Parameters parameters) { + // create and set the jxpathContext... + Object flowContext = FlowHelper.getContextObject(objectModel); + WebContinuation wk = FlowHelper.getWebContinuation(objectModel); + JXPathContext jxpc = JXPathContext.newContext(flowContext); + // We manually create a cocoon object here to provide the same way + // of accessing things as in the jxtg + // as soon as we have our unified om, we should use that + Request request = ObjectModelHelper.getRequest(objectModel); + Session session = request.getSession(false); + final Map cocoonOM = new HashMap(); + cocoonOM.put("continuation", wk); + cocoonOM.put("request", request); + if ( session != null ) { + cocoonOM.put("session", session); + } + cocoonOM.put("parameters", parameters); + + FormsVariables vars = new FormsVariables(); + vars.declareVariable("cocoon", cocoonOM); + // These four are deprecated! + vars.declareVariable("continuation", wk); + vars.declareVariable("request", request); + vars.declareVariable("session", session); + vars.declareVariable("parameters", parameters); + vars.addDeprecatedVariable("continuation"); + vars.addDeprecatedVariable("request"); + vars.addDeprecatedVariable("session"); + vars.addDeprecatedVariable("parameters"); + jxpc.setVariables(vars); + + Locale localeParameter = null; + String localeStr = parameters.getParameter("locale", null); + if (localeStr != null) { + localeParameter = I18nUtils.parseLocale(localeStr); + } + + String attributeName = parameters.getParameter("attribute-name", null); + String actionExpression = parameters.getParameter("form-action", null); + String formMethod = parameters.getParameter("form-method", null); + //TODO (20031223 mpo)think about adding form-encoding for the Generator. + // Note generator will also need some text to go on the submit-button? + // Alternative to adding more here is to apply xinclude ? + + return new FormsPipelineConfig(jxpc, request, localeParameter, + attributeName, actionExpression, formMethod); + } + + /** + * Overloads {@link #findForm(String)} by setting the jxpath-expression to null + */ + public Form findForm() throws SAXException { + return this.findForm(null); + } + + /** + * Finds the form from the current request-context based on the settings of + * this configuration object. The fall-back search-procedure is as follows: + *
  1. Use the provided jxpathExpression (if not null)
  2. + *
  3. Use the setting of the 'attribute-name' parameter on the request
  4. + *
  5. Obtain the form from it's default location in the flow context
  6. + *
+ * + * @param jxpathExpression that should be pointing to the form + * @return the found form if found + * @throws SAXException in any of the following cases: + *
  • The provided jxpathExpression (if not null) not point to a + * {@link Form} instance.
  • + *
  • The request is not holding a {@link Form} instance under the key + * specified by 'attribute-name' (if specified)
  • + *
  • Both jxpathExpression and 'attribute-name' were not specified AND + * also the default location was not holding a valid {@link Form} instance.
  • + * + */ + public Form findForm(String jxpathExpression) throws SAXException { + Object form = null; + if (jxpathExpression != null) { + form = this.jxpathContext.getValue(jxpathExpression); + if (form == null) { + throw new SAXException("No Cocoon Form found at location \"" + jxpathExpression + "\"."); + } else if (!(form instanceof Form)) { + throw new SAXException("Object returned by expression \"" + jxpathExpression + "\" is not a Cocoon Form."); + } + } else if (this.attributeName != null) { // then see if an attribute-name was specified + form = this.request.getAttribute(this.attributeName); + if (form == null) { + throw new SAXException("No Cocoon Form found in request attribute with name \"" + this.attributeName + "\""); + } else if (!(form instanceof Form)) { + throw new SAXException("Object found in request (attribute = '" + this.attributeName + "') is not a Cocoon Form."); + } + } else { // and then see if we got a form from the flow + jxpathExpression = "/" + FormsPipelineConfig.CFORMSKEY; + try { + form = this.jxpathContext.getValue(jxpathExpression); + } catch (JXPathException e) { /* do nothing */ } + if (form == null) { + throw new SAXException("No Cocoon Form found."); + } + } + return (Form)form; + } + + /** + * Replaces JXPath expressions embedded inside #{ and } by their value. + * This will parse the passed String looking for #{} occurences and then + * uses the {@link #evaluateExpression(String)} to evaluate the found expression. + * + * @return the original String with it's #{}-parts replaced by the evaulated results. + */ + public String translateText(String original) { + if (original==null) { + return null; + } + + StringBuffer expression; + StringBuffer translated = new StringBuffer(); + StringReader in = new StringReader(original); + int chr; + try { + while ((chr = in.read()) != -1) { + char c = (char) chr; + if (c == '#') { + chr = in.read(); + if (chr != -1) { + c = (char) chr; + if (c == '{') { + expression = new StringBuffer(); + boolean more = true; + while ( more ) { + more = false; + if ((chr = in.read()) != -1) { + c = (char)chr; + if (c != '}') { + expression.append(c); + more = true; + } else { + translated.append(evaluateExpression(expression.toString()).toString()); + } + } else { + translated.append('#').append('{').append(expression); + } + } + } + } else { + translated.append((char) chr); + } + } else { + translated.append(c); + } + } + } catch (IOException ignored) { + ignored.printStackTrace(); + } + return translated.toString(); + } + + /** + * Evaluates the passed xpath expression using the internal jxpath context + * holding the declared variables: + *
    1. continuation: as made available by flowscript
    2. + *
    3. request: as present in the cocoon processing environment
    4. + *
    5. session: as present in the cocoon processing environment
    6. + *
    7. parameters: as present in the cocoon sitemap node of the pipeline component
    + * + * @param expression + * @return the object-value resulting the expression evaluation. + */ + public Object evaluateExpression(String expression) { + return this.jxpathContext.getValue(expression); + } + + public Locale getLocale() { + return locale; + } + + public void setLocale(Locale locale) { + this.locale = locale; + } + + public Locale getLocaleParameter() { + return localeParameter; + } + + /** + * The value for the wi:form-generated/@action. + * Note: wi:form-template copies this from its wt:form-template counterpart. + * + * @return the {@link #translateText(String)} result of the 'form-action' sitemap + * parameter to the pipeline component, or null if that parameter was not set. + */ + public String getFormAction() { + return formAction; + } + + /** + * The value for the wi:form-generated/@method. + * Note: wi:form-template copies this from its wt:form-template counterpart. + * + * @return the value of the 'form-method' sitemap parameter to the pipeline + * component. (or 'null' if it was not set.) + */ + public String getFormMethod() { + return formMethod; + } + + + /** + * Sets the form method to use in the generator/transformer that uses this. + * + * @param method to use in the generated form should be "POST", "GET" or null + */ + public void setFormMethod(String method) { + this.formMethod = method; + } + + /** + * The grouped attributes to set on the wi:form-generated element. + * Note: wi:form-template copies this from its wt:form-template counterpart. + * + * @see #getFormAction() + * @see #getFormMethod() + */ + public Attributes getFormAttributes() { + AttributesImpl attrs = new org.apache.cocoon.xml.AttributesImpl(); + addFormAttributes(attrs); + return attrs; + } + + public void addFormAttributes(AttributesImpl attrs) { + if (getFormAction() != null) { + attrs.addAttribute("", "action", "action", "CDATA", getFormAction()); + } + if (getFormMethod() != null){ + attrs.addAttribute("", "method", "method", "CDATA", getFormMethod()); + } + } + + public static final class FormsVariables implements Variables { + + final Map vars = new HashMap(); + final List deprecatedNames = new ArrayList(); + + public void addDeprecatedVariable(String name) { + this.deprecatedNames.add(name); + } + + /* (non-Javadoc) + * @see org.apache.commons.jxpath.Variables#declareVariable(java.lang.String, java.lang.Object) + */ + public void declareVariable(String name, Object value) { + this.vars.put(name, value); + } + + /* (non-Javadoc) + * @see org.apache.commons.jxpath.Variables#getVariable(java.lang.String) + */ + public Object getVariable(String name) { + Object value = this.vars.get(name); + if ( deprecatedNames.contains(name) ) { + Deprecation.logger.warn("CForms: usage of the variable '" + name + "' is deprecated."+ + "Please use 'cocoon/" + name + "' instead. The usage of just '"+ + name+"' will be removed in Cocoon 2.2."); + } + return value; + } + + /* (non-Javadoc) + * @see org.apache.commons.jxpath.Variables#isDeclaredVariable(java.lang.String) + */ + public boolean isDeclaredVariable(String name) { + return this.vars.containsKey(name); + } + + /* (non-Javadoc) + * @see org.apache.commons.jxpath.Variables#undeclareVariable(java.lang.String) + */ + public void undeclareVariable(String name) { + this.vars.remove(name); + } + } +} Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/transformation/FormsPipelineConfig.java ------------------------------------------------------------------------------ svn:eol-style = native Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/transformation/FormsTemplateTransformer.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/FormsTemplateTransformer.java?rev=330548&view=auto ============================================================================== --- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/transformation/FormsTemplateTransformer.java (added) +++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/transformation/FormsTemplateTransformer.java Thu Nov 3 05:41:06 2005 @@ -0,0 +1,42 @@ +/* + * 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.ProcessingException; +import org.apache.cocoon.environment.SourceResolver; +import org.apache.cocoon.transformation.Transformer; + +import org.apache.avalon.framework.parameters.Parameters; + +import org.xml.sax.SAXException; + +import java.io.IOException; +import java.util.Map; + +/** + * See description of {@link EffectWidgetReplacingPipe}. + * + * @version $Id: FormsTemplateTransformer.java 170955 2005-05-19 16:28:58Z vgritsenko $ + */ +public class FormsTemplateTransformer extends EffectWidgetReplacingPipe implements Transformer { + + public void setup(SourceResolver resolver, Map objectModel, String src, Parameters parameters) + throws ProcessingException, SAXException, IOException { + + FormsPipelineConfig pipeContext = FormsPipelineConfig.createConfig(objectModel, parameters); + super.init(null, pipeContext); + } +} Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/transformation/FormsTemplateTransformer.java ------------------------------------------------------------------------------ svn:eol-style = native Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/CombiningMap.java URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/CombiningMap.java?rev=330548&view=auto ============================================================================== --- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/CombiningMap.java (added) +++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/CombiningMap.java Thu Nov 3 05:41:06 2005 @@ -0,0 +1,176 @@ +/* + * 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.util; + +import java.util.AbstractMap; +import java.util.AbstractSet; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; + +/** + * A read-only implementation of Map that combines several other maps. + * + * @version $Id: CombiningMap.java 327577 2005-10-21 20:59:14Z sylvain $ + */ +public class CombiningMap extends AbstractMap { + + private List maps = new ArrayList(); + private boolean locked = false; + + /** + * Adds a Map in the combined map, with the lowest lookup priority. + *

    + * New maps cannot be added if this object was already iterated. + * + * @param map the new map + * @return this object, as a convenience to write combiner.add(map1).add(map2).add(map3) + * @throw IllegalStateException if this object was already iterated. + */ + public CombiningMap add(Map map) { + if (locked) { + throw new IllegalStateException("Cannot add new Maps to a CombiningMap once it has been iterated"); + } + maps.add(map); + + return this; + } + + public Object get(Object key) { + // Faster implemetation than the default in AbstractMap + for (int i = 0; i < maps.size(); i++) { + Map map = (Map)maps.get(i); + Object result = map.get(key); + + if (result != null) { + return result; + } + + if (map.containsKey(key)) { + return null; + } + } + + return null; + } + + public boolean containsKey(Object key) { + // Faster implemetation than the default in AbstractMap + for (int i = 0; i < maps.size(); i++) { + Map map = (Map)maps.get(i); + if (map.containsKey(key)) { + return true; + } + } + return false; + } + + public Set entrySet() { + locked = true; + return new CombiningEntrySet(); + } + + private class CombiningEntrySet extends AbstractSet { + + public Iterator iterator() { + return new CombiningIterator(); + } + + /** + * Super inefficient way, but this implementation is meant to be super-lightweight + * and efficient at iterations. + */ + public int size() { + + int size = 0; + Iterator iter = iterator(); + while (iter.hasNext()) { + size++; + iter.next(); + } + return size; + } + } + + private class CombiningIterator implements Iterator { + + private int index; + private Iterator delegate; + private Map.Entry next; + + public CombiningIterator() { + // Initialize the first result + if (!maps.isEmpty()) { + delegate = ((Map)maps.get(0)).entrySet().iterator(); + if (delegate.hasNext()) { + next = (Map.Entry)delegate.next(); + } + } + + } + public boolean hasNext() { + return next != null; + } + + public Object next() { + if (next == null) { + throw new NoSuchElementException(); + } + Object result = next; + fetchNext(); + return result; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + + private void fetchNext() { + boolean skip; + do { + // Get an iterator that has more values + while (delegate != null && !delegate.hasNext()) { + // Ended iteration on the previous map + index++; + if (index < maps.size()) { + delegate = ((Map)maps.get(index)).entrySet().iterator(); + } else { + // Iteration finished + next = null; + delegate = null; + return; + } + } + + // Get the next entry + next = (Map.Entry)delegate.next(); + + // Skip it if its key doesn't exist in the previous Maps + Object key = next.getKey(); + skip = false; + for (int i = 0; i < index-1; i++) { + if (((Map)maps.get(i)).containsKey(key)) { + skip = true; + continue; + } + } + } while(skip); + } + } +} Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/CombiningMap.java ------------------------------------------------------------------------------ svn:eol-style = native Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/ContainerWidgetAsMap.java URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/ContainerWidgetAsMap.java?rev=330548&view=auto ============================================================================== --- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/ContainerWidgetAsMap.java (added) +++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/ContainerWidgetAsMap.java Thu Nov 3 05:41:06 2005 @@ -0,0 +1,226 @@ +/* + * 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.util; + +import java.util.AbstractMap; +import java.util.AbstractSet; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.cocoon.forms.formmodel.AbstractContainerWidget; +import org.apache.cocoon.forms.formmodel.ContainerWidget; +import org.apache.cocoon.forms.formmodel.Repeater; +import org.apache.cocoon.forms.formmodel.Widget; +import org.apache.commons.collections.iterators.AbstractIteratorDecorator; + +/** + * A Map view of a container widget, keys being children names and values either + * maps (for container children), objects (for terminal children) or lists (for repeaters). + *

    + * The returned map is non-modifiable, except using the put() method, which much + * refer to an existing child widget, and putAll(Map) that will silently ignore keys + * that don't refer to existing child widgets. + *

    + * Also, this map accepts getting and setting values for keys that correspond to value-less widgets + * such as {@link org.apache.cocoon.forms.formmodel.Action}. The result in that case is always + * null. This is to allow global retrieving or filling of the map values. + * + * @since 2.1.8 + * @version $Id: ContainerWidgetAsMap.java 327865 2005-10-23 22:03:06Z sylvain $ + */ +public class ContainerWidgetAsMap extends AbstractMap { + private AbstractContainerWidget container; + private boolean lowerCase; + + /** + * Wraps a container widget in a Map. + *

    + * The keysToLowerCase argument specifies if input keys given in get(), + * put() and putAll() should be converted to lower case before searching for + * the corresponding widget. This feature allows to directly feed widgets with Maps coming + * from JDBC resultset rows where keys are uppercase (see JDBI). + * + * @param container the container to wrap + * @param keysToLowerCase should we convert keys to lower case? + */ + public ContainerWidgetAsMap(AbstractContainerWidget container, boolean keysToLowerCase) { + this.container = container; + this.lowerCase = keysToLowerCase; + } + + /** + * Same as ContainerWidgetAsMap(container, false) + */ + public ContainerWidgetAsMap(AbstractContainerWidget container) { + this(container, false); + } + + /** + * Get the container widget that is wrapped by this Map. + * + * @return the wrapped {@link ContainerWidget} + */ + public ContainerWidget getWidget() { + return this.container; + } + + /** + * Get a widget relative to the container wrapped by this Map + * + * @param path a widget lookup path + * @return the widget pointed to by path or null if it doesn't exist. + * @see Widget#lookupWidget(String) + */ + public Widget getWidget(String path) { + return this.container.lookupWidget(path); + } + + /** + * Put a value in a child widget. The value must be compatible with the datatype + * expected by the child widget. In the case of repeaters and containers, this + * datatype is Collection and Map respectively, which + * will be used to fill the rows and child widgets. + *

    + * Note also that the contract of put requires the previous value + * to be returned. In the case of repeaters and containers, the value is a live + * wrapper around the actual widget, meaning that it's not different from the + * current value. + */ + public Object put(Object key, Object value) { + String name = (String)key; + if (lowerCase) name = name.toLowerCase(); + + Widget w = container.getChild(name); + if (w != null) { + return setValue(w, value); + } else { + throw new UnsupportedOperationException(container + " has no child named '" + key + "'"); + } + } + + public void putAll(Map map) { + Iterator iter = map.entrySet().iterator(); + while(iter.hasNext()) { + Map.Entry entry = (Map.Entry)iter.next(); + String name = (String)entry.getKey(); + if (lowerCase) name = name.toLowerCase(); + Widget w = container.getChild(name); + if (w != null) { + setValue(w, entry.getValue()); + } + } + } + + public Object get(Object key) { + String name = (String)key; + if (lowerCase) name = name.toLowerCase(); + Widget w = container.getChild(name); + return w == null ? null : asValueOrMap(w); + } + + public Set entrySet() { + return new ContainerEntrySet(); + } + + private Object asValueOrMap(Widget w) { + if (w instanceof Repeater) { + return new RepeaterAsList((Repeater)w, lowerCase); + } else if (w instanceof AbstractContainerWidget) { + return new ContainerWidgetAsMap((AbstractContainerWidget)w, lowerCase); + } else { + try { + return w.getValue(); + } catch (UnsupportedOperationException uoe) { + // This widget doesn't hold a value + return null; + } + } + } + + /** + * Set a widget's value and returns the previous value as required by put(). + */ + private Object setValue(Widget w, Object value) { + if (w instanceof Repeater) { + // Must be a collection + if (!(value instanceof Collection)) { + throw new IllegalArgumentException("A repeater cannot be filled with " + value); + } + List result = new RepeaterAsList((Repeater)w, lowerCase); + result.addAll((Collection)value); + return result; + + } else if (w instanceof AbstractContainerWidget) { + // Must be a map + if (!(value instanceof Map)) { + throw new IllegalArgumentException("A container cannot be filled with " + value); + } + Map result = new ContainerWidgetAsMap((AbstractContainerWidget)w); + result.putAll((Map)value); + return result; + } else { + try { + Object result = w.getValue(); + w.setValue(value); + return result; + } catch (UnsupportedOperationException uoe) { + // This widget doesn't hold a value + return null; + } + } + } + + private class ContainerEntrySet extends AbstractSet { + public Iterator iterator() { + return new ContainerEntryIterator(); + } + + public int size() { + return container.getSize(); + } + } + + private class ContainerEntryIterator extends AbstractIteratorDecorator { + public ContainerEntryIterator() { + super(container.getChildren()); + } + + public Object next() { + return new ContainerEntry((Widget)super.next()); + } + } + + private class ContainerEntry implements Map.Entry { + Widget widget; + public ContainerEntry(Widget w) { + widget = w; + } + public Object getKey() { + return widget.getName(); + } + public Object getValue() { + return asValueOrMap(widget); + } + public Object setValue(Object value) { + Object result = asValueOrMap(widget); + ContainerWidgetAsMap.this.setValue(widget, value); + return result; + } + } +} \ No newline at end of file Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/ContainerWidgetAsMap.java ------------------------------------------------------------------------------ svn:eol-style = native Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/DomHelper.java URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/DomHelper.java?rev=330548&view=auto ============================================================================== --- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/DomHelper.java (added) +++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/DomHelper.java Thu Nov 3 05:41:06 2005 @@ -0,0 +1,361 @@ +/* + * 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.util; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; + +import org.apache.avalon.framework.service.ServiceException; +import org.apache.avalon.framework.service.ServiceManager; +import org.apache.cocoon.util.location.Location; +import org.apache.cocoon.util.location.LocationAttributes; +import org.apache.cocoon.xml.SaxBuffer; +import org.apache.cocoon.xml.dom.DOMBuilder; +import org.apache.cocoon.xml.dom.DOMStreamer; +import org.apache.commons.lang.BooleanUtils; +import org.apache.excalibur.xml.sax.SAXParser; +import org.apache.excalibur.xml.sax.XMLizable; +import org.w3c.dom.Attr; +import org.w3c.dom.CDATASection; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.Text; +import org.xml.sax.ContentHandler; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.SAXNotSupportedException; + +/** + * Helper class to create and retrieve information from DOM-trees. It provides + * some functionality comparable to what's found in Avalon's Configuration + * objects. These lasts one could however not be used by Cocoon Forms because they + * don't provide an accurate model of an XML file (no mixed content, + * no namespaced attributes, no namespace declarations, ...). + * + *

    This class depends specifically on the Xerces DOM implementation to be + * able to provide information about the location of elements in their source + * XML file. See the {@link #getLocation(Element)} method. + * + * @version $Id: DomHelper.java 289538 2005-09-16 13:46:22Z sylvain $ + */ +public class DomHelper { + + public static final String XMLNS_URI = "http://www.w3.org/2000/xmlns/"; + + public static Location getLocationObject(Element element) { + return LocationAttributes.getLocation(element); + } + + /** + * Retrieves the location of an element node in the source file from which + * the Document was created. This will only work for Document's created + * with the method {@link #parse(InputSource)} of this class. + */ + public static String getLocation(Element element) { + return LocationAttributes.getLocationString(element); + } + + public static String getSystemIdLocation(Element element) { + return LocationAttributes.getURI(element); + } + + public static int getLineLocation(Element element) { + return LocationAttributes.getLine(element); + } + + public static int getColumnLocation(Element element) { + return LocationAttributes.getColumn(element); + } + + /** + * Returns all Element children of an Element that belong to the given + * namespace. + */ + public static Element[] getChildElements(Element element, String namespace) { + ArrayList elements = new ArrayList(); + NodeList nodeList = element.getChildNodes(); + for (int i = 0; i < nodeList.getLength(); i++) { + Node node = nodeList.item(i); + if (node instanceof Element + && namespace.equals(node.getNamespaceURI())) + elements.add(node); + } + return (Element[])elements.toArray(new Element[elements.size()]); + } + + /** + * Returns all Element children of an Element that belong to the given + * namespace and have the given local name. + */ + public static Element[] getChildElements(Element element, + String namespace, String localName) { + ArrayList elements = new ArrayList(); + NodeList nodeList = element.getChildNodes(); + for (int i = 0; i < nodeList.getLength(); i++) { + Node node = nodeList.item(i); + if (node instanceof Element + && namespace.equals(node.getNamespaceURI()) + && localName.equals(node.getLocalName())) { + elements.add(node); + } + } + return (Element[])elements.toArray(new Element[elements.size()]); + } + + /** + * Returns the first child element with the given namespace and localName, + * or null if there is no such element. + */ + public static Element getChildElement(Element element, String namespace, + String localName) { + Element node = null; + try { + node = getChildElement(element, namespace, localName, false); + } catch (Exception e) { + node = null; + } + return node; + } + + /** + * Returns the first child element with the given namespace and localName, + * or null if there is no such element and required flag is unset or + * throws an Exception if the "required" flag is set. + */ + public static Element getChildElement(Element element, String namespace, + String localName, boolean required) throws Exception { + NodeList nodeList = element.getChildNodes(); + for (int i = 0; i < nodeList.getLength(); i++) { + Node node = nodeList.item(i); + if (node instanceof Element + && namespace.equals(node.getNamespaceURI()) + && localName.equals(node.getLocalName())) { + return (Element)node; + } + } + if (required) { + throw new Exception("Missing element \"" + localName + + "\" as child of element \"" + element.getTagName() + + "\" at " + DomHelper.getLocation(element)); + } else { + return null; + } + } + + /** + * Returns the value of an element's attribute, but throws an exception + * if the element has no such attribute. + */ + public static String getAttribute(Element element, String attributeName) + throws Exception { + String attrValue = element.getAttribute(attributeName); + if (attrValue.equals("")) { + throw new Exception("Missing attribute \"" + attributeName + + "\" on element \"" + element.getTagName() + + "\" at " + getLocation(element)); + } + return attrValue; + } + + /** + * Returns the value of an element's attribute, or a default value if the + * element has no such attribute. + */ + public static String getAttribute(Element element, String attributeName, + String defaultValue) { + String attrValue = element.getAttribute(attributeName); + if (attrValue.equals("")) { + return defaultValue; + } + return attrValue; + } + + public static int getAttributeAsInteger(Element element, + String attributeName) throws Exception { + String attrValue = getAttribute(element, attributeName); + try { + return Integer.parseInt(attrValue); + } catch (NumberFormatException e) { + throw new Exception("Cannot parse the value \"" + attrValue + + "\" as an integer in the attribute \"" + attributeName + + "\" on the element \"" + element.getTagName() + + "\" at " + getLocation(element)); + } + } + + public static int getAttributeAsInteger(Element element, + String attributeName, int defaultValue) throws Exception { + String attrValue = element.getAttribute(attributeName); + if (attrValue.equals("")) { + return defaultValue; + } else { + try { + return Integer.parseInt(attrValue); + } catch (NumberFormatException e) { + throw new Exception("Cannot parse the value \"" + attrValue + + "\" as an integer in the attribute \"" + + attributeName + "\" on the element \"" + + element.getTagName() + "\" at " + + getLocation(element)); + } + } + } + + public static boolean getAttributeAsBoolean(Element element, + String attributeName, boolean defaultValue) { + String attrValue = element.getAttribute(attributeName); + Boolean result; + try { + result = BooleanUtils.toBooleanObject(attrValue, "true", "false", null); + } catch (IllegalArgumentException iae) { + result = null; + } + if (result != null) { + return result.booleanValue(); + } + try { + result = BooleanUtils.toBooleanObject(attrValue, "yes", "no", null); + } catch (IllegalArgumentException iae) { + result = null; + } + if (result != null) { + return result.booleanValue(); + } + return defaultValue; + } + + public static String getElementText(Element element) { + StringBuffer value = new StringBuffer(); + NodeList nodeList = element.getChildNodes(); + for (int i = 0; i < nodeList.getLength(); i++) { + Node node = nodeList.item(i); + if (node instanceof Text || node instanceof CDATASection) { + value.append(node.getNodeValue()); + } + } + return value.toString(); + } + + /** + * Returns the content of the given Element as an object implementing the + * XMLizable interface. Practically speaking, the implementation uses the + * {@link SaxBuffer} class. The XMLizable object will be a standalone blurb + * of SAX events, not producing start/endDocument calls and containing all + * necessary namespace declarations. + */ + public static XMLizable compileElementContent(Element element) { + SaxBuffer saxBuffer = new SaxBuffer(); + DOMStreamer domStreamer = new DOMStreamer(); + domStreamer.setContentHandler(saxBuffer); + + NodeList childNodes = element.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) { + try { + domStreamer.stream(childNodes.item(i)); + } catch (SAXException e) { + // It's unlikely that an exception will occur here, + // so use a runtime exception + throw new RuntimeException( + "Error in DomHelper.compileElementContent: " + + e.toString()); + } + } + return saxBuffer; + } + + /** + * Creates a W3C Document that remembers the location of each element in + * the source file. The location of element nodes can then be retrieved + * using the {@link #getLocation(Element)} method. + * + * @param inputSource the inputSource to read the document from + * @param manager the service manager where to lookup the entity resolver + */ + public static Document parse(InputSource inputSource, ServiceManager manager) + throws SAXException, SAXNotSupportedException, IOException, ServiceException { + + SAXParser parser = (SAXParser)manager.lookup(SAXParser.ROLE); + DOMBuilder builder = new DOMBuilder(); + + // Enhance the sax stream with location information + ContentHandler locationHandler = new LocationAttributes.Pipe(builder); + + try { + parser.parse(inputSource, locationHandler); + } finally { + manager.release(parser); + } + + return builder.getDocument(); + } + + public static Map getLocalNSDeclarations(Element elm) + { + return addLocalNSDeclarations(elm, null); + } + + private static Map addLocalNSDeclarations(Element elm, Map nsDeclarations) + { + NamedNodeMap atts = elm.getAttributes(); + int attsSize = atts.getLength(); + + for (int i = 0; i < attsSize; i++) + { + Attr attr = (Attr)atts.item(i); + if (XMLNS_URI.equals(attr.getNamespaceURI())) + { + String nsUri = attr.getValue(); + String pfx = attr.getLocalName(); + if (nsDeclarations == null) + nsDeclarations = new HashMap(); + nsDeclarations.put(nsUri, pfx); + } + } + return nsDeclarations; } + + public static Map getInheritedNSDeclarations(Element elm) + { + List ancestorsAndSelf = new LinkedList(); + Element current = elm; + while (current != null) + { + ancestorsAndSelf.add(current); + Node parent = current.getParentNode(); + if (parent.getNodeType() == Node.ELEMENT_NODE) + current = (Element)parent; + else + current = null; + } + + Map nsDeclarations = null; + ListIterator iter = ancestorsAndSelf.listIterator(ancestorsAndSelf.size()); + while (iter.hasPrevious()) + { + Element element = (Element) iter.previous(); + nsDeclarations = addLocalNSDeclarations(element, nsDeclarations); + } + + return nsDeclarations; + } +} Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/DomHelper.java ------------------------------------------------------------------------------ svn:eol-style = native Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/I18nMessage.java URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/I18nMessage.java?rev=330548&view=auto ============================================================================== --- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/I18nMessage.java (added) +++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/I18nMessage.java Thu Nov 3 05:41:06 2005 @@ -0,0 +1,149 @@ +/* + * 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.util; + +import java.util.Arrays; + +import org.apache.cocoon.transformation.I18nTransformer; +import org.apache.cocoon.xml.AttributesImpl; +import org.apache.cocoon.xml.XMLUtils; +import org.apache.commons.lang.ObjectUtils; +import org.apache.excalibur.xml.sax.XMLizable; +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; + +/** + * A XMLizable implementation that will produce SAX events for the + * I18nTransformer in its toSAX method, based on the information + * given in the constructor. + * + *

    This generates an autonomous SAX-blurb, i.e. all necessary namespace + * declarations will be made, and no start/endDocument events will be generated. + * + * @version $Id: I18nMessage.java 177978 2005-05-23 13:27:51Z sylvain $ + */ +public class I18nMessage implements XMLizable { + private String key; + private String catalogue; + private String[] parameters; + private boolean[] keys; + + /** + * @param key a message key, to be translated by the I18nTransformer + */ + public I18nMessage(String key) { + this(key, (String) null); + } + + /** + * @param key a message key, to be translated by the I18nTransformer + * @param catalogue a named I18nTransformer catalogue to use + */ + public I18nMessage(String key, String catalogue) { + this.key = key; + this.catalogue = catalogue; + } + + /** + * @param key a message key, to be translated by the I18nTransformer + * @param parameters parameters to be substituted in the errorMessage (will be + * done by the I18nTransformer) + */ + public I18nMessage(String key, String[] parameters) { + this(key, parameters, (String)null); + } + + /** + * @param key a message key, to be translated by the I18nTransformer + * @param parameters parameters to be substituted in the errorMessage (will be + * done by the I18nTransformer) + * @param catalogue a named I18nTransformer catalogue to use + */ + public I18nMessage(String key, String[] parameters, String catalogue) { + this.key = key; + this.parameters = parameters; + this.catalogue = catalogue; + } + + /** + * @param key a message key, to be translated by the I18nTransformer + * @param parameters parameters to be substituted in the errorMessage (will be + * done by the I18nTransformer) + * @param keys Each element in the keys array corresponds to a string in the parameters array + * and indicates whether that parameter is in itself again a key. + */ + public I18nMessage(String key, String[] parameters, boolean[] keys) { + this(key, parameters, keys, null); + } + + /** + * @param key a message key, to be translated by the I18nTransformer + * @param parameters parameters to be substituted in the errorMessage (will be + * done by the I18nTransformer) + * @param keys Each element in the keys array corresponds to a string in the parameters array + * and indicates whether that parameter is in itself again a key. + * @param catalogue a named I18nTransformer catalogue to use + */ + public I18nMessage(String key, String[] parameters, boolean[] keys, String catalogue) { + this.key = key; + this.parameters = parameters; + this.keys = keys; + this.catalogue = catalogue; + } + + public void toSAX(ContentHandler contentHandler) throws SAXException { + contentHandler.startPrefixMapping("i18n", I18nTransformer.I18N_NAMESPACE_URI); + if (parameters != null) { + contentHandler.startElement(I18nTransformer.I18N_NAMESPACE_URI, I18nTransformer.I18N_TRANSLATE_ELEMENT, "i18n:" + I18nTransformer.I18N_TRANSLATE_ELEMENT, XMLUtils.EMPTY_ATTRIBUTES); + } + + AttributesImpl i18nAttrs = new AttributesImpl(); + if (catalogue != null) { + i18nAttrs.addCDATAAttribute(I18nTransformer.I18N_NAMESPACE_URI, I18nTransformer.I18N_CATALOGUE_ATTRIBUTE, "i18n:" + I18nTransformer.I18N_CATALOGUE_ATTRIBUTE, catalogue); + } + + contentHandler.startElement(I18nTransformer.I18N_NAMESPACE_URI, I18nTransformer.I18N_TEXT_ELEMENT, "i18n:" + I18nTransformer.I18N_TEXT_ELEMENT, i18nAttrs); + contentHandler.characters(key.toCharArray(), 0, key.length()); + contentHandler.endElement(I18nTransformer.I18N_NAMESPACE_URI, I18nTransformer.I18N_TEXT_ELEMENT, "i18n:" + I18nTransformer.I18N_TEXT_ELEMENT); + + // the parameters + if (parameters != null) { + for (int i = 0; i < parameters.length; i++) { + contentHandler.startElement(I18nTransformer.I18N_NAMESPACE_URI, I18nTransformer.I18N_PARAM_ELEMENT, "i18n:" + I18nTransformer.I18N_PARAM_ELEMENT, XMLUtils.EMPTY_ATTRIBUTES); + if (keys != null && keys[i]) + contentHandler.startElement(I18nTransformer.I18N_NAMESPACE_URI, I18nTransformer.I18N_TEXT_ELEMENT, "i18n:" + I18nTransformer.I18N_TEXT_ELEMENT, i18nAttrs); + contentHandler.characters(parameters[i].toCharArray(), 0, parameters[i].length()); + if (keys != null && keys[i]) + contentHandler.endElement(I18nTransformer.I18N_NAMESPACE_URI, I18nTransformer.I18N_TEXT_ELEMENT, "i18n:" + I18nTransformer.I18N_TEXT_ELEMENT); + contentHandler.endElement(I18nTransformer.I18N_NAMESPACE_URI, I18nTransformer.I18N_PARAM_ELEMENT, "i18n:" + I18nTransformer.I18N_PARAM_ELEMENT); + } + contentHandler.endElement(I18nTransformer.I18N_NAMESPACE_URI, I18nTransformer.I18N_TRANSLATE_ELEMENT, "i18n:" + I18nTransformer.I18N_TRANSLATE_ELEMENT); + } + contentHandler.endPrefixMapping("i18n"); + } + + public boolean equals(Object obj) { + if (obj instanceof I18nMessage) { + I18nMessage other = (I18nMessage)obj; + return ObjectUtils.equals(this.catalogue, other.catalogue) && + ObjectUtils.equals(this.key, other.key) && + Arrays.equals(this.keys, other.keys) && + Arrays.equals(this.parameters, other.parameters); + } else { + return false; + } + } +} Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/I18nMessage.java ------------------------------------------------------------------------------ svn:eol-style = native Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/JavaScriptHelper.java URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/JavaScriptHelper.java?rev=330548&view=auto ============================================================================== --- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/JavaScriptHelper.java (added) +++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/JavaScriptHelper.java Thu Nov 3 05:41:06 2005 @@ -0,0 +1,223 @@ +/* + * 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.util; + +import java.io.IOException; +import java.io.StringReader; +import java.util.Iterator; +import java.util.Map; + +import org.apache.avalon.framework.CascadingRuntimeException; +import org.apache.cocoon.components.flow.FlowHelper; +import org.apache.cocoon.components.flow.javascript.fom.FOM_JavaScriptFlowHelper; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.Function; +import org.mozilla.javascript.JavaScriptException; +import org.mozilla.javascript.Script; +import org.mozilla.javascript.Scriptable; +import org.w3c.dom.Element; + +/** + * Helper methods to use JavaScript in various locations of the Cocoon Forms configuration files + * such as event listeners and bindings. + * + * @author Sylvain Wallez + * @version $Id: JavaScriptHelper.java 289538 2005-09-16 13:46:22Z sylvain $ + */ +public class JavaScriptHelper { + + /** + * A shared root scope, avoiding to recreate a new one each time. + */ + private static Scriptable _rootScope = null; + + /** + * Build a script with the content of a DOM element. + * + * @param element the element containing the script + * @return the compiled script + * @throws IOException + */ + public static Script buildScript(Element element) throws IOException { + String jsText = DomHelper.getElementText(element); + String sourceName = DomHelper.getSystemIdLocation(element); + + Context ctx = Context.enter(); + Script script; + try { + script = ctx.compileReader( + getRootScope(), //scope + new StringReader(jsText), // in + sourceName == null ? "" : sourceName, // sourceName + DomHelper.getLineLocation(element), // lineNo + null // securityDomain + ); + } finally { + Context.exit(); + } + return script; + } + + /** + * Build a function with the content of a DOM element. + * + * @param element the element containing the function body + * @param name the name of the function + * @param argumentNames names of the function arguments + * @return the compiled function + * @throws IOException + */ + public static Function buildFunction(Element element, String name, String[] argumentNames) throws IOException { + // Enclose the script text with a function declaration + StringBuffer buffer = new StringBuffer("function ").append(name).append("("); + for (int i = 0; i < argumentNames.length; i++) { + if (i > 0) { + buffer.append(','); + } + buffer.append(argumentNames[i]); + } + buffer.append(") {\n").append(DomHelper.getElementText(element)).append("\n}"); + + String jsText = buffer.toString(); + String sourceName = DomHelper.getSystemIdLocation(element); + + Context ctx = Context.enter(); + Function func; + try { + func = ctx.compileFunction( + getRootScope(), //scope + jsText, // in + sourceName == null ? "" : sourceName, // sourceName + DomHelper.getLineLocation(element) - 1, // lineNo, "-1" because we added "function..." + null // securityDomain + ); + } finally { + Context.exit(); + } + return func; + } + + /** + * Get a root scope for building child scopes. + * + * @return an appropriate root scope + */ + public static Scriptable getRootScope() { + // FIXME: TemplateOMH should be used in 2.2 + //return TemplateObjectModelHelper.getScope(); + + + if (_rootScope == null) { + // Create it if never used up to now + Context ctx = Context.enter(); + try { + _rootScope = ctx.initStandardObjects(null); + } finally { + Context.exit(); + } + } + return _rootScope; + } + + /** + * Get a parent scope for building a child scope. The request is searched for an existing scope + * that can be provided by a flowscript higher in the call stack, giving visibility to flowscript + * functions and global (session) variables. + * + * @param objectModel the object model where the flowscript scope will be searched (can be null). + * @return an appropriate parent scope. + */ + public static Scriptable getParentScope(Map objectModel) { + // Try to get the flowscript scope + Scriptable parentScope = null; + if (objectModel != null) { + parentScope = FOM_JavaScriptFlowHelper.getFOM_FlowScope(objectModel); + } + + if (parentScope != null) { + return parentScope; + } else { + return getRootScope(); + } + } + + public static Object execScript(Script script, Map values, Map objectModel) throws JavaScriptException { + Context ctx = Context.enter(); + try { + Scriptable parentScope = getParentScope(objectModel); + + // Create a new local scope + Scriptable scope; + try { + scope = ctx.newObject(parentScope); + } catch (Exception e) { + // Should normally not happen + throw new CascadingRuntimeException("Cannont create script scope", e); + } + scope.setParentScope(parentScope); + + // Populate the scope + Iterator iter = values.entrySet().iterator(); + while(iter.hasNext()) { + Map.Entry entry = (Map.Entry)iter.next(); + String key = (String)entry.getKey(); + Object value = entry.getValue(); + scope.put(key, scope, Context.toObject(value, scope)); + } + + if (objectModel != null) { + Object viewData = FlowHelper.getContextObject(objectModel); + if (viewData != null) { + scope.put("viewData", scope, Context.toObject(viewData, scope)); + } + } + + Object result = script.exec(ctx, scope); + return FlowHelper.unwrap(result); + } finally { + Context.exit(); + } + } + + public static Object callFunction(Function func, Object thisObject, Object[] arguments, Map objectModel) throws JavaScriptException { + Context ctx = Context.enter(); + try { + Scriptable scope = getParentScope(objectModel); + + if (objectModel != null) { + Object viewData = FlowHelper.getContextObject(objectModel); + if (viewData != null) { + // Create a new local scope to hold the view data + Scriptable newScope; + try { + newScope = ctx.newObject(scope); + } catch (Exception e) { + // Should normally not happen + throw new CascadingRuntimeException("Cannont create function scope", e); + } + newScope.setParentScope(scope); + scope = newScope; + + scope.put("viewData", scope, Context.toObject(viewData, scope)); + } + } + Object result = func.call(ctx, scope, thisObject == null? null: Context.toObject(thisObject, scope), arguments); + return FlowHelper.unwrap(result); + } finally { + Context.exit(); + } + } +} Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/JavaScriptHelper.java ------------------------------------------------------------------------------ svn:eol-style = native Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/RepeaterAsList.java URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/RepeaterAsList.java?rev=330548&view=auto ============================================================================== --- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/RepeaterAsList.java (added) +++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/RepeaterAsList.java Thu Nov 3 05:41:06 2005 @@ -0,0 +1,126 @@ +/* + * 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.util; + +import java.util.AbstractList; +import java.util.Map; + +import org.apache.cocoon.forms.formmodel.AbstractContainerWidget; +import org.apache.cocoon.forms.formmodel.Repeater; +import org.apache.cocoon.forms.formmodel.Widget; + +/** + * A List view of a {@link Repeater}, each element of the list being a Map + * wrapping a repeater row, as defined by {@link ContainerWidgetAsMap}. + *

    + * This implementation of list supports all methods, with the following restrictions: + *

      + *
    • values stored in the list must be Maps, that will be used with {@link ContainerWidgetAsMap#putAll(Map)} + * on the Map representation of the repeater rows,
    • + *
    • operations that involve testing equality with the list contents (e.g. contains(Object)) will + * not function properly, the Map wrapping the rows being created on demand.
    • + *
    + * + * @since 2.1.8 + * @version $Id: RepeaterAsList.java 327865 2005-10-23 22:03:06Z sylvain $ + */ +public class RepeaterAsList extends AbstractList { + + private Repeater repeater; + private boolean lowerCase; + + /** + * Create a List view around a repeater. The keysToLowerCase parameter + * specifies if Maps wrapping rows should convert input keys to lower case, as + * specified by {@link ContainerWidgetAsMap#ContainerWidgetAsMap(AbstractContainerWidget, boolean)}. + * + * @param repeater the repeater to wrap + * @param keysToLowerCase should we convert input keys to lower case? + */ + public RepeaterAsList(Repeater repeater, boolean keysToLowerCase) { + this.repeater = repeater; + this.lowerCase = keysToLowerCase; + } + + /** + * Same as RepeaterAsList(repeater, false). + */ + public RepeaterAsList(Repeater repeater) { + this(repeater, false); + } + + /** + * Get the repeater widget that is wrapped by this List. + * + * @return the wrapped {@link Repeater} + */ + public Repeater getWidget() { + return this.repeater; + } + + /** + * Get a widget relative to the repeater wrapped by this List + * + * @param path a widget lookup path + * @return the widget pointed to by path or null if it doesn't exist. + * @see Widget#lookupWidget(String) + */ + public Widget getWidget(String path) { + return this.repeater.lookupWidget(path); + } + + public Object get(int index) { + return new ContainerWidgetAsMap(repeater.getRow(index), lowerCase); + } + + public int size() { + return repeater.getSize(); + } + + public Object set(int index, Object o) { + if (o == null) { + throw new NullPointerException("Cannot set null to a repeater"); + } + if (!(o instanceof Map)) { + throw new IllegalArgumentException("Cannot set a '" + o.getClass().toString() + "' to a repeater"); + } + Map result = new ContainerWidgetAsMap(repeater.getRow(index)); + result.putAll((Map)o); + return result; + } + + public void add(int index, Object o) { + if (o == null) { + throw new NullPointerException("Cannot add null to a repeater"); + } + if (!(o instanceof Map)) { + throw new IllegalArgumentException("Cannot add a '" + o.getClass().toString() + "' to a repeater"); + } + Repeater.RepeaterRow row = repeater.addRow(index); + new ContainerWidgetAsMap(row).putAll((Map)o); + } + + public Object remove(int index) { + Map result = new ContainerWidgetAsMap(repeater.getRow(index)); + repeater.removeRow(index); + return result; + } + + // Not mandated by the abstract class, but will speed up things + public void clear() { + repeater.clear(); + } +} \ No newline at end of file Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/RepeaterAsList.java ------------------------------------------------------------------------------ svn:eol-style = native Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/SimpleServiceSelector.java URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/SimpleServiceSelector.java?rev=330548&view=auto ============================================================================== --- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/SimpleServiceSelector.java (added) +++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/SimpleServiceSelector.java Thu Nov 3 05:41:06 2005 @@ -0,0 +1,124 @@ +/* + * 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.util; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.apache.avalon.framework.activity.Disposable; +import org.apache.avalon.framework.configuration.Configurable; +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.configuration.ConfigurationException; +import org.apache.avalon.framework.logger.AbstractLogEnabled; +import org.apache.avalon.framework.logger.LogEnabled; +import org.apache.avalon.framework.service.ServiceException; +import org.apache.avalon.framework.service.ServiceManager; +import org.apache.avalon.framework.service.ServiceSelector; +import org.apache.avalon.framework.service.Serviceable; +import org.apache.avalon.framework.context.Contextualizable; +import org.apache.avalon.framework.context.Context; +import org.apache.avalon.framework.context.ContextException; +import org.apache.cocoon.components.LifecycleHelper; + +/** + * A very simple ServiceSelector for ThreadSafe services. + * + * @version $Id: SimpleServiceSelector.java 76175 2004-11-17 21:45:08Z upayavira $ + */ +public class SimpleServiceSelector extends AbstractLogEnabled implements ServiceSelector, Configurable, LogEnabled, + Serviceable, Disposable, Contextualizable { + private final String hintShortHand; + private final Class componentClass; + private Map components = new HashMap(); + private ServiceManager serviceManager; + private Context context; + + public SimpleServiceSelector(String hintShortHand, Class componentClass) { + this.hintShortHand = hintShortHand; + this.componentClass = componentClass; + } + + public void service(ServiceManager serviceManager) throws ServiceException { + this.serviceManager = serviceManager; + } + + public void contextualize(Context context) throws ContextException { + this.context = context; + } + + public void configure(Configuration configuration) throws ConfigurationException { + Configuration[] componentConfs = configuration.getChildren(hintShortHand); + for (int i = 0; i < componentConfs.length; i++) { + String name = componentConfs[i].getAttribute("name"); + String src = componentConfs[i].getAttribute("src"); + + Class clazz = null; + try { + clazz = Class.forName(src); + } catch (ClassNotFoundException e) { + throw new ConfigurationException("Class not found: " + src + ", declared at " + componentConfs[i].getLocation(), e); + } + + if (!componentClass.isAssignableFrom(clazz)) + throw new ConfigurationException("The class \"" + src + "\" is of an incorrect type, it should implement or extend " + componentClass.getName()); + + Object component = null; + try { + component = clazz.newInstance(); + LifecycleHelper.setupComponent( + component, + getLogger(), + context, + serviceManager, + componentConfs[i]); + } catch (Exception e) { + throw new ConfigurationException("Error creating " + hintShortHand + " declared at " + componentConfs[i].getLocation(), e); + } + + components.put(name, component); + } + } + + public Object select(Object hint) throws ServiceException { + if (!isSelectable(hint)) + throw new ServiceException((String)hint, "Non-existing component for this hint"); + String stringHint = (String)hint; + return components.get(stringHint); + } + + public boolean isSelectable(Object hint) { + String stringHint = (String)hint; + return components.containsKey(stringHint); + } + + public void release(Object o) { + } + + public void dispose() { + Iterator serviceIt = components.values().iterator(); + while (serviceIt.hasNext()) { + Object service = serviceIt.next(); + if (service instanceof Disposable) { + try { + ((Disposable)service).dispose(); + } catch (Exception e) { + getLogger().error("Error disposing service " + service, e); + } + } + } + } +} Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/SimpleServiceSelector.java ------------------------------------------------------------------------------ svn:eol-style = native Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/StringMessage.java URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/StringMessage.java?rev=330548&view=auto ============================================================================== --- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/StringMessage.java (added) +++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/StringMessage.java Thu Nov 3 05:41:06 2005 @@ -0,0 +1,50 @@ +/* + * 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.util; + +import java.util.Arrays; + +import org.apache.excalibur.xml.sax.XMLizable; +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; + +/** + * A string in an XMLizable form. + * + * Will produce exactly one characters call, no start/endDocument calls. + * + * @version $Id: StringMessage.java 177978 2005-05-23 13:27:51Z sylvain $ + */ +public class StringMessage implements XMLizable { + private char[] ch; + + public StringMessage(String message) { + this.ch = message.toCharArray(); + } + + public void toSAX(ContentHandler contentHandler) throws SAXException { + contentHandler.characters(ch, 0, ch.length); + } + + public boolean equals(Object obj) { + if (obj instanceof StringMessage) { + // Compare char arrays + return Arrays.equals(this.ch, ((StringMessage)obj).ch); + } else { + return false; + } + } +} Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/util/StringMessage.java ------------------------------------------------------------------------------ svn:eol-style = native