KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > woody > 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.woody.binding;
17
18 import java.util.Iterator JavaDoc;
19
20 import org.apache.avalon.framework.logger.Logger;
21 import org.apache.cocoon.woody.formmodel.Repeater;
22 import org.apache.cocoon.woody.formmodel.Widget;
23 import org.apache.commons.jxpath.JXPathContext;
24 import org.apache.commons.jxpath.Pointer;
25 import org.w3c.dom.Node JavaDoc;
26 import org.w3c.dom.NodeList JavaDoc;
27
28 /**
29  * Experimental simple binding for repeaters:
30  * on save, first deletes the target data before recreating it from scratch.
31  * Based on code from SimpleRepeater.
32  * <p>
33  * For a smarter binding that avoids deletion and recreation, consider
34  * {@link org.apache.cocoon.woody.binding.RepeaterJXPathBinding}
35  *
36  * @author Timothy Larson
37  * @version CVS $Id: TempRepeaterJXPathBinding.java 30932 2004-07-29 17:35:38Z vgritsenko $
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 void doLoad(Widget frmModel, JXPathContext jctx) throws BindingException {
72         // (There should be a general widget type checker for all the bindings to use,
73
// coupled with a general informative exception class to throw if the widget is
74
// of the wrong type or null.)
75
Repeater repeater = (Repeater) frmModel.getWidget(this.repeaterId);
76         if (repeater == null) {
77             String JavaDoc fullId = frmModel.getFullyQualifiedId();
78             if (fullId == null || fullId.length() == 0) {
79                 fullId = "";
80             } else {
81                 fullId = fullId + ".";
82             }
83             throw new RuntimeException JavaDoc(
84                 "TempRepeaterJXPathBinding: Repeater \"" + fullId + this.repeaterId +
85                 "\" does not exist (" + frmModel.getLocation() + ")");
86         }
87  
88         // Start by clearing the repeater, if necessary.
89
if (this.clearOnLoad) {
90             repeater.removeRows();
91         }
92
93         // Find the location of the repeater data.
94
Pointer repeaterPointer = jctx.getPointer(this.repeaterPath);
95
96         // Check if there is data present.
97
//
98
// (Otherwise, should we check the leniency config option
99
// to decide whether to be silent or throw an exception?)
100
if (repeaterPointer != null) {
101
102             // Narrow to repeater context.
103
JXPathContext repeaterContext = jctx.getRelativeContext(repeaterPointer);
104
105             // Build a jxpath iterator for the repeater row pointers.
106
Iterator JavaDoc rowPointers = repeaterContext.iteratePointers(this.rowPath);
107
108             // Iterate through the rows of data.
109
int rowNum = 0;
110             while (rowPointers.hasNext()) {
111
112                 // Get or create a row widget.
113
Repeater.RepeaterRow thisRow;
114                 if (repeater.getSize() > rowNum) {
115                     thisRow = repeater.getRow(rowNum);
116                 } else {
117                     thisRow = repeater.addRow();
118                 }
119                 rowNum++;
120
121                 // Narrow to the row context.
122
Pointer rowPointer = (Pointer) rowPointers.next();
123                 JXPathContext rowContext = repeaterContext.getRelativeContext(rowPointer);
124
125                 // If virtual rows are requested, place a deep clone of the row data
126
// into a temporary node, and narrow the context to this virtual row.
127
//
128
// (A clone of the data is used to prevent modifying the source document.
129
// Otherwise, the appendChild method would remove the data from the source
130
// document. Is this protection worth the penalty of a deep clone?)
131
//
132
// (This implementation of virtual rows currently only supports DOM
133
// bindings, but could easily be extended to support other bindings.)
134

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

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