Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||||||
ReadContext |
|
| 2.085714285714286;2.086 |
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; |
|
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.registry.PolymorphicReferenceResolver; |
|
29 | import org.apache.commons.betwixt.strategy.ActionMappingStrategy; |
|
30 | import org.apache.commons.collections.ArrayStack; |
|
31 | import org.apache.commons.logging.Log; |
|
32 | import org.apache.commons.logging.LogFactory; |
|
33 | import org.xml.sax.Attributes; |
|
34 | ||
35 | /** |
|
36 | * <p>Extends <code>Context</code> to provide read specific functionality.</p> |
|
37 | * <p> |
|
38 | * Three stacks are used to manage the reading: |
|
39 | * </p> |
|
40 | * <ul> |
|
41 | * <li><strong>Action mapping stack</strong> contains the {@link MappingAction}'s |
|
42 | * used to execute the mapping of the current element and it's ancesters back to the |
|
43 | * document root.</li> |
|
44 | * <li><strong>Result stack</strong> contains the objects which are bound |
|
45 | * to the current element and to each of it's ancester's back to the root</li> |
|
46 | * <li><strong>Element mapping stack</strong> records the names of the element |
|
47 | * and the classes to which they are bound</li> |
|
48 | * </ul> |
|
49 | * @author Robert Burrell Donkina |
|
50 | * @since 0.5 |
|
51 | */ |
|
52 | public class ReadContext extends Context { |
|
53 | ; |
|
54 | /** Classloader to be used to load beans during reading */ |
|
55 | private ClassLoader classLoader; |
|
56 | /** The read specific configuration */ |
|
57 | private ReadConfiguration readConfiguration; |
|
58 | /** Records the element path together with the locations where classes were mapped*/ |
|
59 | 3234 | private ArrayStack elementMappingStack = new ArrayStack(); |
60 | /** Contains actions for each element */ |
|
61 | 3234 | private ArrayStack actionMappingStack = new ArrayStack(); |
62 | /** Stack contains all beans created */ |
|
63 | 3234 | private ArrayStack objectStack = new ArrayStack(); |
64 | /** Stack contains element descriptors */ |
|
65 | 3234 | private ArrayStack descriptorStack = new ArrayStack(); |
66 | /** Stack contains updaters */ |
|
67 | 3234 | private ArrayStack updaterStack = new ArrayStack(); |
68 | ||
69 | private Class rootClass; |
|
70 | /** The <code>XMLIntrospector</code> to be used to map the xml*/ |
|
71 | private XMLIntrospector xmlIntrospector; |
|
72 | ||
73 | /** |
|
74 | * Constructs a <code>ReadContext</code> with the same settings |
|
75 | * as an existing <code>Context</code>. |
|
76 | * @param context not null |
|
77 | * @param readConfiguration not null |
|
78 | */ |
|
79 | public ReadContext(Context context, ReadConfiguration readConfiguration) { |
|
80 | 0 | super(context); |
81 | 0 | this.readConfiguration = readConfiguration; |
82 | 0 | } |
83 | ||
84 | /** |
|
85 | * Constructs a <code>ReadContext</code> with standard log. |
|
86 | * @param bindingConfiguration the dynamic configuration, not null |
|
87 | * @param readConfiguration the extra read configuration not null |
|
88 | */ |
|
89 | public ReadContext( |
|
90 | BindingConfiguration bindingConfiguration, |
|
91 | ReadConfiguration readConfiguration) { |
|
92 | 156 | this( |
93 | 156 | LogFactory.getLog(ReadContext.class), |
94 | 156 | bindingConfiguration, |
95 | 156 | readConfiguration); |
96 | 156 | } |
97 | ||
98 | /** |
|
99 | * Base constructor |
|
100 | * @param log log to this Log |
|
101 | * @param bindingConfiguration the dynamic configuration, not null |
|
102 | * @param readConfiguration the extra read configuration not null |
|
103 | */ |
|
104 | public ReadContext( |
|
105 | Log log, |
|
106 | BindingConfiguration bindingConfiguration, |
|
107 | ReadConfiguration readConfiguration) { |
|
108 | 1695 | super(null, log, bindingConfiguration); |
109 | 1695 | this.readConfiguration = readConfiguration; |
110 | 1695 | } |
111 | ||
112 | /** |
|
113 | * Constructs a <code>ReadContext</code> |
|
114 | * with the same settings as an existing <code>Context</code>. |
|
115 | * @param readContext not null |
|
116 | */ |
|
117 | public ReadContext(ReadContext readContext) { |
|
118 | 1539 | super(readContext); |
119 | 1539 | classLoader = readContext.classLoader; |
120 | 1539 | readConfiguration = readContext.readConfiguration; |
121 | 1539 | } |
122 | ||
123 | /** |
|
124 | * Puts a bean into storage indexed by an (xml) ID. |
|
125 | * |
|
126 | * @param id the ID string of the xml element associated with the bean |
|
127 | * @param bean the Object to store, not null |
|
128 | */ |
|
129 | public void putBean(String id, Object bean) { |
|
130 | 299 | getIdMappingStrategy().setReference(this, bean, id); |
131 | 299 | } |
132 | ||
133 | /** |
|
134 | * Gets a bean from storage by an (xml) ID. |
|
135 | * |
|
136 | * @param id the ID string of the xml element associated with the bean |
|
137 | * @return the Object that the ID references, otherwise null |
|
138 | */ |
|
139 | public Object getBean(String id) { |
|
140 | 39 | return getIdMappingStrategy().getReferenced(this, id); |
141 | } |
|
142 | ||
143 | /** |
|
144 | * Clears the beans indexed by id. |
|
145 | */ |
|
146 | public void clearBeans() { |
|
147 | 1500 | getIdMappingStrategy().reset(); |
148 | 1500 | } |
149 | ||
150 | /** |
|
151 | * Gets the classloader to be used. |
|
152 | * @return the classloader that should be used to load all classes, possibly null |
|
153 | */ |
|
154 | public ClassLoader getClassLoader() { |
|
155 | 1656 | return classLoader; |
156 | } |
|
157 | ||
158 | /** |
|
159 | * Sets the classloader to be used. |
|
160 | * @param classLoader the ClassLoader to be used, possibly null |
|
161 | */ |
|
162 | public void setClassLoader(ClassLoader classLoader) { |
|
163 | 1539 | this.classLoader = classLoader; |
164 | 1539 | } |
165 | ||
166 | /** |
|
167 | * Gets the <code>BeanCreationChange</code> to be used to create beans |
|
168 | * when an element is mapped. |
|
169 | * @return the BeanCreationChain not null |
|
170 | */ |
|
171 | public BeanCreationChain getBeanCreationChain() { |
|
172 | 3663 | return readConfiguration.getBeanCreationChain(); |
173 | } |
|
174 | ||
175 | /** |
|
176 | * Gets the strategy used to define default mappings actions |
|
177 | * for elements. |
|
178 | * @return <code>ActionMappingStrategy</code>. not null |
|
179 | */ |
|
180 | public ActionMappingStrategy getActionMappingStrategy() { |
|
181 | 7786 | return readConfiguration.getActionMappingStrategy(); |
182 | } |
|
183 | ||
184 | /** |
|
185 | * Pops the top element from the element mapping stack. |
|
186 | * Also removes any mapped class marks below the top element. |
|
187 | * |
|
188 | * @return the name of the element popped |
|
189 | * if there are any more elements on the stack, otherwise null. |
|
190 | * This is the local name if the parser is namespace aware, otherwise the name |
|
191 | */ |
|
192 | public String popElement() { |
|
193 | // since the descriptor stack is populated by pushElement, |
|
194 | // need to ensure that it's correct popped by popElement |
|
195 | 13302 | if (!descriptorStack.isEmpty()) { |
196 | 13250 | descriptorStack.pop(); |
197 | } |
|
198 | ||
199 | 13302 | if (!updaterStack.isEmpty()) { |
200 | 13250 | updaterStack.pop(); |
201 | } |
|
202 | ||
203 | 13302 | popOptions(); |
204 | ||
205 | 13302 | Object top = null; |
206 | 13302 | if (!elementMappingStack.isEmpty()) { |
207 | 13250 | top = elementMappingStack.pop(); |
208 | 13250 | if (top != null) { |
209 | 13250 | if (!(top instanceof String)) { |
210 | 4379 | return popElement(); |
211 | } |
|
212 | } |
|
213 | } |
|
214 | ||
215 | 8923 | return (String) 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 getCurrentElement() { |
|
224 | 13 | String result = null; |
225 | 13 | int stackSize = elementMappingStack.size(); |
226 | 13 | int i = 0; |
227 | 39 | while ( i < stackSize ) { |
228 | 26 | Object mappedElement = elementMappingStack.peek(i); |
229 | 26 | if (mappedElement instanceof String) { |
230 | 13 | result = (String) mappedElement; |
231 | 13 | break; |
232 | } |
|
233 | 13 | ++i; |
234 | } |
|
235 | 13 | return result; |
236 | } |
|
237 | ||
238 | /** |
|
239 | * Gets the Class that was last mapped, if there is one. |
|
240 | * |
|
241 | * @return the Class last marked as mapped |
|
242 | * or null if no class has been mapped |
|
243 | */ |
|
244 | public Class getLastMappedClass() { |
|
245 | 52 | Class lastMapped = null; |
246 | 52 | for (int i = 0, size = elementMappingStack.size(); |
247 | 195 | i < size; |
248 | 91 | i++) { |
249 | 130 | Object entry = elementMappingStack.peek(i); |
250 | 130 | if (entry instanceof Class) { |
251 | 39 | lastMapped = (Class) entry; |
252 | 39 | break; |
253 | } |
|
254 | } |
|
255 | 52 | return lastMapped; |
256 | } |
|
257 | ||
258 | private ElementDescriptor getParentDescriptor() throws IntrospectionException { |
|
259 | 330 | ElementDescriptor result = null; |
260 | 330 | if (descriptorStack.size() > 1) { |
261 | 330 | result = (ElementDescriptor) descriptorStack.peek(1); |
262 | } |
|
263 | 330 | return result; |
264 | } |
|
265 | ||
266 | ||
267 | /** |
|
268 | * Pushes the given element onto the element mapping stack. |
|
269 | * |
|
270 | * @param elementName the local name if the parser is namespace aware, |
|
271 | * otherwise the full element name. Not null |
|
272 | */ |
|
273 | public void pushElement(String elementName) throws Exception { |
|
274 | ||
275 | 9235 | elementMappingStack.push(elementName); |
276 | // special case to ensure that root class is appropriately marked |
|
277 | //TODO: is this really necessary? |
|
278 | 9235 | ElementDescriptor nextDescriptor = null; |
279 | 9235 | if (elementMappingStack.size() == 1 && rootClass != null) { |
280 | 942 | markClassMap(rootClass); |
281 | 942 | XMLBeanInfo rootClassInfo |
282 | 942 | = getXMLIntrospector().introspect(rootClass); |
283 | 942 | nextDescriptor = rootClassInfo.getElementDescriptor(); |
284 | } else { |
|
285 | 8293 | ElementDescriptor currentDescriptor = getCurrentDescriptor(); |
286 | 8293 | if (currentDescriptor != null) { |
287 | 8033 | nextDescriptor = currentDescriptor.getElementDescriptor(elementName); |
288 | } |
|
289 | } |
|
290 | 9235 | Updater updater = null; |
291 | 9235 | Options options = null; |
292 | 9235 | if (nextDescriptor != null) { |
293 | 8494 | updater = nextDescriptor.getUpdater(); |
294 | 8494 | options = nextDescriptor.getOptions(); |
295 | } |
|
296 | 9235 | updaterStack.push(updater); |
297 | 9235 | descriptorStack.push(nextDescriptor); |
298 | 9235 | pushOptions(options); |
299 | 9235 | } |
300 | ||
301 | /** |
|
302 | * Marks the element name stack with a class mapping. |
|
303 | * Relative paths and last mapped class are calculated using these marks. |
|
304 | * |
|
305 | * @param mappedClazz the Class which has been mapped at the current path, not null |
|
306 | */ |
|
307 | public void markClassMap(Class mappedClazz) throws IntrospectionException { |
|
308 | 4600 | if (mappedClazz.isArray()) { |
309 | 0 | mappedClazz = mappedClazz.getComponentType(); |
310 | } |
|
311 | 4600 | elementMappingStack.push(mappedClazz); |
312 | ||
313 | 4600 | XMLBeanInfo mappedClassInfo = getXMLIntrospector().introspect(mappedClazz); |
314 | 4600 | ElementDescriptor mappedElementDescriptor = mappedClassInfo.getElementDescriptor(); |
315 | 4600 | descriptorStack.push(mappedElementDescriptor); |
316 | ||
317 | 4600 | Updater updater = mappedElementDescriptor.getUpdater(); |
318 | 4600 | updaterStack.push(updater); |
319 | 4600 | } |
320 | ||
321 | /** |
|
322 | * Pops an action mapping from the stack |
|
323 | * @return <code>MappingAction</code>, not null |
|
324 | */ |
|
325 | public MappingAction popMappingAction() { |
|
326 | 8819 | return (MappingAction) actionMappingStack.pop(); |
327 | } |
|
328 | ||
329 | /** |
|
330 | * Pushs an action mapping onto the stack |
|
331 | * @param mappingAction |
|
332 | */ |
|
333 | public void pushMappingAction(MappingAction mappingAction) { |
|
334 | 8845 | actionMappingStack.push(mappingAction); |
335 | 8845 | } |
336 | ||
337 | /** |
|
338 | * Gets the current mapping action |
|
339 | * @return MappingAction |
|
340 | */ |
|
341 | public MappingAction currentMappingAction() { |
|
342 | 17677 | if (actionMappingStack.size() == 0) |
343 | { |
|
344 | 942 | return null; |
345 | } |
|
346 | 16735 | return (MappingAction) actionMappingStack.peek(); |
347 | } |
|
348 | ||
349 | public Object getBean() { |
|
350 | 11054 | return objectStack.peek(); |
351 | } |
|
352 | ||
353 | public void setBean(Object bean) { |
|
354 | // TODO: maybe need to deprecate the set bean method |
|
355 | // and push into subclass |
|
356 | // for now, do nothing |
|
357 | 13 | } |
358 | ||
359 | /** |
|
360 | * Pops the last mapping <code>Object</code> from the |
|
361 | * stack containing beans that have been mapped. |
|
362 | * @return the last bean pushed onto the stack |
|
363 | */ |
|
364 | public Object popBean() { |
|
365 | 3476 | return objectStack.pop(); |
366 | } |
|
367 | ||
368 | /** |
|
369 | * Pushs a newly mapped <code>Object</code> onto the mapped bean stack. |
|
370 | * @param bean |
|
371 | */ |
|
372 | public void pushBean(Object bean) { |
|
373 | 3502 | objectStack.push(bean); |
374 | 3502 | } |
375 | ||
376 | /** |
|
377 | * Gets the <code>XMLIntrospector</code> to be used to create |
|
378 | * the mappings for the xml. |
|
379 | * @return <code>XMLIntrospector</code>, not null |
|
380 | */ |
|
381 | public XMLIntrospector getXMLIntrospector() { |
|
382 | // read context is not intended to be used by multiple threads |
|
383 | // so no need to worry about lazy creation |
|
384 | 9975 | if (xmlIntrospector == null) { |
385 | 117 | xmlIntrospector = new XMLIntrospector(); |
386 | } |
|
387 | 9975 | return xmlIntrospector; |
388 | } |
|
389 | ||
390 | /** |
|
391 | * Sets the <code>XMLIntrospector</code> to be used to create |
|
392 | * the mappings for the xml. |
|
393 | * @param xmlIntrospector <code>XMLIntrospector</code>, not null |
|
394 | */ |
|
395 | public void setXMLIntrospector(XMLIntrospector xmlIntrospector) { |
|
396 | 1565 | this.xmlIntrospector = xmlIntrospector; |
397 | 1565 | } |
398 | ||
399 | public Class getRootClass() { |
|
400 | 0 | return rootClass; |
401 | } |
|
402 | ||
403 | public void setRootClass(Class rootClass) { |
|
404 | 1539 | this.rootClass = rootClass; |
405 | 1539 | } |
406 | ||
407 | /** |
|
408 | * Gets the <code>ElementDescriptor</code> that describes the |
|
409 | * mapping for the current element. |
|
410 | * @return <code>ElementDescriptor</code> or null if there is no |
|
411 | * current mapping |
|
412 | * @throws Exception |
|
413 | */ |
|
414 | public ElementDescriptor getCurrentDescriptor() throws Exception { |
|
415 | 31777 | ElementDescriptor result = null; |
416 | 31777 | if (!descriptorStack.empty()) { |
417 | 31686 | result = (ElementDescriptor) descriptorStack.peek(); |
418 | } |
|
419 | 31777 | return result; |
420 | } |
|
421 | ||
422 | /** |
|
423 | * Populates the object mapped by the <code>AttributeDescriptor</code>s |
|
424 | * with the values in the given <code>Attributes</code>. |
|
425 | * @param attributeDescriptors <code>AttributeDescriptor</code>s, not null |
|
426 | * @param attributes <code>Attributes</code>, not null |
|
427 | */ |
|
428 | public void populateAttributes( |
|
429 | AttributeDescriptor[] attributeDescriptors, |
|
430 | Attributes attributes) { |
|
431 | ||
432 | 8047 | Log log = getLog(); |
433 | 8047 | if (attributeDescriptors != null) { |
434 | 8047 | for (int i = 0, size = attributeDescriptors.length; |
435 | 17953 | i < size; |
436 | 1859 | i++) { |
437 | 1859 | AttributeDescriptor attributeDescriptor = |
438 | 1859 | attributeDescriptors[i]; |
439 | ||
440 | // The following isn't really the right way to find the attribute |
|
441 | // but it's quite robust. |
|
442 | // The idea is that you try both namespace and local name first |
|
443 | // and if this returns null try the qName. |
|
444 | 1859 | String value = |
445 | 3718 | attributes.getValue( |
446 | 1859 | attributeDescriptor.getURI(), |
447 | 1859 | attributeDescriptor.getLocalName()); |
448 | ||
449 | 1859 | if (value == null) { |
450 | 78 | value = |
451 | 156 | attributes.getValue( |
452 | 78 | attributeDescriptor.getQualifiedName()); |
453 | } |
|
454 | ||
455 | 1859 | if (log.isTraceEnabled()) { |
456 | 0 | log.trace("Attr URL:" + attributeDescriptor.getURI()); |
457 | 0 | log.trace( |
458 | 0 | "Attr LocalName:" + attributeDescriptor.getLocalName()); |
459 | 0 | log.trace(value); |
460 | } |
|
461 | ||
462 | 1859 | Updater updater = attributeDescriptor.getUpdater(); |
463 | 1859 | log.trace(updater); |
464 | 1859 | if (updater != null && value != null) { |
465 | 1729 | updater.update(this, value); |
466 | } |
|
467 | } |
|
468 | } |
|
469 | 8047 | } |
470 | ||
471 | /** |
|
472 | * <p>Pushes an <code>Updater</code> onto the stack.</p> |
|
473 | * <p> |
|
474 | * <strong>Note</strong>Any action pushing an <code>Updater</code> onto |
|
475 | * the stack should take responsibility for popping |
|
476 | * the updater from the stack at an appropriate time. |
|
477 | * </p> |
|
478 | * <p> |
|
479 | * <strong>Usage:</strong> this may be used by actions |
|
480 | * which require a temporary object to be updated. |
|
481 | * Pushing an updater onto the stack allow actions |
|
482 | * downstream to transparently update the temporary proxy. |
|
483 | * </p> |
|
484 | * @param updater Updater, possibly null |
|
485 | */ |
|
486 | public void pushUpdater(Updater updater) { |
|
487 | 65 | updaterStack.push(updater); |
488 | 65 | } |
489 | ||
490 | /** |
|
491 | * Pops the top <code>Updater</code> from the stack. |
|
492 | * <p> |
|
493 | * <strong>Note</strong>Any action pushing an <code>Updater</code> onto |
|
494 | * the stack should take responsibility for popping |
|
495 | * the updater from the stack at an appropriate time. |
|
496 | * </p> |
|
497 | * @return <code>Updater</code>, possibly null |
|
498 | */ |
|
499 | public Updater popUpdater() { |
|
500 | 65 | return (Updater) updaterStack.pop(); |
501 | } |
|
502 | ||
503 | /** |
|
504 | * Gets the current <code>Updater</code>. |
|
505 | * This may (or may not) be the updater for the current |
|
506 | * descriptor. |
|
507 | * If the current descriptor is a bean child, |
|
508 | * the the current updater will (most likely) |
|
509 | * be the updater for the property. |
|
510 | * Actions (that, for example, use proxy objects) |
|
511 | * may push updaters onto the stack. |
|
512 | * @return Updater, possibly null |
|
513 | */ |
|
514 | public Updater getCurrentUpdater() { |
|
515 | // TODO: think about whether this is right |
|
516 | // it makes some sense to look back up the |
|
517 | // stack until a non-empty updater is found. |
|
518 | // actions who need to put a stock to this |
|
519 | // behaviour can always use an ignoring implementation. |
|
520 | 7280 | Updater result = null; |
521 | 7280 | if (!updaterStack.empty()) { |
522 | 7280 | result = (Updater) updaterStack.peek(); |
523 | 7280 | if ( result == null && updaterStack.size() >1 ) { |
524 | 3770 | result = (Updater) updaterStack.peek(1); |
525 | } |
|
526 | } |
|
527 | 7280 | return result; |
528 | } |
|
529 | ||
530 | /** |
|
531 | * Resolves any polymorphism in the element mapping. |
|
532 | * @param mapping <code>ElementMapping</code> describing the mapped element |
|
533 | * @return <code>null</code> if the type cannot be resolved |
|
534 | * or if the current descriptor is not polymorphic |
|
535 | */ |
|
536 | public Class resolvePolymorphicType(ElementMapping mapping) { |
|
537 | 3455 | Class result = null; |
538 | 3455 | Log log = getLog(); |
539 | try { |
|
540 | 3455 | ElementDescriptor currentDescriptor = getCurrentDescriptor(); |
541 | 3455 | if (currentDescriptor != null) { |
542 | 3455 | if (currentDescriptor.isPolymorphic()) { |
543 | 485 | PolymorphicReferenceResolver resolver = getXMLIntrospector().getPolymorphicReferenceResolver(); |
544 | 485 | result = resolver.resolveType(mapping, this); |
545 | 485 | if (result == null) { |
546 | // try the other polymorphic descriptors |
|
547 | 330 | ElementDescriptor parent = getParentDescriptor(); |
548 | 330 | if (parent != null) { |
549 | 330 | ElementDescriptor[] descriptors = parent.getElementDescriptors(); |
550 | 330 | ElementDescriptor originalDescriptor = mapping.getDescriptor(); |
551 | 330 | boolean resolved = false; |
552 | 717 | for (int i=0; i<descriptors.length;i++) { |
553 | 465 | ElementDescriptor descriptor = descriptors[i]; |
554 | 465 | if (descriptor.isPolymorphic()) { |
555 | 447 | mapping.setDescriptor(descriptor); |
556 | 447 | result = resolver.resolveType(mapping, this); |
557 | 447 | if (result != null) { |
558 | 78 | resolved = true; |
559 | 78 | descriptorStack.pop(); |
560 | 78 | popOptions(); |
561 | 78 | descriptorStack.push(descriptor); |
562 | 78 | pushOptions(descriptor.getOptions()); |
563 | 78 | Updater originalUpdater = originalDescriptor.getUpdater(); |
564 | 78 | Updater newUpdater = descriptor.getUpdater(); |
565 | 78 | substituteUpdater(originalUpdater, newUpdater); |
566 | 78 | break; |
567 | } |
|
568 | } |
|
569 | } |
|
570 | 330 | if (resolved) { |
571 | 78 | log.debug("Resolved polymorphic type"); |
572 | } else { |
|
573 | 252 | log.debug("Failed to resolve polymorphic type"); |
574 | 252 | mapping.setDescriptor(originalDescriptor); |
575 | } |
|
576 | } |
|
577 | } |
|
578 | } |
|
579 | } |
|
580 | 0 | } catch (Exception e) { |
581 | 0 | log.info("Failed to resolved polymorphic type"); |
582 | 0 | log.debug(mapping, e); |
583 | } |
|
584 | 3455 | return result; |
585 | } |
|
586 | ||
587 | /** |
|
588 | * Substitutes one updater in the stack for another. |
|
589 | * @param originalUpdater <code>Updater</code> possibly null |
|
590 | * @param newUpdater <code>Updater</code> possibly null |
|
591 | */ |
|
592 | private void substituteUpdater(Updater originalUpdater, Updater newUpdater) { |
|
593 | // recursively pop elements off the stack until the first match is found |
|
594 | // TODO: may need to consider using custom NILL object and match descriptors |
|
595 | 78 | if (!updaterStack.isEmpty()) { |
596 | 78 | Updater updater = (Updater) updaterStack.pop(); |
597 | 78 | if (originalUpdater == null && updater == null) { |
598 | 0 | updaterStack.push(newUpdater); |
599 | 78 | } else if (originalUpdater.equals(updater)) { |
600 | 78 | updaterStack.push(newUpdater); |
601 | } else { |
|
602 | 0 | substituteUpdater(originalUpdater, newUpdater); |
603 | 0 | updaterStack.push(updater); |
604 | } |
|
605 | } |
|
606 | 78 | } |
607 | ||
608 | } |