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.HashMap;
020: import java.util.Map;
021:
022: import org.apache.avalon.framework.logger.LogEnabled;
023: import org.apache.avalon.framework.logger.Logger;
024: import org.apache.cocoon.util.jxpath.DOMFactory;
025: import org.apache.cocoon.woody.formmodel.Widget;
026: import org.apache.commons.jxpath.JXPathContext;
027: import org.w3c.dom.Node;
028:
029: /**
030: * Provides a base class for hooking up Binding implementations that use the
031: * Jakarta Commons <a href="http://jakarta.apache.org/commons/jxpath/index.html">
032: * JXPath package</a>.
033: *
034: * @version CVS $Id: JXPathBindingBase.java 433543 2006-08-22 06:22:54Z crossley $
035: */
036: public abstract class JXPathBindingBase implements Binding, LogEnabled {
037:
038: /**
039: * Avalon Logger to use in all subclasses.
040: */
041: private Logger logger;
042:
043: /**
044: * Object holding the values of the common objects on all Bindings.
045: */
046: private final JXPathBindingBuilderBase.CommonAttributes commonAtts;
047:
048: /**
049: * Parent binding of this binding.
050: */
051: protected Binding parent;
052:
053: /**
054: * Cache of class definitions
055: */
056: protected Map classes;
057:
058: private JXPathBindingBase() {
059: this (JXPathBindingBuilderBase.CommonAttributes.DEFAULT);
060: }
061:
062: protected JXPathBindingBase(
063: JXPathBindingBuilderBase.CommonAttributes commonAtts) {
064: this .commonAtts = commonAtts;
065: }
066:
067: /**
068: * Sets parent binding.
069: */
070: public void setParent(Binding binding) {
071: this .parent = binding;
072: }
073:
074: /**
075: * Returns binding definition id.
076: */
077: public String getId() {
078: return null;
079: }
080:
081: public Binding getClass(String id) {
082: Binding classBinding = null;
083: if (classes != null) {
084: // Query cache for class
085: classBinding = (Binding) classes.get(id);
086: }
087: if (classBinding == null) {
088: // Query parent for class
089: if (parent != null) {
090: classBinding = parent.getClass(id);
091: // Cache result
092: if (classes == null) {
093: classes = new HashMap();
094: }
095: classes.put(id, classBinding);
096: } else {
097: // TODO: Improve message to include source location.
098: throw new RuntimeException("Class \"" + id
099: + "\" not found.");
100: }
101: }
102: return classBinding;
103: }
104:
105: protected Widget getWidget(Widget widget, String id) {
106: Widget childWidget = widget.getWidget(id);
107: if (childWidget != null) {
108: return childWidget;
109: } else {
110: throw new RuntimeException(getClass().getName()
111: + ": Widget \"" + id
112: + "\" does not exist in container \""
113: + widget.getFullyQualifiedId() + "\" ("
114: + widget.getLocation() + ").");
115: }
116: }
117:
118: /**
119: * Performs the actual load binding regardless of the configured value of the "direction" attribute.
120: * Abstract method that subclasses need to implement for specific activity.
121: */
122: public abstract void doLoad(Widget frmModel, JXPathContext jxpc)
123: throws BindingException;
124:
125: /**
126: * Redefines the Binding action as working on a JXPathContext Type rather
127: * then on generic objects.
128: * Executes the actual loading via {@link #doLoad(Widget, JXPathContext)}
129: * depending on the configured value of the "direction" attribute.
130: */
131: public final void loadFormFromModel(Widget frmModel,
132: JXPathContext jxpc) throws BindingException {
133: boolean inheritedLeniency = jxpc.isLenient();
134: applyLeniency(jxpc);
135: if (this .commonAtts.loadEnabled) {
136: doLoad(frmModel, jxpc);
137: }
138: jxpc.setLenient(inheritedLeniency);
139: }
140:
141: /**
142: * Hooks up with the more generic Binding of any objectModel by wrapping
143: * it up in a JXPathContext object and then transfering control over to
144: * the new overloaded version of this method.
145: */
146: public final void loadFormFromModel(Widget frmModel, Object objModel)
147: throws BindingException {
148: if (objModel != null) {
149: JXPathContext jxpc = makeJXPathContext(objModel);
150: loadFormFromModel(frmModel, jxpc);
151: } else {
152: throw new NullPointerException(
153: "null object passed to loadFormFromModel() method");
154: }
155: }
156:
157: /**
158: * Performs the actual save binding regardless of the configured value of the "direction" attribute.
159: * Abstract method that subclasses need to implement for specific activity.
160: */
161: public abstract void doSave(Widget frmModel, JXPathContext jxpc)
162: throws BindingException;
163:
164: /**
165: * Redefines the Binding action as working on a JXPathContext Type rather
166: * then on generic objects.
167: * Executes the actual saving via {@link #doSave(Widget, JXPathContext)}
168: * depending on the configured value of the "direction" attribute.
169: */
170: public final void saveFormToModel(Widget frmModel,
171: JXPathContext jxpc) throws BindingException {
172: boolean inheritedLeniency = jxpc.isLenient();
173: applyLeniency(jxpc);
174: if (this .commonAtts.saveEnabled) {
175: doSave(frmModel, jxpc);
176: }
177: jxpc.setLenient(inheritedLeniency);
178: }
179:
180: /**
181: * Hooks up with the more generic Binding of any objectModel by wrapping
182: * it up in a JXPathContext object and then transfering control over to
183: * the new overloaded version of this method.
184: */
185: public void saveFormToModel(Widget frmModel, Object objModel)
186: throws BindingException {
187: if (objModel != null) {
188: JXPathContext jxpc = makeJXPathContext(objModel);
189: saveFormToModel(frmModel, jxpc);
190: } else {
191: throw new NullPointerException(
192: "null object passed to saveFormToModel() method");
193: }
194: }
195:
196: private void applyLeniency(JXPathContext jxpc) {
197: if (this .commonAtts.leniency != null) {
198: jxpc.setLenient(this .commonAtts.leniency.booleanValue());
199: }
200: }
201:
202: private JXPathContext makeJXPathContext(Object objModel) {
203: JXPathContext jxpc;
204: if (!(objModel instanceof JXPathContext)) {
205: jxpc = JXPathContext.newContext(objModel);
206: jxpc.setLenient(true);
207: if (objModel instanceof Node) {
208: jxpc.setFactory(new DOMFactory());
209: }
210: } else {
211: jxpc = (JXPathContext) objModel;
212: }
213: return jxpc;
214: }
215:
216: /**
217: * Receives the Avalon logger to use.
218: * Subclasses should always start with <code>super.enableLogging(logger)
219: * </code> in possible overriding versions.
220: */
221: public void enableLogging(Logger logger) {
222: this .logger = logger;
223: }
224:
225: protected Logger getLogger() {
226: return logger;
227: }
228: }
|