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 517733 2007-03-13 15:37:22Z 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 " + this );
142: }
143: }
144:
145: public void doSave(Widget frmModel, JXPathContext jctx)
146: throws BindingException {
147: Widget widget = selectWidget(frmModel, this .multiValueId);
148: Object[] values = (Object[]) widget.getValue();
149:
150: JXPathContext multiValueContext = jctx.getRelativeContext(jctx
151: .createPath(this .multiValuePath));
152:
153: // Delete all that is already present
154:
155: // Unfortunately the following statement doesn't work (it doesn't removes all elements from the
156: // list because of a bug in JXPath I wasn't able to locate).
157: //multiValueContext.removeAll(this.rowPath);
158:
159: // TODO: This is a workaround until the bug in commons-jxpath is found, fixed and released
160: Iterator rowPointers = multiValueContext
161: .iteratePointers(this .rowPath);
162: int cnt = 0;
163: while (rowPointers.hasNext()) {
164: cnt++;
165: rowPointers.next();
166: }
167: while (cnt >= 1) {
168: String thePath = this .rowPath + "[" + cnt + "]";
169: multiValueContext.removePath(thePath);
170: cnt--;
171: }
172:
173: boolean update = false;
174:
175: if (values != null) {
176: // first update the values
177: for (int i = 0; i < values.length; i++) {
178: String path = this .rowPath + '[' + (i + 1) + ']';
179: Pointer rowPtr = multiValueContext.createPath(path);
180:
181: Object value = values[i];
182: if (value != null && convertor != null) {
183: value = convertor.convertToString(value,
184: convertorLocale, null);
185: }
186:
187: rowPtr.setValue(value);
188: }
189:
190: // now perform any other bindings that need to be performed when the value is updated
191: this .updateBinding.saveFormToModel(frmModel,
192: multiValueContext);
193:
194: update = true;
195: }
196:
197: if (getLogger().isDebugEnabled()) {
198: getLogger().debug(
199: "done saving " + this + " -- on-update == "
200: + update);
201: }
202:
203: }
204:
205: public String toString() {
206: return "MultiValueJXPathBinding [widget=" + this .multiValueId
207: + ", xpath=" + this .multiValuePath + "]";
208: }
209:
210: }
|