KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > betwixt > io > read > ReadContext


1 /*
2  * Copyright 2001-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.commons.betwixt.io.read;
17
18 import java.beans.IntrospectionException JavaDoc;
19 import java.util.HashMap JavaDoc;
20
21 import org.apache.commons.betwixt.AttributeDescriptor;
22 import org.apache.commons.betwixt.BindingConfiguration;
23 import org.apache.commons.betwixt.ElementDescriptor;
24 import org.apache.commons.betwixt.XMLBeanInfo;
25 import org.apache.commons.betwixt.XMLIntrospector;
26 import org.apache.commons.betwixt.expression.Context;
27 import org.apache.commons.betwixt.expression.Updater;
28 import org.apache.commons.betwixt.strategy.ActionMappingStrategy;
29 import org.apache.commons.collections.ArrayStack;
30 import org.apache.commons.logging.Log;
31 import org.apache.commons.logging.LogFactory;
32 import org.xml.sax.Attributes JavaDoc;
33
34 /**
35   * <p>Extends <code>Context</code> to provide read specific functionality.</p>
36   * <p>
37   * Three stacks are used to manage the reading:
38   * </p>
39   * <ul>
40   * <li><strong>Action mapping stack</strong> contains the {@link MappingAction}'s
41   * used to execute the mapping of the current element and it's ancesters back to the
42   * document root.</li>
43   * <li><strong>Result stack</strong> contains the objects which are bound
44   * to the current element and to each of it's ancester's back to the root</li>
45   * <li><strong>Element mapping stack</strong> records the names of the element
46   * and the classes to which they are bound</li>
47   * </ul>
48   * @author Robert Burrell Donkina
49   * @since 0.5
50   */

