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.util.Iterator;
020:
021: import org.apache.avalon.framework.logger.Logger;
022:
023: import org.apache.cocoon.forms.formmodel.Repeater;
024: import org.apache.cocoon.forms.formmodel.Widget;
025:
026: import org.apache.commons.jxpath.JXPathContext;
027: import org.apache.commons.jxpath.Pointer;
028:
029: /**
030: * Simple binding for repeaters: on save, first deletes the target data
031: * before recreating it from scratch.
032: * <p>
033: * For a smarter binding that avoids deletion and recreation, consider
034: * {@link org.apache.cocoon.forms.binding.RepeaterJXPathBinding}
035: *
036: * @version $Id: SimpleRepeaterJXPathBinding.java 517733 2007-03-13 15:37:22Z vgritsenko $
037: */
038: public class SimpleRepeaterJXPathBinding extends JXPathBindingBase {
039:
040: private final String repeaterId;
041: private final String repeaterPath;
042: private final String rowPath;
043: private final boolean clearOnLoad;
044: private final JXPathBindingBase rowBinding;
045: private final boolean deleteIfEmpty;
046:
047: public SimpleRepeaterJXPathBinding(
048: JXPathBindingBuilderBase.CommonAttributes commonAtts,
049: String repeaterId, String repeaterPath, String rowPath,
050: boolean clearOnLoad, boolean deleteIfEmpty,
051: JXPathBindingBase rowBinding) {
052: super (commonAtts);
053: this .repeaterId = repeaterId;
054: this .repeaterPath = repeaterPath;
055: this .rowPath = rowPath;
056: this .rowBinding = rowBinding;
057: this .rowBinding.setParent(this );
058: this .clearOnLoad = clearOnLoad;
059: this .deleteIfEmpty = deleteIfEmpty;
060: }
061:
062: public void enableLogging(Logger logger) {
063: super .enableLogging(logger);
064: this .rowBinding.enableLogging(logger);
065: }
066:
067: public String getId() {
068: return repeaterId;
069: }
070:
071: public String getRepeaterPath() {
072: return repeaterPath;
073: }
074:
075: public String getRowPath() {
076: return rowPath;
077: }
078:
079: public boolean getClearOnLoad() {
080: return clearOnLoad;
081: }
082:
083: public boolean getDeleteIfEmpty() {
084: return deleteIfEmpty;
085: }
086:
087: public JXPathBindingBase[] getChildBindings() {
088: return ((ComposedJXPathBindingBase) rowBinding)
089: .getChildBindings();
090: }
091:
092: public void doLoad(Widget frmModel, JXPathContext jctx)
093: throws BindingException {
094: // Find the repeater and clear it
095: Repeater repeater = (Repeater) selectWidget(frmModel,
096: this .repeaterId);
097:
098: if (this .clearOnLoad) {
099: repeater.clear();
100: }
101:
102: // Move to repeater context
103: Pointer ptr = jctx.getPointer(this .repeaterPath);
104: if (ptr.getNode() != null) {
105: // There are some nodes to load from
106:
107: JXPathContext repeaterContext = jctx
108: .getRelativeContext(ptr);
109: // build a jxpath iterator for pointers
110: Iterator rowPointers = repeaterContext
111: .iteratePointers(this .rowPath);
112:
113: //iterate through it
114: int rowNum = 0;
115: while (rowPointers.hasNext()) {
116: // Get a row. It is created if needed (depends on clearOnLoad)
117: Repeater.RepeaterRow this Row;
118: if (repeater.getSize() > rowNum) {
119: this Row = repeater.getRow(rowNum);
120: } else {
121: this Row = repeater.addRow();
122: }
123: rowNum++;
124:
125: // make a jxpath sub context on the iterated element
126: Pointer jxp = (Pointer) rowPointers.next();
127: JXPathContext rowContext = repeaterContext
128: .getRelativeContext(jxp);
129:
130: this .rowBinding.loadFormFromModel(this Row, rowContext);
131: }
132: }
133: if (getLogger().isDebugEnabled()) {
134: getLogger().debug("done loading rows " + this );
135: }
136: }
137:
138: public void doSave(Widget frmModel, JXPathContext jctx)
139: throws BindingException {
140: // Find the repeater
141: Repeater repeater = (Repeater) selectWidget(frmModel,
142: this .repeaterId);
143:
144: if (repeater.getSize() == 0 && this .deleteIfEmpty) {
145: // Repeater is empty : erase all
146: jctx.removeAll(this .repeaterPath);
147: } else {
148: // Repeater is not empty
149: // Move to repeater context and create the path if needed
150: JXPathContext repeaterContext = jctx
151: .getRelativeContext(jctx
152: .createPath(this .repeaterPath));
153:
154: // Delete all that is already present
155: repeaterContext.removeAll(this .rowPath);
156:
157: for (int i = 0; i < repeater.getSize(); i++) {
158: Pointer rowPtr = repeaterContext
159: .createPath(this .rowPath + '[' + (i + 1) + ']');
160: JXPathContext rowContext = repeaterContext
161: .getRelativeContext(rowPtr);
162: this .rowBinding.saveFormToModel(repeater.getRow(i),
163: rowContext);
164: }
165: }
166: }
167:
168: public String toString() {
169: return this .getClass().getName() + " [widget="
170: + this .repeaterId + ", xpath=" + this .repeaterPath
171: + "]";
172: }
173: }
|