KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > forms > binding > JXPathBindingBase


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.cocoon.forms.binding;
17
18 import java.util.Collection JavaDoc;
19 import java.util.Iterator JavaDoc;
20 import java.util.Map JavaDoc;
21
22 import org.apache.avalon.framework.logger.LogEnabled;
23 import org.apache.avalon.framework.logger.Logger;
24 import org.apache.cocoon.forms.binding.library.Library;
25 import org.apache.cocoon.forms.formmodel.Widget;
26 import org.apache.cocoon.util.jxpath.DOMFactory;
27 import org.apache.commons.jxpath.JXPathContext;
28 import org.apache.commons.jxpath.Pointer;
29 import org.apache.commons.jxpath.ri.model.beans.BeanPropertyPointer;
30 import org.apache.commons.jxpath.util.TypeUtils;
31 import org.apache.commons.lang.exception.NestableRuntimeException;
32
33 /**
34  * Provides a base class for hooking up Binding implementations that use the
35  * Jakarta Commons <a HREF="http://jakarta.apache.org/commons/jxpath/index.html">
36  * JXPath package</a>.
37  *
38  * @version $Id: JXPathBindingBase.java 292395 2005-09-29 06:38:10Z sylvain $
39  */

40 public abstract class JXPathBindingBase implements Binding, LogEnabled {
41
42     /**
43      * Avalon Logger to use in all subclasses.
44      */

45     private Logger logger;
46     
47     /**
48      * the local library, if this is the top binding
49      */

50     private Library localLibrary = null;
51     
52     /**
53      * Object holding the values of the common objects on all Bindings.
54      */

55     private final JXPathBindingBuilderBase.CommonAttributes commonAtts;
56
57     /**
58      * Parent binding of this binding.
59      */

60     protected Binding parent;
61
62     /**
63      * Cache of class definitions
64      */

65     protected Map JavaDoc classes;
66
67     private JXPathBindingBase() {
68         this(JXPathBindingBuilderBase.CommonAttributes.DEFAULT);
69     }
70
71     protected JXPathBindingBase(
72             JXPathBindingBuilderBase.CommonAttributes commonAtts) {
73         this.commonAtts = commonAtts;
74     }
75     
76     public Library getLocalLibrary() {
77         if(parent != null) {
78             return parent.getLocalLibrary();
79         } else {
80             return localLibrary;
81         }
82     }
83     public void setLocalLibary(Library lib) {
84         this.localLibrary = lib;
85     }
86     
87     public boolean isValid() {
88         if(this.localLibrary!=null) {
89             if(parent!=null)
90                 return parent.isValid();
91             return true; // no library used
92
} else {
93             try {
94                 return !this.localLibrary.dependenciesHaveChanged();
95             } catch(Exception JavaDoc e) {
96                 logger.error("Error checking dependencies!",e);
97                 throw new NestableRuntimeException("Error checking dependencies!",e);
98             }
99         }
100     }
101     
102     public JXPathBindingBuilderBase.CommonAttributes getCommonAtts() {
103         return this.commonAtts;
104     }
105     
106     /**
107      * Gets source location of this binding.
108      */

109     public String JavaDoc getLocation() {
110         return this.commonAtts.location;
111     }
112
113     /**
114      * Sets parent binding.
115      */

116     public void setParent(Binding binding) {
117         this.parent = binding;
118     }
119
120     /**
121      * Returns binding definition id.
122      */

123     public String JavaDoc getId() {
124         return null;
125     }
126
127     public Binding getClass(String JavaDoc id) {
128         
129         Binding classBinding = null;
130         
131         try {
132             if(this.localLibrary!=null && (classBinding = this.localLibrary.getBinding(id))!=null)
133                 return classBinding;
134         } catch(Exception JavaDoc ignore) {}
135         
136         if (classes != null) {
137             // Query cache for class
138
classBinding = (Binding)classes.get(id);
139         }
140         if (classBinding == null) {
141             // Query parent for class
142
if (parent != null) {
143                 classBinding = parent.getClass(id);
144                 // dont cache, doesn't matter and makes things complicated with libraries
145
// **************************************
146
// Cache result
147
/*if (classes == null) {
148                  classes = new HashMap();
149                  }
150                  classes.put(id, classBinding);*/

151                 // **************************************
152
} else {
153                 throw new RuntimeException JavaDoc("Class \"" + id + "\" not found (" + getLocation() + ")");
154             }
155         }
156         return classBinding;
157     }
158
159     /**
160      * Helper method that selects a child-widget with a given id from a parent.
161      *
162      * @param parent containing the child-widget to return.
163      * @param id of the childWidget to find, if this is <code>null</code> then the parent is returned.
164      * @return the selected widget
165      *
166      * @throws RuntimeException if the id is not null and points to a
167      * child-widget that cannot be found.
168      */

169     protected Widget selectWidget(Widget parent, String JavaDoc id) {
170         if (id == null) return parent;
171         
172         Widget childWidget = null;
173         
174         childWidget = parent.lookupWidget(id);
175             
176         if (childWidget == null) {
177             String JavaDoc containerId = parent.getRequestParameterName();
178             if(containerId == null || "".equals(containerId)) {
179                 containerId = "top-level form-widget";
180             } else {
181                 containerId = "container \"" + containerId + "\"";
182             }
183             throw new RuntimeException JavaDoc(getClass().getName() + " (" + getLocation() + "): Widget \"" +
184                     id + "\" does not exist in the " + containerId +
185                     " (" + parent.getLocation() + ").");
186         }
187         
188         return childWidget;
189     }
190
191     /**
192      * Performs the actual load binding regardless of the configured value of the "direction" attribute.
193      * Abstract method that subclasses need to implement for specific activity.
194      */

195     public abstract void doLoad(Widget frmModel, JXPathContext jxpc)
196         throws BindingException;
197
198     /**
199      * Redefines the Binding action as working on a JXPathContext Type rather
200      * then on generic objects.
201      * Executes the actual loading via {@link #doLoad(Widget, JXPathContext)}
202      * depending on the configured value of the "direction" attribute.
203      */

204     public final void loadFormFromModel(Widget frmModel, JXPathContext jxpc)
205             throws BindingException {
206         boolean inheritedLeniency = jxpc.isLenient();
207         applyLeniency(jxpc);
208         applyNSDeclarations(jxpc);
209         if (this.commonAtts.loadEnabled) {
210             doLoad(frmModel, jxpc);
211         }
212         jxpc.setLenient(inheritedLeniency);
213     }
214
215     /**
216      * Hooks up with the more generic Binding of any objectModel by wrapping
217      * it up in a JXPathContext object and then transfering control over to
218      * the new overloaded version of this method.
219      */

220     public final void loadFormFromModel(Widget frmModel, Object JavaDoc objModel)
221             throws BindingException {
222         if (objModel != null) {
223             JXPathContext jxpc = makeJXPathContext(objModel);
224             loadFormFromModel(frmModel, jxpc);
225         } else {
226             throw new NullPointerException JavaDoc(
227                     "null object passed to loadFormFromModel() method");
228         }
229     }
230
231     /**
232      * Performs the actual save binding regardless of the configured value of the "direction" attribute.
233      * Abstract method that subclasses need to implement for specific activity.
234      */

235     public abstract void doSave(Widget frmModel, JXPathContext jxpc)
236             throws BindingException;
237
238     /**
239      * Redefines the Binding action as working on a JXPathContext Type rather
240      * then on generic objects.
241      * Executes the actual saving via {@link #doSave(Widget, JXPathContext)}
242      * depending on the configured value of the "direction" attribute.
243      */

244     public final void saveFormToModel(Widget frmModel, JXPathContext jxpc)
245             throws BindingException{
246         boolean inheritedLeniency = jxpc.isLenient();
247         applyLeniency(jxpc);
248         applyNSDeclarations(jxpc);
249         if (this.commonAtts.saveEnabled) {
250             doSave(frmModel, jxpc);
251         }
252         jxpc.setLenient(inheritedLeniency);
253     }
254
255     /**
256      * Hooks up with the more generic Binding of any objectModel by wrapping
257      * it up in a JXPathContext object and then transfering control over to
258      * the new overloaded version of this method.
259      */

260     public void saveFormToModel(Widget frmModel, Object JavaDoc objModel)
261                 throws BindingException {
262         if (objModel != null) {
263             JXPathContext jxpc = makeJXPathContext(objModel);
264             saveFormToModel(frmModel, jxpc);
265         } else {
266             throw new NullPointerException JavaDoc(
267                     "null object passed to saveFormToModel() method");
268         }
269     }
270
271     private void applyLeniency(JXPathContext jxpc) {
272         if (this.commonAtts.leniency != null) {
273             jxpc.setLenient(this.commonAtts.leniency.booleanValue());
274         }
275     }
276
277     private void applyNSDeclarations(JXPathContext jxpc)
278     {
279         if (this.commonAtts.nsDeclarations != null)
280         {
281             Iterator JavaDoc keysIter = this.commonAtts.nsDeclarations.keySet().iterator();
282             while (keysIter.hasNext())
283             {
284                 String JavaDoc nsuri = (String JavaDoc) keysIter.next();
285                 String JavaDoc pfx = (String JavaDoc) this.commonAtts.nsDeclarations.get(nsuri);
286                 jxpc.registerNamespace(pfx, nsuri);
287             }
288         }
289     }
290
291     private JXPathContext makeJXPathContext(Object JavaDoc objModel) {
292         JXPathContext jxpc;
293         if (!(objModel instanceof JXPathContext)) {
294             jxpc = JXPathContext.newContext(objModel);
295             jxpc.setLenient(true);
296             jxpc.setFactory(new BindingJXPathFactory());
297         } else {
298             jxpc = (JXPathContext) objModel;
299         }
300         return jxpc;
301     }
302
303     /**
304      * Receives the Avalon logger to use.
305      * Subclasses should always start with <code>super.enableLogging(logger)
306      * </code> in possible overriding versions.
307      */

308     public void enableLogging(Logger logger) {
309         this.logger = logger;
310     }
311
312     protected Logger getLogger() {
313         return logger;
314     }
315     
316     /**
317      * JXPath factory that combines the DOMFactory and support for collections.
318      */

319     private static class BindingJXPathFactory extends DOMFactory {
320         
321         public boolean createObject(JXPathContext context, Pointer pointer, Object JavaDoc parent, String JavaDoc name, int index) {
322             if (createCollectionItem(context, pointer, parent, name, index)) {
323                 return true;
324             // AG: If this is a bean, then the object is supposed to exists.
325
} else if (pointer instanceof BeanPropertyPointer) {
326                 return createBeanField(context, pointer, parent, name, index);
327             } else {
328                 return super.createObject(context, pointer, parent, name, index);
329             }
330         }
331         
332         private boolean createCollectionItem(JXPathContext context, Pointer pointer, Object JavaDoc parent, String JavaDoc name, int index) {
333             // FIXME: don't clearly understand how this works.
334
// see http://marc.theaimsgroup.com/?l=xml-cocoon-dev&m=111148567029114&w=2
335
final Object JavaDoc o = context.getValue(name);
336             if (o == null) {
337                 return false;
338             }
339             if (o instanceof Collection JavaDoc) {
340                 ((Collection JavaDoc)o).add(null);
341             } else if(o.getClass().isArray()) {
342                 // not yet supported
343
return false;
344             } else {
345                 return false;
346             }
347             return true;
348         }
349
350         // AG: Create the Object for the field as defined in the Bean.
351
// The value we will set here is not important. JXPath knows that it is UNITIALIZED.
352
// if we set it Pointer Value to null then the code will throw an exception.
353
//
354
// In short, there is no harm. The value will never show up.
355
// TODO: Manage other forms' types as Date, Bean and others not covered by this method.
356
private boolean createBeanField(JXPathContext context, Pointer pointer, Object JavaDoc parent, String JavaDoc name, int index) {
357             try {
358                 Class JavaDoc clazz = parent.getClass().getDeclaredField(name).getType();
359                 Object JavaDoc o = context.getValue(name);
360                 if (o == null) {
361                     final Class JavaDoc[] parametersTypes = {String JavaDoc.class};
362                     final Object JavaDoc[] initArgs = {"0"};
363                     try {
364                         // AG: Here we service Booleans, Strings and Number() + his Direct know subclasses:
365
// (BigDecimal, BigInteger, Byte, Double, Float, Integer, Long, Short)
366
// as well as other classes that use an String as Constructor parameter.
367
o = clazz.getConstructor(parametersTypes).newInstance(initArgs);
368                     } catch (Exception JavaDoc e) {
369                         // AG: The class has not a constructor using a String as a parameter.
370
// ie: Boolean(String), Integer(String), etc.
371
// Lets try with a constructor with no parameters. ie: Number().
372
o = clazz.newInstance();
373                     }
374                 } else if (TypeUtils.canConvert(o, clazz)) {
375                     o = TypeUtils.convert(o, clazz);
376                 }
377                 if (o != null) {
378                     pointer.setValue(o);
379                     return true; // OK. We have an initial Object of the right Class initialized.
380
}
381             } catch (Exception JavaDoc e) {
382                 // TODO: Output info in logs.
383
}
384             return false;
385         }
386     }
387 }
388
Popular Tags