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 org.apache.cocoon.forms.util.DomHelper;
020:
021: import org.w3c.dom.Element;
022:
023: /**
024: * RepeaterJXPathBindingBuilder provides a helper class for the Factory
025: * implemented in {@link JXPathBindingManager} that helps construct the
026: * actual {@link RepeaterJXPathBinding} out of the configuration in the
027: * provided configElement which looks like:
028: * <pre><code>
029: * <fb:repeater
030: * id="contacts"
031: * parent-path="contacts"
032: * row-path="contact"
033: * row-path-insert="new-contact" >
034: *
035: * <fb:identity>
036: * <!-- nested bindings that map the 'identity' of the items -->
037: * </fb:identity>
038: *
039: * <fb:on-bind>
040: * <!-- nested bindings executed on updates AND right after the insert -->
041: * </fb:on-bind>
042: *
043: * <fb:on-delete-row>
044: * <!-- nested bindings executed on deletion of row -->
045: * </fb:on-delete-row>
046: *
047: * <fb:on-insert-row>
048: * <!-- nested bindings executed to prepare the insertion of a row -->
049: * </fb:on-insert-row>
050: *
051: * </fb:repeater>
052: * </code></pre>
053: *
054: * @version $Id: RepeaterJXPathBindingBuilder.java 517733 2007-03-13 15:37:22Z vgritsenko $
055: */
056: public class RepeaterJXPathBindingBuilder extends
057: JXPathBindingBuilderBase {
058:
059: /**
060: * Creates an instance of {@link RepeaterJXPathBinding} according to the
061: * attributes and nested comfiguration elements of the bindingElm.
062: *
063: * @param bindingElm
064: * @param assistant
065: * @return JXPathBindingBase
066: */
067: public JXPathBindingBase buildBinding(Element bindingElm,
068: JXPathBindingManager.Assistant assistant)
069: throws BindingException {
070: if (bindingElm.hasAttribute("unique-row-id")) {
071: throw new BindingException(
072: "Attribute 'unique-row-id' is no more supported, use <fb:identity> instead",
073: DomHelper.getLocationObject(bindingElm));
074: }
075:
076: if (bindingElm.hasAttribute("unique-path")) {
077: throw new BindingException(
078: "Attribute 'unique-path' is no more supported, use <fb:identity> instead",
079: DomHelper.getLocationObject(bindingElm));
080: }
081:
082: try {
083: CommonAttributes commonAtts = JXPathBindingBuilderBase
084: .getCommonAttributes(bindingElm);
085:
086: String repeaterId = DomHelper.getAttribute(bindingElm,
087: "id", null);
088: String parentPath = DomHelper.getAttribute(bindingElm,
089: "parent-path", null);
090: String rowPath = DomHelper.getAttribute(bindingElm,
091: "row-path", null);
092: String rowPathForInsert = DomHelper.getAttribute(
093: bindingElm, "row-path-insert", rowPath);
094: String adapterClass = DomHelper.getAttribute(bindingElm,
095: "adapter-class", null);
096:
097: // do inheritance
098: RepeaterJXPathBinding otherBinding = (RepeaterJXPathBinding) assistant
099: .getContext().getSuperBinding();
100: JXPathBindingBase[] existingOnBind = null;
101: JXPathBindingBase[] existingOnDelete = null;
102: JXPathBindingBase[] existingOnInsert = null;
103: JXPathBindingBase[] existingIdentity = null;
104:
105: if (otherBinding != null) {
106: commonAtts = JXPathBindingBuilderBase
107: .mergeCommonAttributes(otherBinding
108: .getCommonAtts(), commonAtts);
109:
110: if (repeaterId == null)
111: repeaterId = otherBinding.getId();
112: if (parentPath == null)
113: parentPath = otherBinding.getRepeaterPath();
114: if (rowPath == null)
115: rowPath = otherBinding.getRowPath();
116: if (rowPathForInsert == null)
117: rowPathForInsert = otherBinding.getInsertRowPath();
118:
119: if (otherBinding.getRowBinding() != null)
120: existingOnBind = otherBinding.getRowBinding()
121: .getChildBindings();
122: if (otherBinding.getDeleteRowBinding() != null)
123: existingOnDelete = otherBinding
124: .getDeleteRowBinding().getChildBindings();
125: if (otherBinding.getIdentityBinding() != null)
126: existingIdentity = otherBinding
127: .getIdentityBinding().getChildBindings();
128: if (otherBinding.getInsertRowBinding() != null)
129: existingOnInsert = new JXPathBindingBase[] { otherBinding
130: .getInsertRowBinding() };
131: }
132:
133: // Simple mode will be used if no fb:identity, fb:on-bind, fb:on-delete-row,
134: // or fb:on-insert-row exists in this or inherited binding definitions.
135: // In that case, the children of fb:repeater will be used as child bindings.
136: boolean simpleMode = true;
137:
138: Element childWrapElement = DomHelper.getChildElement(
139: bindingElm, BindingManager.NAMESPACE, "on-bind");
140: JXPathBindingBase[] childBindings = assistant
141: .makeChildBindings(childWrapElement, existingOnBind);
142: if (childWrapElement != null) {
143: // Not checking existingOnBind!=null here because parent can be in 'simpleMode'.
144: simpleMode = false;
145: }
146:
147: JXPathBindingBase[] deleteBindings = null;
148: Element deleteWrapElement = DomHelper.getChildElement(
149: bindingElm, BindingManager.NAMESPACE,
150: "on-delete-row");
151: if (deleteWrapElement != null || existingOnDelete != null) {
152: deleteBindings = assistant.makeChildBindings(
153: deleteWrapElement, existingOnDelete);
154: simpleMode = false;
155: }
156:
157: JXPathBindingBase insertBinding = null;
158: Element insertWrapElement = DomHelper.getChildElement(
159: bindingElm, BindingManager.NAMESPACE,
160: "on-insert-row");
161: if (insertWrapElement != null || existingOnInsert != null) {
162: insertBinding = assistant.makeChildBindings(
163: insertWrapElement, existingOnInsert)[0];
164: simpleMode = false;
165: }
166:
167: JXPathBindingBase[] identityBinding = null;
168: Element identityWrapElement = DomHelper.getChildElement(
169: bindingElm, BindingManager.NAMESPACE, "identity");
170: if (identityWrapElement != null || existingIdentity != null) {
171: // TODO: we can only handle ValueJXPathBinding at the moment:
172: // http://marc.theaimsgroup.com/?l=xml-cocoon-dev&m=107906438632484&w=4
173: identityBinding = assistant.makeChildBindings(
174: identityWrapElement, existingIdentity);
175: for (int i = 0; i < identityBinding.length; i++) {
176: if (!(identityBinding[i] instanceof ValueJXPathBinding)) {
177: throw new BindingException(
178: "Error building repeater binding defined at "
179: + DomHelper
180: .getLocation(bindingElm)
181: + ": Only value binding (i.e. fb:value) "
182: + "can be used inside fb:identity at the moment. You can read "
183: + "http://marc.theaimsgroup.com/?l=xml-cocoon-dev&m=107906438632484&w=4"
184: + " if you want to know more on this.");
185: }
186: }
187: simpleMode = false;
188: }
189:
190: if (simpleMode) {
191: // Use the children of the current element
192: childBindings = assistant.makeChildBindings(bindingElm,
193: existingOnBind);
194: }
195:
196: return new EnhancedRepeaterJXPathBinding(commonAtts,
197: repeaterId, parentPath, rowPath, rowPathForInsert,
198: childBindings, insertBinding, deleteBindings,
199: identityBinding, adapterClass);
200: } catch (BindingException e) {
201: throw e;
202: } catch (Exception e) {
203: throw new BindingException(
204: "Error building repeater binding", e, DomHelper
205: .getLocationObject(bindingElm));
206: }
207: }
208: }
|