001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.forms.binding;
018:
019: import java.lang.reflect.Method;
020:
021: import org.apache.cocoon.components.LifecycleHelper;
022: import org.apache.cocoon.forms.binding.JXPathBindingManager.Assistant;
023: import org.apache.cocoon.forms.util.DomHelper;
024:
025: import org.w3c.dom.Element;
026:
027: /**
028: * CustomJXPathBindingBuilder provides a helper class for the Factory
029: * implemented in {@link JXPathBindingManager} that helps construct the
030: * actual {@link CustomJXPathBinding} out of the configuration in the
031: * provided configElement which looks like one of the following:
032: *
033: * <p> 1. No additional configuration requirements:
034: * <pre><code>
035: * <fb:custom id="<i>widget-id</i>" path="<i>xpath expression</i>"
036: * class="your.package.CustomBindingX" />
037: * </code></pre>
038: *
039: * <p> 2. With custom configuration requirements:
040: * <pre><code>
041: * <fb:custom id="<i>widget-id</i>" path="<i>xpath expression</i>"
042: * builderclass="your.package.CustomBindingXBuilder"
043: * factorymethod="makeBinding"
044: * >
045: * <fb:config custom-atts="someValue">
046: * <!-- in here come the nested custom elements (recommended in own namespace)
047: * that make up the custom config -->
048: * </fb:config>
049: * </fb:context>
050: * </code></pre>
051: *
052: * @version $Id: CustomJXPathBindingBuilder.java 517733 2007-03-13 15:37:22Z vgritsenko $
053: */
054: public class CustomJXPathBindingBuilder extends
055: JXPathBindingBuilderBase {
056:
057: private static final Class[] DOMELEMENT_METHODARGS;
058: private static final Class[] EMPTY_METHODARGS;
059:
060: static {
061: DOMELEMENT_METHODARGS = new Class[1];
062: DOMELEMENT_METHODARGS[0] = Element.class;
063: EMPTY_METHODARGS = null;
064: }
065:
066: /**
067: * Builds the custom Binding class and wraps it into a CustomJXPathBinding
068: *
069: * @param bindingElm configuration element describing the binding to build
070: * @param assistant helper-class for building possible nested bindings
071: * @return the freshly built binding based on the configuration element
072: * @throws BindingException
073: */
074: public JXPathBindingBase buildBinding(Element bindingElm,
075: Assistant assistant) throws BindingException {
076:
077: try {
078: CommonAttributes commonAtts = JXPathBindingBuilderBase
079: .getCommonAttributes(bindingElm);
080: String xpath = DomHelper.getAttribute(bindingElm, "path",
081: ".");
082: String widgetId = DomHelper.getAttribute(bindingElm, "id",
083: null);
084:
085: Object bindingInstance = null;
086:
087: String className = DomHelper.getAttribute(bindingElm,
088: "class", null);
089: if (className != null) {
090: Class clazz = Class.forName(className);
091: bindingInstance = clazz.newInstance();
092:
093: } else {
094: String builderClassName = DomHelper.getAttribute(
095: bindingElm, "builderclass", null);
096: String factoryMethodName = DomHelper.getAttribute(
097: bindingElm, "factorymethod", null);
098: Element configNode = DomHelper.getChildElement(
099: bindingElm, BindingManager.NAMESPACE, "config");
100:
101: // only do it if attributes exist
102: if (builderClassName != null
103: && factoryMethodName != null) {
104: Class builderClass = Class
105: .forName(builderClassName);
106: Method factoryMethod;
107: Object[] args = null;
108: try {
109: factoryMethod = builderClass.getMethod(
110: factoryMethodName,
111: DOMELEMENT_METHODARGS);
112: args = new Object[1];
113: args[0] = configNode;
114: } catch (NoSuchMethodException e) {
115: factoryMethod = null;
116: }
117:
118: if (factoryMethod == null) {
119: factoryMethod = builderClass.getMethod(
120: factoryMethodName, EMPTY_METHODARGS);
121: args = null;
122: }
123:
124: // we pass null to indicate that the method should be static
125: bindingInstance = factoryMethod.invoke(null, args);
126: }
127: }
128:
129: // do inheritance
130: CustomJXPathBinding otherBinding = (CustomJXPathBinding) assistant
131: .getContext().getSuperBinding();
132: if (otherBinding != null) {
133: commonAtts = JXPathBindingBuilderBase
134: .mergeCommonAttributes(otherBinding
135: .getCommonAtts(), commonAtts);
136:
137: if (xpath == null) {
138: xpath = otherBinding.getXPath();
139: }
140: if (widgetId == null) {
141: widgetId = otherBinding.getId();
142: }
143: if (bindingInstance == null) {
144: bindingInstance = otherBinding.getWrappedBinding();
145: }
146: }
147:
148: CustomJXPathBinding customBinding = new CustomJXPathBinding(
149: commonAtts, widgetId, xpath,
150: (AbstractCustomBinding) bindingInstance);
151:
152: // Fire Avalon-setup for the custom binding
153: LifecycleHelper.setupComponent(customBinding, getLogger(),
154: null, assistant.getServiceManager(), null);
155:
156: return customBinding;
157: } catch (BindingException e) {
158: throw e;
159: } catch (Exception e) {
160: throw new BindingException("Error building custom binding",
161: e, DomHelper.getLocationObject(bindingElm));
162: }
163: }
164: }
|