KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > forms > binding > TempRepeaterJXPathBinding


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.cocoon.forms.binding;
17
18 import java.util.Iterator JavaDoc;
19
20 import org.apache.avalon.framework.logger.Logger;
21 import org.apache.cocoon.forms.formmodel.Repeater;
22 import org.apache.cocoon.forms.formmodel.Widget;
23 import org.apache.commons.jxpath.JXPathContext;
24 import org.apache.commons.jxpath.Pointer;
25 import org.w3c.dom.Document JavaDoc;
26 import org.w3c.dom.Node JavaDoc;
27 import org.w3c.dom.NodeList JavaDoc;
28
29 /**
30  * Experimental simple binding for repeaters:
31  * on save, first deletes the target data before recreating it from scratch.
32  * Based on code from SimpleRepeater.
33  * <p>
34  * For a smarter binding that avoids deletion and recreation, consider
35  * {@link org.apache.cocoon.forms.binding.RepeaterJXPathBinding}
36  *
37  * @version $Id: TempRepeaterJXPathBinding.java 289538 2005-09-16 13:46:22Z sylvain $
38  */

39 public class TempRepeaterJXPathBinding extends JXPathBindingBase {
40
41     private final String JavaDoc repeaterId;
42     private final String JavaDoc repeaterPath;
43     private final String JavaDoc rowPath;
44     private final String JavaDoc rowPathInsert;
45     private final boolean clearOnLoad;
46     private final JXPathBindingBase rowBinding;
47     private final JXPathBindingBase insertRowBinding;
48     private final boolean deleteIfEmpty;
49     private final boolean virtualRows;
50
51     public TempRepeaterJXPathBinding(
52             JXPathBindingBuilderBase.CommonAttributes commonAtts,
53             String JavaDoc repeaterId, String JavaDoc repeaterPath,
54             String JavaDoc rowPath, String JavaDoc rowPathInsert,
55             boolean virtualRows, boolean clearOnLoad, boolean deleteIfEmpty,
56             JXPathBindingBase rowBinding, JXPathBindingBase insertBinding) {
57         super(commonAtts);
58         this.repeaterId = repeaterId;
59         this.repeaterPath = repeaterPath;
60         this.rowPath = rowPath;
61         this.rowPathInsert = rowPathInsert;
62         this.rowBinding = rowBinding;
63         this.rowBinding.setParent(this);
64         this.insertRowBinding = insertBinding;
65         this.insertRowBinding.setParent(this);
66         this.virtualRows = virtualRows;
67         this.clearOnLoad = clearOnLoad;
68         this.deleteIfEmpty = deleteIfEmpty;
69     }
70     
71     public String JavaDoc getId() { return repeaterId; }
72     public String JavaDoc getRepeaterPath() { return repeaterPath; }
73     public String JavaDoc getRowPath() { return rowPath; }
74     public String JavaDoc getRowPathInsert() { return rowPathInsert; }
75     public boolean getVirtualRows() { return virtualRows; }
76     public boolean getClearOnLoad() { return clearOnLoad; }
77     public boolean getDeleteIfEmpty() { return deleteIfEmpty; }
78     public JXPathBindingBase[] getChildBindings() { return ((ComposedJXPathBindingBase)rowBinding).getChildBindings(); }
79     public JXPathBindingBase[] getInsertChildBindings() { return ((ComposedJXPathBindingBase)insertRowBinding).getChildBindings(); }
80
81     public void doLoad(Widget frmModel, JXPathContext jctx) throws BindingException {
82         // (There should be a general widget type checker for all the bindings to use,
83
// coupled with a general informative exception class to throw if the widget is
84
// of the wrong type or null.)
85
Repeater repeater = (Repeater) selectWidget(frmModel, this.repeaterId);
86         if (repeater == null) {
87             String JavaDoc fullId = frmModel.getRequestParameterName();
88             if (fullId == null || fullId.length() == 0) {
89                 fullId = "";
90             } else {
91                 fullId = fullId + ".";
92             }
93             throw new RuntimeException JavaDoc(
94                 "TempRepeaterJXPathBinding: Repeater \"" + fullId + this.repeaterId +
95                 "\" does not exist (" + frmModel.getLocation() + ")");
96         }
97  
98         // Start by clearing the repeater, if necessary.
99
if (this.clearOnLoad) {
100             repeater.clear();
101         }
102
103         // Find the location of the repeater data.
104
Pointer repeaterPointer = jctx.getPointer(this.repeaterPath);
105
106         // Check if there is data present.
107
//
108
// (Otherwise, should we check the leniency config option
109
// to decide whether to be silent or throw an exception?)
110
if (repeaterPointer != null) {
111
112             // Narrow to repeater context.
113
JXPathContext repeaterContext = jctx.getRelativeContext(repeaterPointer);
114
115             // Build a jxpath iterator for the repeater row pointers.
116
Iterator JavaDoc rowPointers = repeaterContext.iteratePointers(this.rowPath);
117
118             // Iterate through the rows of data.
119
int rowNum = 0;
120             while (rowPointers.hasNext()) {
121
122                 // Get or create a row widget.
123
Repeater.RepeaterRow thisRow;
124                 if (repeater.getSize() > rowNum) {
125                     thisRow = repeater.getRow(rowNum);
126                 } else {
127                     thisRow = repeater.addRow();
128                 }
129                 rowNum++;
130
131                 // Narrow to the row context.
132
Pointer rowPointer = (Pointer) rowPointers.next();
133                 JXPathContext rowContext = repeaterContext.getRelativeContext(rowPointer);
134
135                 // If virtual rows are requested, place a deep clone of the row data
136
// into a temporary node, and narrow the context to this virtual row.
137
//
138
// (A clone of the data is used to prevent modifying the source document.
139
// Otherwise, the appendChild method would remove the data from the source
140
// document. Is this protection worth the penalty of a deep clone?)
141
//
142
// (This implementation of virtual rows currently only supports DOM
143
// bindings, but could easily be extended to support other bindings.)
144

145                 if (virtualRows == true) {
146                     Node JavaDoc repeaterNode = (Node JavaDoc)repeaterPointer.getNode();
147                     Node JavaDoc virtualNode = repeaterNode.getOwnerDocument().createElementNS(null, "virtual");
148                     Node JavaDoc node = (Node JavaDoc)rowPointer.getNode();
149                     Node JavaDoc clone = node.cloneNode(true);
150                     Node JavaDoc fakeDocElement = node.getOwnerDocument().getDocumentElement().cloneNode(false);
151                     virtualNode.appendChild(clone);
152                     fakeDocElement.appendChild(virtualNode);
153                     rowContext = JXPathContext.newContext(repeaterContext, fakeDocElement);
154                     rowContext = rowContext.getRelativeContext(rowContext.getPointer("virtual"));
155                 }
156
157                 // Finally, perform the load row binding.
158
this.rowBinding.loadFormFromModel(thisRow, rowContext);
159             }
160         }
161
162         if (getLogger().isDebugEnabled())
163             getLogger().debug("done loading rows " + toString());
164     }
165
166     public void doSave(Widget frmModel, JXPathContext jctx) throws BindingException {
167         // (See comment in doLoad about type checking and throwing a meaningful exception.)
168
Repeater repeater = (Repeater) selectWidget(frmModel, this.repeaterId);
169
170         // Perform shortcut binding if the repeater is empty
171
// and the deleteIfEmpty config option is selected.
172
if (repeater.getSize() == 0 && this.deleteIfEmpty) {
173             // Delete all of the old data for this repeater.
174
jctx.removeAll(this.repeaterPath);
175
176         // Otherwise perform the normal save binding.
177
} else {
178
179             // Narrow to the repeater context, creating the path if it did not exist.
180
JXPathContext repeaterContext = jctx.getRelativeContext(jctx.createPath(this.repeaterPath));
181
182             // Start by deleting all of the old row data.
183
repeaterContext.removeAll(this.rowPath);
184
185             // Verify that repeater is not empty and has an insert row binding.
186
if(repeater.getSize() > 0) {
187                 if (this.insertRowBinding != null) {
188
189                     //register the factory!
190
//this.insertRowBinding.saveFormToModel(repeater, repeaterContext);
191

192                     // Iterate through the repeater rows.
193
for (int i = 0; i < repeater.getSize(); i++) {
194
195                         // Narrow to the repeater row context.
196
Pointer rowPointer = repeaterContext.getPointer(this.rowPathInsert);
197                         JXPathContext rowContext = repeaterContext.getRelativeContext(rowPointer);
198
199                         // Variables used for virtual rows.
200
// They are initialized here just to keep the compiler happy.
201
Node JavaDoc rowNode = null;
202                         Node JavaDoc virtualNode = null;
203
204                         // If virtual rows are requested, create a temporary node and
205
// narrow the context to this initially empty new virtual row.
206
if (virtualRows == true) {
207                             rowNode = (Node JavaDoc)rowContext.getContextBean();
208                             Document JavaDoc document = rowNode.getOwnerDocument();
209                             virtualNode = document.createElementNS(null, "virtual");
210                             Node JavaDoc fakeDocElement = document.getDocumentElement().cloneNode(false);
211                             fakeDocElement.appendChild(virtualNode);
212                             rowContext = JXPathContext.newContext(repeaterContext, fakeDocElement);
213                             rowContext = rowContext.getRelativeContext(rowContext.getPointer("virtual"));
214                         }
215
216                         // Perform the insert row binding
217
this.insertRowBinding.saveFormToModel(repeater, rowContext);
218
219                         // Perform the save row binding.
220
this.rowBinding.saveFormToModel(repeater.getRow(i), rowContext);
221
222                         // If virtual rows are requested, finish by appending the
223
// children of the virtual row to the real context node.
224
if (virtualRows == true) {
225                             NodeList JavaDoc list = virtualNode.getChildNodes();
226                             int count = list.getLength();
227                             for (int j = 0; j < count; j++) {
228                                 // The list shrinks when a child is appended to the context
229
// node, so we always reference the first child in the list.
230
rowNode.appendChild(list.item(0));
231                             }
232                         }
233                         getLogger().debug("bound new row");
234                     }
235                 } else {
236                     getLogger().warn("TempRepeaterBinding has detected rows to insert, " +
237                         "but misses the <on-insert-row> binding to do it.");
238                 }
239             }
240         }
241     }
242
243     public String JavaDoc toString() {
244         return "TempRepeaterJXPathBinding [widget=" + this.repeaterId + ", xpath=" + this.repeaterPath + "]";
245     }
246
247     public void enableLogging(Logger logger) {
248         super.enableLogging(logger);
249         if (this.insertRowBinding != null) {
250             this.insertRowBinding.enableLogging(logger);
251         }
252         this.rowBinding.enableLogging(logger);
253     }
254 }
255
Popular Tags