Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||||||
BeanCreateRule |
|
| 3.260869565217391;3.261 |
1 | 0 | /* |
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; |
|
17 | ||
18 | import java.util.HashMap; |
|
19 | import java.util.List; |
|
20 | import java.util.Map; |
|
21 | ||
22 | import org.apache.commons.betwixt.AttributeDescriptor; |
|
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.digester.XMLIntrospectorHelper; |
|
27 | import org.apache.commons.betwixt.expression.Context; |
|
28 | import org.apache.commons.betwixt.expression.MethodUpdater; |
|
29 | import org.apache.commons.betwixt.expression.Updater; |
|
30 | import org.apache.commons.digester.Rule; |
|
31 | import org.apache.commons.digester.Rules; |
|
32 | import org.apache.commons.logging.Log; |
|
33 | import org.apache.commons.logging.LogFactory; |
|
34 | import org.xml.sax.Attributes; |
|
35 | ||
36 | /** <p><code>BeanCreateRule</code> is a Digester Rule for creating beans |
|
37 | * from the betwixt XML metadata.</p> |
|
38 | * |
|
39 | * @author <a href="mailto:jstrachan@apache.org">James Strachan</a> |
|
40 | * @author <a href="mailto:martin@mvdb.net">Martin van den Bemt</a> |
|
41 | * @deprecated 0.5 this Rule does not allowed good integration with other Rules - |
|
42 | * use {@link BeanRuleSet} instead. |
|
43 | */ |
|
44 | 0 | public class BeanCreateRule extends Rule { |
45 | ||
46 | /** Logger */ |
|
47 | 0 | private static Log log = LogFactory.getLog( BeanCreateRule.class ); |
48 | ||
49 | /** |
|
50 | * Set log to be used by <code>BeanCreateRule</code> instances |
|
51 | * @param aLog the <code>Log</code> implementation for this class to log to |
|
52 | */ |
|
53 | public static void setLog(Log aLog) { |
|
54 | 0 | log = aLog; |
55 | 0 | } |
56 | ||
57 | /** The descriptor of this element */ |
|
58 | private ElementDescriptor descriptor; |
|
59 | /** The Context used when evaluating Updaters */ |
|
60 | 0 | private Context context; |
61 | /** Have we added our child rules to the digester? */ |
|
62 | private boolean addedChildren; |
|
63 | /** In this begin-end loop did we actually create a new bean */ |
|
64 | private boolean createdBean; |
|
65 | /** The type of the bean to create */ |
|
66 | private Class beanClass; |
|
67 | /** The prefix added to digester rules */ |
|
68 | private String pathPrefix; |
|
69 | /** Use id's to match beans? */ |
|
70 | 0 | private boolean matchIDs = true; |
71 | /** allows an attribute to be specified to overload the types of beans used */ |
|
72 | 0 | private String classNameAttribute = "className"; |
73 | ||
74 | /** |
|
75 | * Convenience constructor which uses <code>ID's</code> for matching. |
|
76 | * |
|
77 | * @param descriptor the <code>ElementDescriptor</code> describing the element mapped |
|
78 | * @param beanClass the <code>Class</code> to be created |
|
79 | * @param pathPrefix the digester style path |
|
80 | */ |
|
81 | public BeanCreateRule( |
|
82 | ElementDescriptor descriptor, |
|
83 | Class beanClass, |
|
84 | String pathPrefix ) { |
|
85 | 0 | this( descriptor, beanClass, pathPrefix, true ); |
86 | 0 | } |
87 | ||
88 | /** |
|
89 | * Constructor taking a class. |
|
90 | * |
|
91 | * @param descriptor the <code>ElementDescriptor</code> describing the element mapped |
|
92 | * @param beanClass the <code>Class</code> to be created |
|
93 | * @param pathPrefix the digester style path |
|
94 | * @param matchIDs should <code>ID</code>/<code>IDREF</code>'s be used for matching |
|
95 | */ |
|
96 | public BeanCreateRule( |
|
97 | ElementDescriptor descriptor, |
|
98 | Class beanClass, |
|
99 | String pathPrefix, |
|
100 | boolean matchIDs ) { |
|
101 | 0 | this( |
102 | 0 | descriptor, |
103 | 0 | beanClass, |
104 | 0 | new Context(), |
105 | 0 | pathPrefix, |
106 | 0 | matchIDs); |
107 | 0 | } |
108 | ||
109 | /** |
|
110 | * Convenience constructor which uses <code>ID's</code> for matching. |
|
111 | * |
|
112 | * @param descriptor the <code>ElementDescriptor</code> describing the element mapped |
|
113 | * @param beanClass the <code>Class</code> to be created |
|
114 | */ |
|
115 | public BeanCreateRule( ElementDescriptor descriptor, Class beanClass ) { |
|
116 | 0 | this( descriptor, beanClass, true ); |
117 | 0 | } |
118 | ||
119 | /** |
|
120 | * Constructor uses standard qualified name. |
|
121 | * |
|
122 | * @param descriptor the <code>ElementDescriptor</code> describing the element mapped |
|
123 | * @param beanClass the <code>Class</code> to be created |
|
124 | * @param matchIDs should <code>ID</code>/<code>IDREF</code>'s be used for matching |
|
125 | */ |
|
126 | public BeanCreateRule( ElementDescriptor descriptor, Class beanClass, boolean matchIDs ) { |
|
127 | 0 | this( descriptor, beanClass, descriptor.getQualifiedName() + "/" , matchIDs ); |
128 | 0 | } |
129 | ||
130 | /** |
|
131 | * Convenience constructor which uses <code>ID's</code> for match. |
|
132 | * |
|
133 | * @param descriptor the <code>ElementDescriptor</code> describing the element mapped |
|
134 | * @param context the <code>Context</code> to be used to evaluate expressions |
|
135 | * @param pathPrefix the digester path prefix |
|
136 | */ |
|
137 | public BeanCreateRule( |
|
138 | ElementDescriptor descriptor, |
|
139 | Context context, |
|
140 | String pathPrefix ) { |
|
141 | 0 | this( descriptor, context, pathPrefix, true ); |
142 | 0 | } |
143 | ||
144 | /** |
|
145 | * Constructor taking a context. |
|
146 | * |
|
147 | * @param descriptor the <code>ElementDescriptor</code> describing the element mapped |
|
148 | * @param context the <code>Context</code> to be used to evaluate expressions |
|
149 | * @param pathPrefix the digester path prefix |
|
150 | * @param matchIDs should <code>ID</code>/<code>IDREF</code>'s be used for matching |
|
151 | */ |
|
152 | public BeanCreateRule( |
|
153 | ElementDescriptor descriptor, |
|
154 | Context context, |
|
155 | String pathPrefix, |
|
156 | boolean matchIDs ) { |
|
157 | 0 | this( |
158 | 0 | descriptor, |
159 | 0 | descriptor.getSingularPropertyType(), |
160 | 0 | context, |
161 | 0 | pathPrefix, |
162 | 0 | matchIDs ); |
163 | 0 | } |
164 | ||
165 | /** |
|
166 | * Base constructor (used by other constructors). |
|
167 | * |
|
168 | * @param descriptor the <code>ElementDescriptor</code> describing the element mapped |
|
169 | * @param beanClass the <code>Class</code> of the bean to be created |
|
170 | * @param context the <code>Context</code> to be used to evaluate expressions |
|
171 | * @param pathPrefix the digester path prefix |
|
172 | * @param matchIDs should <code>ID</code>/<code>IDREF</code>'s be used for matching |
|
173 | */ |
|
174 | 0 | private BeanCreateRule( |
175 | ElementDescriptor descriptor, |
|
176 | Class beanClass, |
|
177 | Context context, |
|
178 | String pathPrefix, |
|
179 | boolean matchIDs ) { |
|
180 | 0 | this.descriptor = descriptor; |
181 | 0 | this.context = context; |
182 | 0 | this.beanClass = beanClass; |
183 | 0 | this.pathPrefix = pathPrefix; |
184 | 0 | this.matchIDs = matchIDs; |
185 | 0 | if (log.isTraceEnabled()) { |
186 | 0 | log.trace("Created bean create rule"); |
187 | 0 | log.trace("Descriptor=" + descriptor); |
188 | 0 | log.trace("Class=" + beanClass); |
189 | 0 | log.trace("Path prefix=" + pathPrefix); |
190 | } |
|
191 | 0 | } |
192 | ||
193 | ||
194 | ||
195 | // Rule interface |
|
196 | //------------------------------------------------------------------------- |
|
197 | ||
198 | /** |
|
199 | * Process the beginning of this element. |
|
200 | * |
|
201 | * @param attributes The attribute list of this element |
|
202 | */ |
|
203 | public void begin(Attributes attributes) { |
|
204 | 0 | log.debug( "Called with descriptor: " + descriptor |
205 | 0 | + " propertyType: " + descriptor.getPropertyType() ); |
206 | ||
207 | 0 | if (log.isTraceEnabled()) { |
208 | 0 | int attributesLength = attributes.getLength(); |
209 | 0 | if (attributesLength > 0) { |
210 | 0 | log.trace("Attributes:"); |
211 | } |
|
212 | 0 | for (int i=0, size=attributesLength; i<size; i++) { |
213 | 0 | log.trace("Local:" + attributes.getLocalName(i)); |
214 | 0 | log.trace("URI:" + attributes.getURI(i)); |
215 | 0 | log.trace("QName:" + attributes.getQName(i)); |
216 | } |
|
217 | } |
|
218 | ||
219 | ||
220 | ||
221 | // XXX: if a single rule instance gets reused and nesting occurs |
|
222 | // XXX: we should probably use a stack of booleans to test if we created a bean |
|
223 | // XXX: or let digester take nulls, which would be easier for us ;-) |
|
224 | 0 | createdBean = false; |
225 | ||
226 | 0 | Object instance = null; |
227 | 0 | if ( beanClass != null ) { |
228 | 0 | instance = createBean(attributes); |
229 | 0 | if ( instance != null ) { |
230 | 0 | createdBean = true; |
231 | ||
232 | 0 | context.setBean( instance ); |
233 | 0 | digester.push(instance); |
234 | ||
235 | ||
236 | // if we are a reference to a type we should lookup the original |
|
237 | // as this ElementDescriptor will be 'hollow' and have no child attributes/elements. |
|
238 | // XXX: this should probably be done by the NodeDescriptors... |
|
239 | 0 | ElementDescriptor typeDescriptor = getElementDescriptor( descriptor ); |
240 | //ElementDescriptor typeDescriptor = descriptor; |
|
241 | ||
242 | // iterate through all attributes |
|
243 | 0 | AttributeDescriptor[] attributeDescriptors |
244 | 0 | = typeDescriptor.getAttributeDescriptors(); |
245 | 0 | if ( attributeDescriptors != null ) { |
246 | 0 | for ( int i = 0, size = attributeDescriptors.length; i < size; i++ ) { |
247 | 0 | AttributeDescriptor attributeDescriptor = attributeDescriptors[i]; |
248 | ||
249 | // The following isn't really the right way to find the attribute |
|
250 | // but it's quite robust. |
|
251 | // The idea is that you try both namespace and local name first |
|
252 | // and if this returns null try the qName. |
|
253 | 0 | String value = attributes.getValue( |
254 | 0 | attributeDescriptor.getURI(), |
255 | 0 | attributeDescriptor.getLocalName() |
256 | ); |
|
257 | ||
258 | 0 | if (value == null) { |
259 | 0 | value = attributes.getValue(attributeDescriptor.getQualifiedName()); |
260 | } |
|
261 | ||
262 | 0 | if (log.isTraceEnabled()) { |
263 | 0 | log.trace("Attr URL:" + attributeDescriptor.getURI()); |
264 | 0 | log.trace("Attr LocalName:" + attributeDescriptor.getLocalName() ); |
265 | 0 | log.trace(value); |
266 | } |
|
267 | ||
268 | 0 | Updater updater = attributeDescriptor.getUpdater(); |
269 | 0 | log.trace(updater); |
270 | 0 | if ( updater != null && value != null ) { |
271 | 0 | updater.update( context, value ); |
272 | } |
|
273 | } |
|
274 | } |
|
275 | ||
276 | 0 | addChildRules(); |
277 | ||
278 | // add bean for ID matching |
|
279 | 0 | if ( matchIDs ) { |
280 | // XXX need to support custom ID attribute names |
|
281 | // XXX i have a feeling that the current mechanism might need to change |
|
282 | // XXX so i'm leaving this till later |
|
283 | 0 | String id = attributes.getValue( "id" ); |
284 | 0 | if ( id != null ) { |
285 | 0 | getBeansById().put( id, instance ); |
286 | } |
|
287 | } |
|
288 | } |
|
289 | } |
|
290 | 0 | } |
291 | ||
292 | /** |
|
293 | * Process the end of this element. |
|
294 | */ |
|
295 | public void end() { |
|
296 | 0 | if ( createdBean ) { |
297 | ||
298 | // force any setters of the parent bean to be called for this new bean instance |
|
299 | 0 | Updater updater = descriptor.getUpdater(); |
300 | 0 | Object instance = context.getBean(); |
301 | ||
302 | 0 | Object top = digester.pop(); |
303 | 0 | if (digester.getCount() == 0) { |
304 | 0 | context.setBean(null); |
305 | }else{ |
|
306 | 0 | context.setBean( digester.peek() ); |
307 | } |
|
308 | ||
309 | 0 | if ( updater != null ) { |
310 | 0 | if ( log.isDebugEnabled() ) { |
311 | 0 | log.debug( "Calling updater for: " + descriptor + " with: " |
312 | 0 | + instance + " on bean: " + context.getBean() ); |
313 | } |
|
314 | 0 | updater.update( context, instance ); |
315 | } else { |
|
316 | 0 | if ( log.isDebugEnabled() ) { |
317 | 0 | log.debug( "No updater for: " + descriptor + " with: " |
318 | 0 | + instance + " on bean: " + context.getBean() ); |
319 | } |
|
320 | } |
|
321 | } |
|
322 | 0 | } |
323 | ||
324 | /** |
|
325 | * Tidy up. |
|
326 | */ |
|
327 | 0 | public void finish() {} |
328 | ||
329 | ||
330 | // Properties |
|
331 | //------------------------------------------------------------------------- |
|
332 | ||
333 | ||
334 | /** |
|
335 | * The name of the attribute which can be specified in the XML to override the |
|
336 | * type of a bean used at a certain point in the schema. |
|
337 | * |
|
338 | * <p>The default value is 'className'.</p> |
|
339 | * |
|
340 | * @return The name of the attribute used to overload the class name of a bean |
|
341 | */ |
|
342 | public String getClassNameAttribute() { |
|
343 | 0 | return classNameAttribute; |
344 | } |
|
345 | ||
346 | /** |
|
347 | * Sets the name of the attribute which can be specified in |
|
348 | * the XML to override the type of a bean used at a certain |
|
349 | * point in the schema. |
|
350 | * |
|
351 | * <p>The default value is 'className'.</p> |
|
352 | * |
|
353 | * @param classNameAttribute The name of the attribute used to overload the class name of a bean |
|
354 | */ |
|
355 | public void setClassNameAttribute(String classNameAttribute) { |
|
356 | 0 | this.classNameAttribute = classNameAttribute; |
357 | 0 | } |
358 | ||
359 | // Implementation methods |
|
360 | //------------------------------------------------------------------------- |
|
361 | ||
362 | /** |
|
363 | * Factory method to create new bean instances |
|
364 | * |
|
365 | * @param attributes the <code>Attributes</code> used to match <code>ID/IDREF</code> |
|
366 | * @return the created bean |
|
367 | */ |
|
368 | protected Object createBean(Attributes attributes) { |
|
369 | // |
|
370 | // See if we've got an IDREF |
|
371 | // |
|
372 | // XXX This should be customizable but i'm not really convinced by the existing system |
|
373 | // XXX maybe it's going to have to change so i'll use 'idref' for nows |
|
374 | // |
|
375 | 0 | if ( matchIDs ) { |
376 | 0 | String idref = attributes.getValue( "idref" ); |
377 | 0 | if ( idref != null ) { |
378 | // XXX need to check up about ordering |
|
379 | // XXX this is a very simple system that assumes that id occurs before idrefs |
|
380 | // XXX would need some thought about how to implement a fuller system |
|
381 | 0 | log.trace( "Found IDREF" ); |
382 | 0 | Object bean = getBeansById().get( idref ); |
383 | 0 | if ( bean != null ) { |
384 | 0 | if (log.isTraceEnabled()) { |
385 | 0 | log.trace( "Matched bean " + bean ); |
386 | } |
|
387 | 0 | return bean; |
388 | } |
|
389 | 0 | log.trace( "No match found" ); |
390 | } |
|
391 | } |
|
392 | ||
393 | 0 | Class theClass = beanClass; |
394 | try { |
|
395 | ||
396 | 0 | String className = attributes.getValue(classNameAttribute); |
397 | 0 | if (className != null) { |
398 | // load the class we should instantiate |
|
399 | 0 | theClass = getDigester().getClassLoader().loadClass(className); |
400 | } |
|
401 | 0 | if (log.isTraceEnabled()) { |
402 | 0 | log.trace( "Creating instance of " + theClass ); |
403 | } |
|
404 | 0 | return theClass.newInstance(); |
405 | ||
406 | 0 | } catch (Exception e) { |
407 | 0 | log.warn( "Could not create instance of type: " + theClass.getName() ); |
408 | 0 | return null; |
409 | } |
|
410 | } |
|
411 | ||
412 | /** Adds the rules to the digester for all child elements */ |
|
413 | protected void addChildRules() { |
|
414 | 0 | if ( ! addedChildren ) { |
415 | 0 | addedChildren = true; |
416 | ||
417 | 0 | addChildRules( pathPrefix, descriptor ); |
418 | } |
|
419 | 0 | } |
420 | ||
421 | /** |
|
422 | * Add child rules for given descriptor at given prefix |
|
423 | * |
|
424 | * @param prefix add child rules at this (digester) path prefix |
|
425 | * @param currentDescriptor add child rules for this descriptor |
|
426 | */ |
|
427 | protected void addChildRules(String prefix, ElementDescriptor currentDescriptor ) { |
|
428 | ||
429 | 0 | if (log.isTraceEnabled()) { |
430 | 0 | log.trace("Adding child rules for " + currentDescriptor + "@" + prefix); |
431 | } |
|
432 | ||
433 | // if we are a reference to a type we should lookup the original |
|
434 | // as this ElementDescriptor will be 'hollow' and have no child attributes/elements. |
|
435 | // XXX: this should probably be done by the NodeDescriptors... |
|
436 | 0 | ElementDescriptor typeDescriptor = getElementDescriptor( currentDescriptor ); |
437 | //ElementDescriptor typeDescriptor = descriptor; |
|
438 | ||
439 | ||
440 | 0 | ElementDescriptor[] childDescriptors = typeDescriptor.getElementDescriptors(); |
441 | 0 | if ( childDescriptors != null ) { |
442 | 0 | for ( int i = 0, size = childDescriptors.length; i < size; i++ ) { |
443 | 0 | final ElementDescriptor childDescriptor = childDescriptors[i]; |
444 | 0 | if (log.isTraceEnabled()) { |
445 | 0 | log.trace("Processing child " + childDescriptor); |
446 | } |
|
447 | ||
448 | 0 | String qualifiedName = childDescriptor.getQualifiedName(); |
449 | 0 | if ( qualifiedName == null ) { |
450 | 0 | log.trace( "Ignoring" ); |
451 | 0 | continue; |
452 | } |
|
453 | 0 | String path = prefix + qualifiedName; |
454 | // this code is for making sure that recursive elements |
|
455 | // can also be used.. |
|
456 | ||
457 | 0 | if ( qualifiedName.equals( currentDescriptor.getQualifiedName() ) |
458 | 0 | && currentDescriptor.getPropertyName() != null ) { |
459 | 0 | log.trace("Creating generic rule for recursive elements"); |
460 | 0 | int index = -1; |
461 | 0 | if (childDescriptor.isWrapCollectionsInElement()) { |
462 | 0 | index = prefix.indexOf(qualifiedName); |
463 | 0 | if (index == -1) { |
464 | // shouldn't happen.. |
|
465 | 0 | log.debug( "Oops - this shouldn't happen" ); |
466 | 0 | continue; |
467 | } |
|
468 | 0 | int removeSlash = prefix.endsWith("/")?1:0; |
469 | 0 | path = "*/" + prefix.substring(index, prefix.length()-removeSlash); |
470 | }else{ |
|
471 | // we have a element/element type of thing.. |
|
472 | 0 | ElementDescriptor[] desc = currentDescriptor.getElementDescriptors(); |
473 | 0 | if (desc.length == 1) { |
474 | 0 | path = "*/"+desc[0].getQualifiedName(); |
475 | } |
|
476 | } |
|
477 | 0 | Rule rule = new BeanCreateRule( childDescriptor, context, path, matchIDs); |
478 | 0 | addRule(path, rule); |
479 | 0 | continue; |
480 | } |
|
481 | 0 | if ( childDescriptor.getUpdater() != null ) { |
482 | 0 | if (log.isTraceEnabled()) { |
483 | 0 | log.trace("Element has updater " |
484 | 0 | + ((MethodUpdater) childDescriptor.getUpdater()).getMethod().getName()); |
485 | } |
|
486 | 0 | if ( childDescriptor.isPrimitiveType() ) { |
487 | 0 | addPrimitiveTypeRule(path, childDescriptor); |
488 | ||
489 | } else { |
|
490 | // add the first child to the path |
|
491 | 0 | ElementDescriptor[] grandChildren = childDescriptor.getElementDescriptors(); |
492 | 0 | if ( grandChildren != null && grandChildren.length > 0 ) { |
493 | 0 | ElementDescriptor grandChild = grandChildren[0]; |
494 | 0 | String grandChildQName = grandChild.getQualifiedName(); |
495 | 0 | if ( grandChildQName != null && grandChildQName.length() > 0 ) { |
496 | 0 | if (childDescriptor.isWrapCollectionsInElement()) { |
497 | 0 | path += '/' + grandChildQName; |
498 | ||
499 | } else { |
|
500 | 0 | path = prefix + (prefix.endsWith("/")?"":"/") + grandChildQName; |
501 | } |
|
502 | } |
|
503 | } |
|
504 | ||
505 | // maybe we are adding a primitve type to a collection/array |
|
506 | 0 | Class beanClass = childDescriptor.getSingularPropertyType(); |
507 | 0 | if ( XMLIntrospectorHelper.isPrimitiveType( beanClass ) ) { |
508 | 0 | addPrimitiveTypeRule(path, childDescriptor); |
509 | ||
510 | } else { |
|
511 | 0 | Rule rule = new BeanCreateRule( |
512 | 0 | childDescriptor, |
513 | 0 | context, |
514 | 0 | path + '/', |
515 | 0 | matchIDs ); |
516 | 0 | addRule( path, rule ); |
517 | } |
|
518 | } |
|
519 | } else { |
|
520 | 0 | log.trace("Element does not have updater"); |
521 | } |
|
522 | ||
523 | 0 | ElementDescriptor[] grandChildren = childDescriptor.getElementDescriptors(); |
524 | 0 | if ( grandChildren != null && grandChildren.length > 0 ) { |
525 | 0 | log.trace("Adding grand children"); |
526 | 0 | addChildRules( path + '/', childDescriptor ); |
527 | } |
|
528 | } |
|
529 | } |
|
530 | 0 | } |
531 | ||
532 | /** |
|
533 | * Get the associated bean reader. |
|
534 | * |
|
535 | * @return the <code>BeanReader</code digesting the xml |
|
536 | */ |
|
537 | protected BeanReader getBeanReader() { |
|
538 | // XXX this breaks the rule contact |
|
539 | // XXX maybe the reader should be passed in the constructor |
|
540 | 0 | return (BeanReader) getDigester(); |
541 | } |
|
542 | ||
543 | /** |
|
544 | * Allows the navigation from a reference to a property object to the descriptor defining what |
|
545 | * the property is. In other words, doing the join from a reference to a type to lookup its descriptor. |
|
546 | * This could be done automatically by the NodeDescriptors. Refer to TODO.txt for more info. |
|
547 | * |
|
548 | * @param propertyDescriptor find descriptor for property object referenced by this descriptor |
|
549 | * @return descriptor for the singular property class type referenced. |
|
550 | */ |
|
551 | protected ElementDescriptor getElementDescriptor( ElementDescriptor propertyDescriptor ) { |
|
552 | 0 | Class beanClass = propertyDescriptor.getSingularPropertyType(); |
553 | 0 | if ( beanClass != null ) { |
554 | 0 | XMLIntrospector introspector = getBeanReader().getXMLIntrospector(); |
555 | try { |
|
556 | 0 | XMLBeanInfo xmlInfo = introspector.introspect( beanClass ); |
557 | 0 | return xmlInfo.getElementDescriptor(); |
558 | ||
559 | 0 | } catch (Exception e) { |
560 | 0 | log.warn( "Could not introspect class: " + beanClass, e ); |
561 | } |
|
562 | } |
|
563 | // could not find a better descriptor so use the one we've got |
|
564 | 0 | return propertyDescriptor; |
565 | } |
|
566 | ||
567 | /** |
|
568 | * Adds a new Digester rule to process the text as a primitive type |
|
569 | * |
|
570 | * @param path digester path where this rule will be attached |
|
571 | * @param childDescriptor update this <code>ElementDescriptor</code> with the body text |
|
572 | */ |
|
573 | protected void addPrimitiveTypeRule(String path, final ElementDescriptor childDescriptor) { |
|
574 | 0 | Rule rule = new Rule() { |
575 | public void body(String text) throws Exception { |
|
576 | 0 | childDescriptor.getUpdater().update( context, text ); |
577 | 0 | } |
578 | }; |
|
579 | 0 | addRule( path, rule ); |
580 | 0 | } |
581 | ||
582 | /** |
|
583 | * Safely add a rule with given path. |
|
584 | * |
|
585 | * @param path the digester path to add rule at |
|
586 | * @param rule the <code>Rule</code> to add |
|
587 | */ |
|
588 | protected void addRule(String path, Rule rule) { |
|
589 | 0 | Rules rules = digester.getRules(); |
590 | 0 | List matches = rules.match(null, path); |
591 | 0 | if ( matches.isEmpty() ) { |
592 | 0 | if ( log.isDebugEnabled() ) { |
593 | 0 | log.debug( "Adding digester rule for path: " + path + " rule: " + rule ); |
594 | } |
|
595 | 0 | digester.addRule( path, rule ); |
596 | ||
597 | } else { |
|
598 | 0 | if ( log.isDebugEnabled() ) { |
599 | 0 | log.debug( "Ignoring duplicate digester rule for path: " |
600 | 0 | + path + " rule: " + rule ); |
601 | 0 | log.debug( "New rule (not added): " + rule ); |
602 | 0 | log.debug( "Existing rule:" + matches.get(0) ); |
603 | } |
|
604 | } |
|
605 | 0 | } |
606 | ||
607 | /** |
|
608 | * Get the map used to index beans (previously read in) by id. |
|
609 | * This is stored in the evaluation context. |
|
610 | * |
|
611 | * @return map indexing beans created by id |
|
612 | */ |
|
613 | protected Map getBeansById() { |
|
614 | // |
|
615 | // we need a single index for beans read in by id |
|
616 | // so that we can use them for idref-matching |
|
617 | // store this in the context |
|
618 | // |
|
619 | 0 | Map beansById = (Map) context.getVariable( "beans-index" ); |
620 | 0 | if ( beansById == null ) { |
621 | // lazy creation |
|
622 | 0 | beansById = new HashMap(); |
623 | 0 | context.setVariable( "beans-index", beansById ); |
624 | 0 | log.trace( "Created new index-by-id map" ); |
625 | } |
|
626 | ||
627 | 0 | return beansById; |
628 | } |
|
629 | ||
630 | /** |
|
631 | * Return something meaningful for logging. |
|
632 | * |
|
633 | * @return something useful for logging |
|
634 | */ |
|
635 | public String toString() { |
|
636 | 0 | return "BeanCreateRule [path prefix=" + pathPrefix + " descriptor=" + descriptor + "]"; |
637 | } |
|
638 | ||
639 | } |