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: import java.util.LinkedList;
021: import java.util.Locale;
022:
023: import org.apache.avalon.framework.logger.Logger;
024:
025: import org.apache.cocoon.forms.datatype.convertor.ConversionResult;
026: import org.apache.cocoon.forms.datatype.convertor.Convertor;
027: import org.apache.cocoon.forms.formmodel.Widget;
028:
029: import org.apache.commons.jxpath.JXPathContext;
030: import org.apache.commons.jxpath.Pointer;
031:
032: /**
033: * Simple binding for multi fields: on save, first deletes the target data
034: * before recreating it from scratch.
035: *
036: * @version $Id: MultiValueJXPathBinding.java 450255 2006-09-26 23:48:43Z vgritsenko $
037: */
038: public class MultiValueJXPathBinding extends JXPathBindingBase {
039:
040: private final String multiValueId;
041: private final String multiValuePath;
042: private final String rowPath;
043: private final JXPathBindingBase updateBinding;
044: private final Convertor convertor;
045: private final Locale convertorLocale;
046:
047: public MultiValueJXPathBinding(
048: JXPathBindingBuilderBase.CommonAttributes commonAtts,
049: String multiValueId, String multiValuePath, String rowPath,
050: JXPathBindingBase[] updateBindings, Convertor convertor,
051: Locale convertorLocale) {
052: super (commonAtts);
053: this .multiValueId = multiValueId;
054: this .multiValuePath = multiValuePath;
055: this .rowPath = rowPath;
056: this .updateBinding = new ComposedJXPathBindingBase(
057: JXPathBindingBuilderBase.CommonAttributes.DEFAULT,
058: updateBindings);
059: this .convertor = convertor;
060: this .convertorLocale = convertorLocale;
061: }
062:
063: public void enableLogging(Logger logger) {
064: super .enableLogging(logger);
065: this .updateBinding.enableLogging(logger);
066: }
067:
068: public String getId() {
069: return multiValueId;
070: }
071:
072: public String getMultiValuePath() {
073: return multiValuePath;
074: }
075:
076: public String getRowPath() {
077: return rowPath;
078: }
079:
080: public ComposedJXPathBindingBase getUpdateBinding() {
081: return (ComposedJXPathBindingBase) updateBinding;
082: }
083:
084: public Convertor getConvertor() {
085: return convertor;
086: }
087:
088: public Locale getLocale() {
089: return convertorLocale;
090: }
091:
092: public void doLoad(Widget frmModel, JXPathContext jctx)
093: throws BindingException {
094: Widget widget = selectWidget(frmModel, this .multiValueId);
095: if (widget == null) {
096: throw new BindingException(
097: "The widget with the ID ["
098: + this .multiValueId
099: + "] referenced in the binding does not exist in the form definition.");
100: }
101:
102: // Move to multi value context
103: Pointer ptr = jctx.getPointer(this .multiValuePath);
104: if (ptr.getNode() != null) {
105: // There are some nodes to load from
106:
107: JXPathContext multiValueContext = jctx
108: .getRelativeContext(ptr);
109: // build a jxpath iterator for pointers
110: Iterator rowPointers = multiValueContext
111: .iterate(this .rowPath);
112:
113: LinkedList list = new LinkedList();
114:
115: while (rowPointers.hasNext()) {
116: Object value = rowPointers.next();
117:
118: if (value != null && convertor != null) {
119: if (value instanceof String) {
120: ConversionResult conversionResult = convertor
121: .convertFromString((String) value,
122: convertorLocale, null);
123: if (conversionResult.isSuccessful())
124: value = conversionResult.getResult();
125: else
126: value = null;
127: } else {
128: getLogger()
129: .warn(
130: "Convertor ignored on backend-value which isn't of type String.");
131: }
132: }
133:
134: list.add(value);
135: }
136:
137: widget.setValue(list.toArray());
138: }
139:
140: if (getLogger().isDebugEnabled())
141: getLogger().debug("done loading values " + toString());
142: }
143:
144: public void doSave(Widget frmModel, JXPathContext jctx)
145: throws BindingException {
146: Widget widget = selectWidget(frmModel, this .multiValueId);
147: Object[] values = (Object[]) widget.getValue();
148:
149: JXPathContext multiValueContext = jctx.getRelativeContext(jctx
150: .createPath(this .multiValuePath));
151:
152: // Delete all that is already present
153:
154: // Unfortunately the following statement doesn't work (it doesn't removes all elements from the
155: // list because of a bug in JXPath I wasn't able to locate).
156: //multiValueContext.removeAll(this.rowPath);
157:
158: // TODO: This is a workaround until the bug in commons-jxpath is found, fixed and released
159: Iterator rowPointers = multiValueContext
160: .iteratePointers(this .rowPath);
161: int cnt = 0;
162: while (rowPointers.hasNext()) {
163: cnt++;
164: rowPointers.next();
165: }
166: while (cnt >= 1) {
167: String thePath = this .rowPath + "[" + cnt + "]";
168: multiValueContext.removePath(thePath);
169: cnt--;
170: }
171:
172: boolean update = false;
173:
174: if (values != null) {
175: // first update the values
176: for (int i = 0; i < values.length; i++) {
177: String path = this .rowPath + '[' + (i + 1) + ']';
178: Pointer rowPtr = multiValueContext.createPath(path);
179:
180: Object value = values[i];
181: if (value != null && convertor != null) {
182: value = convertor.convertToString(value,
183: convertorLocale, null);
184: }
185:
186: rowPtr.setValue(value);
187: }
188:
189: // now perform any other bindings that need to be performed when the value is updated
190: this .updateBinding.saveFormToModel(frmModel,
191: multiValueContext);
192:
193: update = true;
194: }
195:
196: if (getLogger().isDebugEnabled()) {
197: getLogger().debug(
198: "done saving " + toString() + " -- on-update == "
199: + update);
200: }
201:
202: }
203:
204: public String toString() {
205: return "MultiValueJXPathBinding [widget=" + this .multiValueId
206: + ", xpath=" + this .multiValuePath + "]";
207: }
208:
209: }
|