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