| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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.binding; |
| |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| |
| import org.apache.avalon.framework.context.Context; |
| import org.apache.avalon.framework.context.ContextException; |
| import org.apache.avalon.framework.context.Contextualizable; |
| |
| import org.apache.cocoon.forms.binding.JXPathBindingManager.Assistant; |
| import org.apache.cocoon.forms.util.DomHelper; |
| import org.apache.cocoon.forms.util.JavaScriptHelper; |
| |
| import org.mozilla.javascript.Function; |
| import org.w3c.dom.Element; |
| |
| /** |
| * Builds a {@link Binding} based on two JavaScript snippets, respectively for loading and saving the form. |
| * This binding also optionally accepts named child bindings, which are useful when the bound widget is a container. |
| * <p> |
| * The syntax for this binding is as follows: |
| * <pre> |
| * <fb:javascript id="foo" path="@foo"> |
| * <fb:load-form> |
| * var appValue = jxpathPointer.getValue(); |
| * var formValue = doLoadConversion(appValue); |
| * widget.setValue(formValue); |
| * childBindings["foo"].loadFormFromModel(widget, jxpathContext); |
| * </fb:load-form> |
| * <fb:save-form> |
| * var formValue = widget.getValue(); |
| * var appValue = doSaveConversion(formValue); |
| * jxpathPointer.setValue(appValue); |
| * childBindings["foo"].saveFormToModel(widget, jxpathContext); |
| * </fb:save-form> |
| * <fb:child-binding name="foo"> |
| * <fb:value id="bar" path="baz"/> |
| * </fb:child-binding> |
| * </fb:javascript> |
| * </pre> |
| * This example is rather trivial and could be replaced by a simple <fb:value>, but |
| * it shows the available variables in the script: |
| * <ul> |
| * <li><code>widget</code>: the widget identified by the "id" attribute, |
| * <li><code>jxpathPointer</code>: the JXPath pointer corresponding to the "path" attribute, |
| * <li><code>jxpathContext</code> (not shown): the JXPath context corresponding to the "path" attribute |
| * </ul> |
| * <b>Notes:</b><ul> |
| * <li>The <fb:save-form> snippet should be ommitted if the "direction" attribute is set to "load".</li> |
| * <li>The <fb:load-form> snippet should be ommitted if the "direction" attribute is set to "save".</li> |
| * </ul> |
| * |
| * @version $Id$ |
| */ |
| public class JavaScriptJXPathBindingBuilder extends JXPathBindingBuilderBase |
| implements Contextualizable { |
| |
| private Context avalonContext; |
| |
| |
| public void contextualize(Context context) throws ContextException { |
| this.avalonContext = context; |
| } |
| |
| public JXPathBindingBase buildBinding(Element element, Assistant assistant) |
| throws BindingException { |
| try { |
| CommonAttributes commonAtts = JXPathBindingBuilderBase.getCommonAttributes(element); |
| |
| String id = DomHelper.getAttribute(element, "id", null); |
| String path = DomHelper.getAttribute(element, "path", null); |
| |
| JavaScriptJXPathBinding otherBinding = (JavaScriptJXPathBinding)assistant.getContext().getSuperBinding(); |
| |
| if (otherBinding != null) { |
| commonAtts = JXPathBindingBuilderBase.mergeCommonAttributes(otherBinding.getCommonAtts(), commonAtts); |
| |
| if (id == null) { |
| id = otherBinding.getId(); |
| } |
| if (path == null) { |
| path = otherBinding.getPath(); |
| } |
| } |
| |
| // Build load script |
| Function loadScript = null; |
| if (commonAtts.loadEnabled) { |
| if (otherBinding != null) { |
| loadScript = otherBinding.getLoadScript(); |
| } |
| |
| Element loadElem = DomHelper.getChildElement(element, BindingManager.NAMESPACE, "load-form"); |
| if (loadElem != null) { |
| loadScript = JavaScriptHelper.buildFunction(loadElem, "loadForm", JavaScriptJXPathBinding.LOAD_PARAMS); |
| } |
| } |
| |
| // Build save script |
| Function saveScript = null; |
| if (commonAtts.saveEnabled) { |
| if (otherBinding != null) { |
| saveScript = otherBinding.getSaveScript(); |
| } |
| |
| Element saveElem = DomHelper.getChildElement(element, BindingManager.NAMESPACE, "save-form"); |
| if (saveElem != null) { |
| saveScript = JavaScriptHelper.buildFunction(saveElem, "saveForm", JavaScriptJXPathBinding.SAVE_PARAMS); |
| } |
| } |
| |
| // Build child bindings |
| Map childBindings = new HashMap(); |
| |
| if (otherBinding != null) { |
| Map otherChildren = otherBinding.getChildBindingsMap(); |
| Iterator it = otherChildren.entrySet().iterator(); |
| while (it.hasNext()) { |
| Map.Entry entry = (Map.Entry) it.next(); |
| childBindings.put(entry.getKey(), entry.getValue()); |
| } |
| } |
| |
| Element[] children = DomHelper.getChildElements(element, BindingManager.NAMESPACE, "child-binding"); |
| if (children.length != 0) { |
| for (int i = 0; i < children.length; i++) { |
| Element child = children[i]; |
| |
| // Get the binding name and check its uniqueness |
| String name = DomHelper.getAttribute(child, "name"); |
| |
| JXPathBindingBase[] otherBindings = null; |
| if (childBindings.containsKey(name)) { |
| //throw new BindingException("Duplicate name '" + name + "' at " + DomHelper.getLocation(child)); |
| otherBindings = ((ComposedJXPathBindingBase)childBindings.get(name)).getChildBindings(); |
| } |
| |
| // Build the child binding |
| JXPathBindingBase[] bindings = assistant.makeChildBindings(child,otherBindings); |
| if (bindings == null) { |
| bindings = new JXPathBindingBase[0]; |
| } |
| |
| ComposedJXPathBindingBase composedBinding = new ComposedJXPathBindingBase(commonAtts, bindings); |
| composedBinding.enableLogging(getLogger()); |
| childBindings.put(name, composedBinding); |
| } |
| } |
| |
| JXPathBindingBase result = new JavaScriptJXPathBinding(this.avalonContext, commonAtts, id, path, loadScript, saveScript, |
| Collections.unmodifiableMap(childBindings)); |
| result.enableLogging(getLogger()); |
| return result; |
| |
| } catch (BindingException e) { |
| throw e; |
| } catch (Exception e) { |
| throw new BindingException("Cannot build binding", e, |
| DomHelper.getLocationObject(element)); |
| } |
| } |
| } |