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