51 public class ReadContext extends Context {
52
53     /** Beans indexed by ID strings */
54     private HashMap JavaDoc beansById = new HashMap JavaDoc();
55     /** Classloader to be used to load beans during reading */
56     private ClassLoader JavaDoc classLoader;
57     /** The read specific configuration */
58     private ReadConfiguration readConfiguration;
59     /** Records the element path together with the locations where classes were mapped*/
60     private ArrayStack elementMappingStack = new ArrayStack();
61     /** Contains actions for each element */
62     private ArrayStack actionMappingStack = new ArrayStack();
63     /** Stack contains all beans created */
64     private ArrayStack objectStack = new ArrayStack();
65     
66     private ArrayStack descriptorStack = new ArrayStack();
67     
68     private ArrayStack updaterStack = new ArrayStack();
69
70     private Class JavaDoc rootClass;
71     /** The <code>XMLIntrospector</code> to be used to map the xml*/
72     private XMLIntrospector xmlIntrospector;
73
74     /**
75       * Constructs a <code>ReadContext</code> with the same settings
76       * as an existing <code>Context</code>.
77       * @param context not null
78       * @param readConfiguration not null
79       */

80     public ReadContext(Context context, ReadConfiguration readConfiguration) {
81         super(context);
82         this.readConfiguration = readConfiguration;
83     }
84
85     /**
86       * Constructs a <code>ReadContext</code> with standard log.
87       * @param bindingConfiguration the dynamic configuration, not null
88       * @param readConfiguration the extra read configuration not null
89       */

90     public ReadContext(
91         BindingConfiguration bindingConfiguration,
92         ReadConfiguration readConfiguration) {
93         this(
94             LogFactory.getLog(ReadContext.class),
95             bindingConfiguration,
96             readConfiguration);
97     }
98
99     /**
100       * Base constructor
101       * @param log log to this Log
102       * @param bindingConfiguration the dynamic configuration, not null
103       * @param readConfiguration the extra read configuration not null
104       */

105     public ReadContext(
106         Log log,
107         BindingConfiguration bindingConfiguration,
108         ReadConfiguration readConfiguration) {
109         super(null, log, bindingConfiguration);
110         this.readConfiguration = readConfiguration;
111     }
112
113     /**
114       * Constructs a <code>ReadContext</code>
115       * with the same settings as an existing <code>Context</code>.
116       * @param readContext not null
117       */

118     public ReadContext(ReadContext readContext) {
119         super(readContext);
120         beansById = readContext.beansById;
121         classLoader = readContext.classLoader;
122         readConfiguration = readContext.readConfiguration;
123     }
124
125     /**
126      * Puts a bean into storage indexed by an (xml) ID.
127      *
128      * @param id the ID string of the xml element associated with the bean
129      * @param bean the Object to store, not null
130      */

131     public void putBean(String JavaDoc id, Object JavaDoc bean) {
132         beansById.put(id, bean);
133     }
134
135     /**
136      * Gets a bean from storage by an (xml) ID.
137      *
138      * @param id the ID string of the xml element associated with the bean
139      * @return the Object that the ID references, otherwise null
140      */

141     public Object JavaDoc getBean(String JavaDoc id) {
142         return beansById.get(id);
143     }
144
145     /**
146      * Clears the beans indexed by id.
147      */

148     public void clearBeans() {
149         beansById.clear();
150     }
151
152     /**
153       * Gets the classloader to be used.
154       * @return the classloader that should be used to load all classes, possibly null
155       */

156     public ClassLoader JavaDoc getClassLoader() {
157         return classLoader;
158     }
159
160     /**
161       * Sets the classloader to be used.
162       * @param classLoader the ClassLoader to be used, possibly null
163       */

164     public void setClassLoader(ClassLoader JavaDoc classLoader) {
165         this.classLoader = classLoader;
166     }
167
168     /**
169       * Gets the <code>BeanCreationChange</code> to be used to create beans
170       * when an element is mapped.
171       * @return the BeanCreationChain not null
172       */

173     public BeanCreationChain getBeanCreationChain() {
174         return readConfiguration.getBeanCreationChain();
175     }
176
177     /**
178      * Gets the strategy used to define default mappings actions
179      * for elements.
180      * @return <code>ActionMappingStrategy</code>. not null
181      */

182     public ActionMappingStrategy getActionMappingStrategy() {
183         return readConfiguration.getActionMappingStrategy();
184     }
185
186     /**
187       * Pops the top element from the element mapping stack.
188       * Also removes any mapped class marks below the top element.
189       *
190       * @return the name of the element popped
191       * if there are any more elements on the stack, otherwise null.
192       * This is the local name if the parser is namespace aware, otherwise the name
193       */

194     public String JavaDoc popElement() {
195         // since the descriptor stack is populated by pushElement,
196
// need to ensure that it's correct popped by popElement
197
if (!descriptorStack.isEmpty()) {
198             descriptorStack.pop();
199         }
200         
201         if (!updaterStack.isEmpty()) {
202             updaterStack.pop();
203         }
204         
205         Object JavaDoc top = null;
206         if (!elementMappingStack.isEmpty()) {
207             top = elementMappingStack.pop();
208             if (top != null) {
209                 if (!(top instanceof String JavaDoc)) {
210                     return popElement();
211                 }
212             }
213         }
214
215         return (String JavaDoc) top;
216     }
217
218     /**
219      * Gets the element name for the currently mapped element.
220      * @return the name of the currently mapped element,
221      * or null if there has been no element mapped
222      */

223     public String JavaDoc getCurrentElement() {
224         return (String JavaDoc) elementMappingStack.peek();
225     }
226
227     /**
228       * Gets the Class that was last mapped, if there is one.
229       *
230       * @return the Class last marked as mapped
231       * or null if no class has been mapped
232       */

233     public Class JavaDoc getLastMappedClass() {
234         Class JavaDoc lastMapped = null;
235         for (int i = 0, size = elementMappingStack.size();
236             i < size;
237             i++) {
238             Object JavaDoc entry = elementMappingStack.peek(i);
239             if (entry instanceof Class JavaDoc) {
240                 lastMapped = (Class JavaDoc) entry;
241                 break;
242             }
243         }
244         return lastMapped;
245     }
246
247     private ElementDescriptor getParentDescriptor() throws IntrospectionException JavaDoc {
248         ElementDescriptor result = null;
249         if (descriptorStack.size() > 1) {
250             result = (ElementDescriptor) descriptorStack.peek(1);
251         }
252         return result;
253     }
254     
255
256     /**
257       * Pushes the given element onto the element mapping stack.
258       *
259       * @param elementName the local name if the parser is namespace aware,
260       * otherwise the full element name. Not null
261       */

262     public void pushElement(String JavaDoc elementName) throws Exception JavaDoc {
263
264         elementMappingStack.push(elementName);
265         // special case to ensure that root class is appropriately marked
266
//TODO: is this really necessary?
267
ElementDescriptor nextDescriptor = null;
268         if (elementMappingStack.size() == 1 && rootClass != null) {
269             markClassMap(rootClass);
270             XMLBeanInfo rootClassInfo
271                 = getXMLIntrospector().introspect(rootClass);
272             nextDescriptor = rootClassInfo.getElementDescriptor();
273         } else {
274             ElementDescriptor currentDescriptor = getCurrentDescriptor();
275             if (currentDescriptor != null) {
276                 nextDescriptor = currentDescriptor.getElementDescriptor(elementName);
277             }
278         }
279         Updater updater = null;
280         if (nextDescriptor != null) {
281             updater = nextDescriptor.getUpdater();
282         }
283         updaterStack.push(updater);
284         descriptorStack.push(nextDescriptor);
285     }
286
287     /**
288       * Marks the element name stack with a class mapping.
289       * Relative paths and last mapped class are calculated using these marks.
290       *
291       * @param mappedClazz the Class which has been mapped at the current path, not null
292       */

293     public void markClassMap(Class JavaDoc mappedClazz) throws IntrospectionException JavaDoc {
294         if (mappedClazz.isArray()) {
295             mappedClazz = mappedClazz.getComponentType();
296         }
297         elementMappingStack.push(mappedClazz);
298         
299         XMLBeanInfo mappedClassInfo = getXMLIntrospector().introspect(mappedClazz);
300         ElementDescriptor mappedElementDescriptor = mappedClassInfo.getElementDescriptor();
301         descriptorStack.push(mappedElementDescriptor);
302         
303         Updater updater = mappedElementDescriptor.getUpdater();
304         updaterStack.push(updater);
305     }
306
307     /**
308      * Pops an action mapping from the stack
309      * @return
310      */

311     public MappingAction popMappingAction() {
312         return (MappingAction) actionMappingStack.pop();
313     }
314
315     /**
316      * Pushs an action mapping onto the stack
317      * @param mappingAction
318      */

319     public void pushMappingAction(MappingAction mappingAction) {
320         actionMappingStack.push(mappingAction);
321     }
322
323     /**
324      * Gets the current mapping action
325      * @return MappingAction
326      */

327     public MappingAction currentMappingAction() {
328         if (actionMappingStack.size() == 0)
329         {
330             return null;
331         }
332         return (MappingAction) actionMappingStack.peek();
333     }
334
335     public Object JavaDoc getBean() {
336         return objectStack.peek();
337     }
338
339     public void setBean(Object JavaDoc bean) {
340         // TODO: maybe need to deprecate the set bean method
341
// and push into subclass
342
// for now, do nothing
343
}
344
345     /**
346      * Pops the last mapping <code>Object</code> from the
347      * stack containing beans that have been mapped.
348      * @return
349      */

350     public Object JavaDoc popBean() {
351         return objectStack.pop();
352     }
353
354     /**
355      * Pushs a newly mapped <code>Object</code> onto the mapped bean stack.
356      * @param bean
357      */

358     public void pushBean(Object JavaDoc bean) {
359         objectStack.push(bean);
360     }
361
362     /**
363      * Gets the <code>XMLIntrospector</code> to be used to create
364      * the mappings for the xml.
365      * @return <code>XMLIntrospector, not null
366      */

367     public XMLIntrospector getXMLIntrospector() {
368         // read context is not intended to be used by multiple threads
369
// so no need to worry about lazy creation
370
if (xmlIntrospector == null) {
371             xmlIntrospector = new XMLIntrospector();
372         }
373         return xmlIntrospector;
374     }
375
376     /**
377      * Sets the <code>XMLIntrospector</code> to be used to create
378      * the mappings for the xml.
379      * @param xmlIntrospector <code>XMLIntrospector</code>, not null
380      */

381     public void setXMLIntrospector(XMLIntrospector xmlIntrospector) {
382         this.xmlIntrospector = xmlIntrospector;
383     }
384
385     public Class JavaDoc getRootClass() {
386         return rootClass;
387     }
388
389     public void setRootClass(Class JavaDoc rootClass) {
390         this.rootClass = rootClass;
391     }
392
393     /**
394      * Gets the <code>ElementDescriptor</code> that describes the
395      * mapping for the current element.
396      * @return <code>ElementDescriptor</code> or null if there is no
397      * current mapping
398      * @throws Exception
399      */

400     public ElementDescriptor getCurrentDescriptor() throws Exception JavaDoc {
401         ElementDescriptor result = null;
402         if (!descriptorStack.empty()) {
403             result = (ElementDescriptor) descriptorStack.peek();
404         }
405         return result;
406     }
407     
408     /**
409      * Populates the object mapped by the <code>AttributeDescriptor</code>s
410      * with the values in the given <code>Attributes</code>.
411      * @param attributeDescriptors <code>AttributeDescriptor</code>s, not null
412      * @param attributes <code>Attributes</code>, not null
413      */

414     public void populateAttributes(
415         AttributeDescriptor[] attributeDescriptors,
416         Attributes JavaDoc attributes) {
417
418         Log log = getLog();
419         if (attributeDescriptors != null) {
420             for (int i = 0, size = attributeDescriptors.length;
421                 i < size;
422                 i++) {
423                 AttributeDescriptor attributeDescriptor =
424                     attributeDescriptors[i];
425
426                 // The following isn't really the right way to find the attribute
427
// but it's quite robust.
428
// The idea is that you try both namespace and local name first
429
// and if this returns null try the qName.
430
String JavaDoc value =
431                     attributes.getValue(
432                         attributeDescriptor.getURI(),
433                         attributeDescriptor.getLocalName());
434
435                 if (value == null) {
436                     value =
437                         attributes.getValue(
438                             attributeDescriptor.getQualifiedName());
439                 }
440
441                 if (log.isTraceEnabled()) {
442                     log.trace("Attr URL:" + attributeDescriptor.getURI());
443                     log.trace(
444                         "Attr LocalName:" + attributeDescriptor.getLocalName());
445                     log.trace(value);
446                 }
447
448                 Updater updater = attributeDescriptor.getUpdater();
449                 log.trace(updater);
450                 if (updater != null && value != null) {
451                     updater.update(this, value);
452                 }
453             }
454         }
455     }
456
457     /**
458      * <p>Pushes an <code>Updater</code> onto the stack.</p>
459      * <p>
460      * <strong>Note</strong>Any action pushing an <code>Updater</code> onto
461      * the stack should take responsibility for popping
462      * the updater from the stack at an appropriate time.
463      * </p>
464      * <p>
465      * <strong>Usage:</strong> this may be used by actions
466      * which require a temporary object to be updated.
467      * Pushing an updater onto the stack allow actions
468      * downstream to transparently update the temporary proxy.
469      * </p>
470      * @param updater Updater, possibly null
471      */

472     public void pushUpdater(Updater updater) {
473         updaterStack.push(updater);
474     }
475     
476     /**
477      * Pops the top <code>Updater</code> from the stack.
478      * <p>
479      * <strong>Note</strong>Any action pushing an <code>Updater</code> onto
480      * the stack should take responsibility for popping
481      * the updater from the stack at an appropriate time.
482      * </p>
483      * @return <code>Updater</code>, possibly null
484      */

485     public Updater popUpdater() {
486         return (Updater) updaterStack.pop();
487     }
488
489     /**
490      * Gets the current <code>Updater</code>.
491      * This may (or may not) be the updater for the current
492      * descriptor.
493      * If the current descriptor is a bean child,
494      * the the current updater will (most likely)
495      * be the updater for the property.
496      * Actions (that, for example, use proxy objects)
497      * may push updaters onto the stack.
498      * @return Updater, possibly null
499      */

500     public Updater getCurrentUpdater() {
501         // TODO: think about whether this is right
502
// it makes some sense to look back up the
503
// stack until a non-empty updater is found.
504
// actions who need to put a stock to this
505
// behaviour can always use an ignoring implementation.
506
Updater result = null;
507         if (!updaterStack.empty()) {
508             result = (Updater) updaterStack.peek();
509             if ( result == null && updaterStack.size() >1 ) {
510                 result = (Updater) updaterStack.peek(1);
511             }
512         }
513         return result;
514     }
515
516 }
517
Popular Tags