1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.commons.betwixt.io.read;
17
18 import java.beans.IntrospectionException;
19
20 import org.apache.commons.betwixt.AttributeDescriptor;
21 import org.apache.commons.betwixt.BindingConfiguration;
22 import org.apache.commons.betwixt.ElementDescriptor;
23 import org.apache.commons.betwixt.Options;
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;
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 /*** Classloader to be used to load beans during reading */
54 private ClassLoader classLoader;
55 /*** The read specific configuration */
56 private ReadConfiguration readConfiguration;
57 /*** Records the element path together with the locations where classes were mapped*/
58 private ArrayStack elementMappingStack = new ArrayStack();
59 /*** Contains actions for each element */
60 private ArrayStack actionMappingStack = new ArrayStack();
61 /*** Stack contains all beans created */
62 private ArrayStack objectStack = new ArrayStack();
63 /*** Stack contains element descriptors */
64 private ArrayStack descriptorStack = new ArrayStack();
65 /*** Stack contains updaters */
66 private ArrayStack updaterStack = new ArrayStack();
67
68 private Class rootClass;
69 /*** The <code>XMLIntrospector</code> to be used to map the xml*/
70 private XMLIntrospector xmlIntrospector;
71
72 /***
73 * Constructs a <code>ReadContext</code> with the same settings
74 * as an existing <code>Context</code>.
75 * @param context not null
76 * @param readConfiguration not null
77 */
78 public ReadContext(Context context, ReadConfiguration readConfiguration) {
79 super(context);
80 this.readConfiguration = readConfiguration;
81 }
82
83 /***
84 * Constructs a <code>ReadContext</code> with standard log.
85 * @param bindingConfiguration the dynamic configuration, not null
86 * @param readConfiguration the extra read configuration not null
87 */
88 public ReadContext(
89 BindingConfiguration bindingConfiguration,
90 ReadConfiguration readConfiguration) {
91 this(
92 LogFactory.getLog(ReadContext.class),
93 bindingConfiguration,
94 readConfiguration);
95 }
96
97 /***
98 * Base constructor
99 * @param log log to this Log
100 * @param bindingConfiguration the dynamic configuration, not null
101 * @param readConfiguration the extra read configuration not null
102 */
103 public ReadContext(
104 Log log,
105 BindingConfiguration bindingConfiguration,
106 ReadConfiguration readConfiguration) {
107 super(null, log, bindingConfiguration);
108 this.readConfiguration = readConfiguration;
109 }
110
111 /***
112 * Constructs a <code>ReadContext</code>
113 * with the same settings as an existing <code>Context</code>.
114 * @param readContext not null
115 */
116 public ReadContext(ReadContext readContext) {
117 super(readContext);
118 classLoader = readContext.classLoader;
119 readConfiguration = readContext.readConfiguration;
120 }
121
122 /***
123 * Puts a bean into storage indexed by an (xml) ID.
124 *
125 * @param id the ID string of the xml element associated with the bean
126 * @param bean the Object to store, not null
127 */
128 public void putBean(String id, Object bean) {
129 getIdMappingStrategy().setReference(this, bean, id);
130 }
131
132 /***
133 * Gets a bean from storage by an (xml) ID.
134 *
135 * @param id the ID string of the xml element associated with the bean
136 * @return the Object that the ID references, otherwise null
137 */
138 public Object getBean(String id) {
139 return getIdMappingStrategy().getReferenced(this, id);
140 }
141
142 /***
143 * Clears the beans indexed by id.
144 */
145 public void clearBeans() {
146 getIdMappingStrategy().reset();
147 }
148
149 /***
150 * Gets the classloader to be used.
151 * @return the classloader that should be used to load all classes, possibly null
152 */
153 public ClassLoader getClassLoader() {
154 return classLoader;
155 }
156
157 /***
158 * Sets the classloader to be used.
159 * @param classLoader the ClassLoader to be used, possibly null
160 */
161 public void setClassLoader(ClassLoader classLoader) {
162 this.classLoader = classLoader;
163 }
164
165 /***
166 * Gets the <code>BeanCreationChange</code> to be used to create beans
167 * when an element is mapped.
168 * @return the BeanCreationChain not null
169 */
170 public BeanCreationChain getBeanCreationChain() {
171 return readConfiguration.getBeanCreationChain();
172 }
173
174 /***
175 * Gets the strategy used to define default mappings actions
176 * for elements.
177 * @return <code>ActionMappingStrategy</code>. not null
178 */
179 public ActionMappingStrategy getActionMappingStrategy() {
180 return readConfiguration.getActionMappingStrategy();
181 }
182
183 /***
184 * Pops the top element from the element mapping stack.
185 * Also removes any mapped class marks below the top element.
186 *
187 * @return the name of the element popped
188 * if there are any more elements on the stack, otherwise null.
189 * This is the local name if the parser is namespace aware, otherwise the name
190 */
191 public String popElement() {
192
193
194 if (!descriptorStack.isEmpty()) {
195 descriptorStack.pop();
196 }
197
198 if (!updaterStack.isEmpty()) {
199 updaterStack.pop();
200 }
201
202 popOptions();
203
204 Object top = null;
205 if (!elementMappingStack.isEmpty()) {
206 top = elementMappingStack.pop();
207 if (top != null) {
208 if (!(top instanceof String)) {
209 return popElement();
210 }
211 }
212 }
213
214 return (String) top;
215 }
216
217 /***
218 * Gets the element name for the currently mapped element.
219 * @return the name of the currently mapped element,
220 * or null if there has been no element mapped
221 */
222 public String getCurrentElement() {
223 String result = null;
224 int stackSize = elementMappingStack.size();
225 int i = 0;
226 while ( i < stackSize ) {
227 Object mappedElement = elementMappingStack.peek(i);
228 if (mappedElement instanceof String) {
229 result = (String) mappedElement;
230 break;
231 }
232 ++i;
233 }
234 return result;
235 }
236
237 /***
238 * Gets the Class that was last mapped, if there is one.
239 *
240 * @return the Class last marked as mapped
241 * or null if no class has been mapped
242 */
243 public Class getLastMappedClass() {
244 Class lastMapped = null;
245 for (int i = 0, size = elementMappingStack.size();
246 i < size;
247 i++) {
248 Object entry = elementMappingStack.peek(i);
249 if (entry instanceof Class) {
250 lastMapped = (Class) entry;
251 break;
252 }
253 }
254 return lastMapped;
255 }
256
257 private ElementDescriptor getParentDescriptor() throws IntrospectionException {
258 ElementDescriptor result = null;
259 if (descriptorStack.size() > 1) {
260 result = (ElementDescriptor) descriptorStack.peek(1);
261 }
262 return result;
263 }
264
265
266 /***
267 * Pushes the given element onto the element mapping stack.
268 *
269 * @param elementName the local name if the parser is namespace aware,
270 * otherwise the full element name. Not null
271 */
272 public void pushElement(String elementName) throws Exception {
273
274 elementMappingStack.push(elementName);
275
276
277 ElementDescriptor nextDescriptor = null;
278 if (elementMappingStack.size() == 1 && rootClass != null) {
279 markClassMap(rootClass);
280 XMLBeanInfo rootClassInfo
281 = getXMLIntrospector().introspect(rootClass);
282 nextDescriptor = rootClassInfo.getElementDescriptor();
283 } else {
284 ElementDescriptor currentDescriptor = getCurrentDescriptor();
285 if (currentDescriptor != null) {
286 nextDescriptor = currentDescriptor.getElementDescriptor(elementName);
287 }
288 }
289 Updater updater = null;
290 Options options = null;
291 if (nextDescriptor != null) {
292 updater = nextDescriptor.getUpdater();
293 options = nextDescriptor.getOptions();
294 }
295 updaterStack.push(updater);
296 descriptorStack.push(nextDescriptor);
297 pushOptions(options);
298 }
299
300 /***
301 * Marks the element name stack with a class mapping.
302 * Relative paths and last mapped class are calculated using these marks.
303 *
304 * @param mappedClazz the Class which has been mapped at the current path, not null
305 */
306 public void markClassMap(Class mappedClazz) throws IntrospectionException {
307 if (mappedClazz.isArray()) {
308 mappedClazz = mappedClazz.getComponentType();
309 }
310 elementMappingStack.push(mappedClazz);
311
312 XMLBeanInfo mappedClassInfo = getXMLIntrospector().introspect(mappedClazz);
313 ElementDescriptor mappedElementDescriptor = mappedClassInfo.getElementDescriptor();
314 descriptorStack.push(mappedElementDescriptor);
315
316 Updater updater = mappedElementDescriptor.getUpdater();
317 updaterStack.push(updater);
318 }
319
320 /***
321 * Pops an action mapping from the stack
322 * @return <code>MappingAction</code>, not null
323 */
324 public MappingAction popMappingAction() {
325 return (MappingAction) actionMappingStack.pop();
326 }
327
328 /***
329 * Pushs an action mapping onto the stack
330 * @param mappingAction
331 */
332 public void pushMappingAction(MappingAction mappingAction) {
333 actionMappingStack.push(mappingAction);
334 }
335
336 /***
337 * Gets the current mapping action
338 * @return MappingAction
339 */
340 public MappingAction currentMappingAction() {
341 if (actionMappingStack.size() == 0)
342 {
343 return null;
344 }
345 return (MappingAction) actionMappingStack.peek();
346 }
347
348 public Object getBean() {
349 return objectStack.peek();
350 }
351
352 public void setBean(Object bean) {
353
354
355
356 }
357
358 /***
359 * Pops the last mapping <code>Object</code> from the
360 * stack containing beans that have been mapped.
361 * @return the last bean pushed onto the stack
362 */
363 public Object popBean() {
364 return objectStack.pop();
365 }
366
367 /***
368 * Pushs a newly mapped <code>Object</code> onto the mapped bean stack.
369 * @param bean
370 */
371 public void pushBean(Object bean) {
372 objectStack.push(bean);
373 }
374
375 /***
376 * Gets the <code>XMLIntrospector</code> to be used to create
377 * the mappings for the xml.
378 * @return <code>XMLIntrospector</code>, not null
379 */
380 public XMLIntrospector getXMLIntrospector() {
381
382
383 if (xmlIntrospector == null) {
384 xmlIntrospector = new XMLIntrospector();
385 }
386 return xmlIntrospector;
387 }
388
389 /***
390 * Sets the <code>XMLIntrospector</code> to be used to create
391 * the mappings for the xml.
392 * @param xmlIntrospector <code>XMLIntrospector</code>, not null
393 */
394 public void setXMLIntrospector(XMLIntrospector xmlIntrospector) {
395 this.xmlIntrospector = xmlIntrospector;
396 }
397
398 public Class getRootClass() {
399 return rootClass;
400 }
401
402 public void setRootClass(Class rootClass) {
403 this.rootClass = rootClass;
404 }
405
406 /***
407 * Gets the <code>ElementDescriptor</code> that describes the
408 * mapping for the current element.
409 * @return <code>ElementDescriptor</code> or null if there is no
410 * current mapping
411 * @throws Exception
412 */
413 public ElementDescriptor getCurrentDescriptor() throws Exception {
414 ElementDescriptor result = null;
415 if (!descriptorStack.empty()) {
416 result = (ElementDescriptor) descriptorStack.peek();
417 }
418 return result;
419 }
420
421 /***
422 * Populates the object mapped by the <code>AttributeDescriptor</code>s
423 * with the values in the given <code>Attributes</code>.
424 * @param attributeDescriptors <code>AttributeDescriptor</code>s, not null
425 * @param attributes <code>Attributes</code>, not null
426 */
427 public void populateAttributes(
428 AttributeDescriptor[] attributeDescriptors,
429 Attributes attributes) {
430
431 Log log = getLog();
432 if (attributeDescriptors != null) {
433 for (int i = 0, size = attributeDescriptors.length;
434 i < size;
435 i++) {
436 AttributeDescriptor attributeDescriptor =
437 attributeDescriptors[i];
438
439
440
441
442
443 String value =
444 attributes.getValue(
445 attributeDescriptor.getURI(),
446 attributeDescriptor.getLocalName());
447
448 if (value == null) {
449 value =
450 attributes.getValue(
451 attributeDescriptor.getQualifiedName());
452 }
453
454 if (log.isTraceEnabled()) {
455 log.trace("Attr URL:" + attributeDescriptor.getURI());
456 log.trace(
457 "Attr LocalName:" + attributeDescriptor.getLocalName());
458 log.trace(value);
459 }
460
461 Updater updater = attributeDescriptor.getUpdater();
462 log.trace(updater);
463 if (updater != null && value != null) {
464 updater.update(this, value);
465 }
466 }
467 }
468 }
469
470 /***
471 * <p>Pushes an <code>Updater</code> onto the stack.</p>
472 * <p>
473 * <strong>Note</strong>Any action pushing an <code>Updater</code> onto
474 * the stack should take responsibility for popping
475 * the updater from the stack at an appropriate time.
476 * </p>
477 * <p>
478 * <strong>Usage:</strong> this may be used by actions
479 * which require a temporary object to be updated.
480 * Pushing an updater onto the stack allow actions
481 * downstream to transparently update the temporary proxy.
482 * </p>
483 * @param updater Updater, possibly null
484 */
485 public void pushUpdater(Updater updater) {
486 updaterStack.push(updater);
487 }
488
489 /***
490 * Pops the top <code>Updater</code> from the stack.
491 * <p>
492 * <strong>Note</strong>Any action pushing an <code>Updater</code> onto
493 * the stack should take responsibility for popping
494 * the updater from the stack at an appropriate time.
495 * </p>
496 * @return <code>Updater</code>, possibly null
497 */
498 public Updater popUpdater() {
499 return (Updater) updaterStack.pop();
500 }
501
502 /***
503 * Gets the current <code>Updater</code>.
504 * This may (or may not) be the updater for the current
505 * descriptor.
506 * If the current descriptor is a bean child,
507 * the the current updater will (most likely)
508 * be the updater for the property.
509 * Actions (that, for example, use proxy objects)
510 * may push updaters onto the stack.
511 * @return Updater, possibly null
512 */
513 public Updater getCurrentUpdater() {
514
515
516
517
518
519 Updater result = null;
520 if (!updaterStack.empty()) {
521 result = (Updater) updaterStack.peek();
522 if ( result == null && updaterStack.size() >1 ) {
523 result = (Updater) updaterStack.peek(1);
524 }
525 }
526 return result;
527 }
528
529
530
531 }