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.Locale;
020:
021: import org.apache.avalon.framework.logger.Logger;
022:
023: import org.apache.cocoon.forms.datatype.convertor.ConversionResult;
024: import org.apache.cocoon.forms.datatype.convertor.Convertor;
025: import org.apache.cocoon.forms.formmodel.Widget;
026:
027: import org.apache.commons.jxpath.JXPathContext;
028: import org.apache.commons.jxpath.JXPathException;
029:
030: /**
031: * ValueJXPathBinding provides an implementation of a {@link Binding}
032: * that loads and saves the information behind a specific xpath expresion
033: * (pointing to an attribute or text-node) to and from a specific CForms
034: * widget as identified by its id.
035: *
036: * @version $Id: ValueJXPathBinding.java 517733 2007-03-13 15:37:22Z vgritsenko $
037: */
038: public class ValueJXPathBinding extends JXPathBindingBase {
039:
040: /**
041: * The xpath expression to the objectModel property
042: */
043: private final String xpath;
044:
045: /**
046: * The id of the CForms form-widget
047: */
048: private final String fieldId;
049:
050: /**
051: * Flag indicating if the objectModel-property can be altered or not
052: */
053: private final JXPathBindingBase updateBinding;
054:
055: /**
056: * Optional convertor to convert values to and from strings when setting or reading
057: * the from the model. Especially used in combination with XML models where everything
058: * are strings.
059: */
060: private final Convertor convertor;
061:
062: /**
063: * The locale to pass to the {@link #convertor}.
064: */
065: private final Locale convertorLocale;
066:
067: /**
068: * Constructs FieldJXPathBinding.
069: *
070: * @param convertor may be null
071: */
072: public ValueJXPathBinding(
073: JXPathBindingBuilderBase.CommonAttributes commonAtts,
074: String widgetId, String xpath,
075: JXPathBindingBase[] updateBindings, Convertor convertor,
076: Locale convertorLocale) {
077: super (commonAtts);
078: this .fieldId = widgetId;
079: this .xpath = xpath;
080: this .updateBinding = new ComposedJXPathBindingBase(
081: JXPathBindingBuilderBase.CommonAttributes.DEFAULT,
082: updateBindings);
083: this .convertor = convertor;
084: this .convertorLocale = convertorLocale;
085: }
086:
087: public void enableLogging(Logger logger) {
088: super .enableLogging(logger);
089: this .updateBinding.enableLogging(logger);
090: }
091:
092: public String getId() {
093: return fieldId;
094: }
095:
096: public ComposedJXPathBindingBase getUpdateBinding() {
097: return (ComposedJXPathBindingBase) updateBinding;
098: }
099:
100: /**
101: * Actively performs the binding from the ObjectModel wrapped in a jxpath
102: * context to the CForms-form-widget specified in this object.
103: */
104: public void doLoad(Widget frmModel, JXPathContext jxpc)
105: throws BindingException {
106: Widget widget = selectWidget(frmModel, this .fieldId);
107: if (widget == null) {
108: throw new BindingException(
109: "The widget with the ID ["
110: + this .fieldId
111: + "] referenced in the binding does not exist in the form definition.");
112: }
113:
114: Object value = jxpc.getValue(this .xpath);
115: if (value != null && convertor != null) {
116: if (value instanceof String) {
117: ConversionResult conversionResult = convertor
118: .convertFromString((String) value,
119: convertorLocale, null);
120: if (conversionResult.isSuccessful())
121: value = conversionResult.getResult();
122: else
123: value = null;
124: } else {
125: getLogger()
126: .warn(
127: "Convertor ignored on backend-value which isn't of type String.");
128: }
129: }
130:
131: widget.setValue(value);
132: if (getLogger().isDebugEnabled()) {
133: getLogger().debug(
134: "Done loading " + this + " -- value= " + value);
135: }
136: }
137:
138: /**
139: * Actively performs the binding from the CForms-form to the ObjectModel
140: * wrapped in a jxpath context
141: */
142: public void doSave(Widget frmModel, JXPathContext jxpc)
143: throws BindingException {
144: Widget widget = selectWidget(frmModel, this .fieldId);
145: Object value = widget.getValue();
146: if (value != null && convertor != null) {
147: value = convertor.convertToString(value, convertorLocale,
148: null);
149: }
150:
151: Object oldValue = jxpc.getValue(this .xpath);
152: if (getLogger().isDebugEnabled()) {
153: getLogger().debug(
154: "value= " + value + " -- oldvalue=" + oldValue);
155: }
156:
157: boolean update = false;
158:
159: if ((value == null && oldValue != null) || value != null
160: && !value.equals(oldValue)) {
161: // first update the value itself
162: jxpc.createPathAndSetValue(this .xpath, value);
163:
164: // now perform any other bindings that need to be performed when the value is updated
165: JXPathContext subContext = null;
166: try {
167: subContext = jxpc.getRelativeContext(jxpc
168: .getPointer(this .xpath));
169: } catch (JXPathException e) {
170: // if the value has been set to null and the underlying model is a bean, then
171: // JXPath will not be able to create a relative context
172: if (getLogger().isDebugEnabled()) {
173: getLogger().debug(
174: "(Ignorable) problem binding field "
175: + widget.getRequestParameterName(),
176: e);
177: }
178: }
179: if (subContext != null) {
180: this .updateBinding
181: .saveFormToModel(frmModel, subContext);
182: }
183:
184: update = true;
185: }
186:
187: if (getLogger().isDebugEnabled()) {
188: getLogger().debug(
189: "done saving " + this + " -- value= " + value
190: + " -- on-update == " + update);
191: }
192: }
193:
194: public String toString() {
195: return "ValueJXPathBinding [widget=" + this .fieldId
196: + ", xpath=" + this .xpath + "]";
197: }
198:
199: public String getFieldId() {
200: return this .fieldId;
201: }
202:
203: public String getXPath() {
204: return this .xpath;
205: }
206:
207: public Convertor getConvertor() {
208: return this .convertor;
209: }
210:
211: public Locale getConvertorLocale() {
212: return this.convertorLocale;
213: }
214: }
|