Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||||||
XMLIntrospectorHelper |
|
| 7.384615384615385;7.385 |
1 | package org.apache.commons.betwixt.digester; |
|
2 | ||
3 | /* |
|
4 | * Copyright 2001-2004 The Apache Software Foundation. |
|
5 | * |
|
6 | * Licensed under the Apache License, Version 2.0 (the "License"); |
|
7 | * you may not use this file except in compliance with the License. |
|
8 | * You may obtain a copy of the License at |
|
9 | * |
|
10 | * http://www.apache.org/licenses/LICENSE-2.0 |
|
11 | * |
|
12 | * Unless required by applicable law or agreed to in writing, software |
|
13 | * distributed under the License is distributed on an "AS IS" BASIS, |
|
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
15 | * See the License for the specific language governing permissions and |
|
16 | * limitations under the License. |
|
17 | */ |
|
18 | ||
19 | import java.beans.IntrospectionException; |
|
20 | import java.beans.Introspector; |
|
21 | import java.beans.PropertyDescriptor; |
|
22 | import java.lang.reflect.Method; |
|
23 | import java.util.Collection; |
|
24 | import java.util.Date; |
|
25 | import java.util.Enumeration; |
|
26 | import java.util.HashMap; |
|
27 | import java.util.Iterator; |
|
28 | import java.util.Map; |
|
29 | ||
30 | import org.apache.commons.betwixt.AttributeDescriptor; |
|
31 | import org.apache.commons.betwixt.ElementDescriptor; |
|
32 | import org.apache.commons.betwixt.NodeDescriptor; |
|
33 | import org.apache.commons.betwixt.XMLIntrospector; |
|
34 | import org.apache.commons.betwixt.expression.IteratorExpression; |
|
35 | import org.apache.commons.betwixt.expression.MapEntryAdder; |
|
36 | import org.apache.commons.betwixt.expression.MethodExpression; |
|
37 | import org.apache.commons.betwixt.expression.MethodUpdater; |
|
38 | import org.apache.commons.betwixt.strategy.PluralStemmer; |
|
39 | import org.apache.commons.logging.Log; |
|
40 | import org.apache.commons.logging.LogFactory; |
|
41 | ||
42 | /** |
|
43 | * <p><code>XMLIntrospectorHelper</code> a helper class for |
|
44 | * common code shared between the digestor and introspector.</p> |
|
45 | * |
|
46 | * TODO this class will be deprecated soon |
|
47 | * need to move the isLoop and isPrimitiveType but probably need to |
|
48 | * think about whether they need replacing with something different. |
|
49 | * @author <a href="mailto:jstrachan@apache.org">James Strachan</a> |
|
50 | * @author <a href="mailto:martin@mvdb.net">Martin van den Bemt</a> |
|
51 | * |
|
52 | * @deprecated |
|
53 | */ |
|
54 | 0 | public class XMLIntrospectorHelper { |
55 | ||
56 | /** Log used for logging (Doh!) */ |
|
57 | 0 | protected static Log log = LogFactory.getLog( XMLIntrospectorHelper.class ); |
58 | ||
59 | /** Base constructor */ |
|
60 | 0 | public XMLIntrospectorHelper() { |
61 | 0 | } |
62 | ||
63 | /** |
|
64 | * <p>Gets the current logging implementation.</p> |
|
65 | * |
|
66 | * @return current log |
|
67 | */ |
|
68 | public static Log getLog() { |
|
69 | 0 | return log; |
70 | } |
|
71 | ||
72 | /** |
|
73 | * <p>Sets the current logging implementation.</p> |
|
74 | * |
|
75 | * @param aLog use this <code>Log</code> |
|
76 | */ |
|
77 | public static void setLog(Log aLog) { |
|
78 | 0 | log = aLog; |
79 | 0 | } |
80 | ||
81 | ||
82 | ||
83 | /** |
|
84 | * Process a property. |
|
85 | * Go through and work out whether it's a loop property, a primitive or a standard. |
|
86 | * The class property is ignored. |
|
87 | * |
|
88 | * @param propertyDescriptor create a <code>NodeDescriptor</code> for this property |
|
89 | * @param useAttributesForPrimitives write primitives as attributes (rather than elements) |
|
90 | * @param introspector use this <code>XMLIntrospector</code> |
|
91 | * @return a correctly configured <code>NodeDescriptor</code> for the property |
|
92 | * @throws IntrospectionException when bean introspection fails |
|
93 | * @deprecated 0.5 this method has been replaced by {@link XMLIntrospector#createDescriptor} |
|
94 | */ |
|
95 | public static NodeDescriptor createDescriptor( |
|
96 | PropertyDescriptor propertyDescriptor, |
|
97 | boolean useAttributesForPrimitives, |
|
98 | XMLIntrospector introspector |
|
99 | ) throws IntrospectionException { |
|
100 | 0 | String name = propertyDescriptor.getName(); |
101 | 0 | Class type = propertyDescriptor.getPropertyType(); |
102 | ||
103 | 0 | if (log.isTraceEnabled()) { |
104 | 0 | log.trace("Creating descriptor for property: name=" |
105 | 0 | + name + " type=" + type); |
106 | } |
|
107 | ||
108 | 0 | NodeDescriptor nodeDescriptor = null; |
109 | 0 | Method readMethod = propertyDescriptor.getReadMethod(); |
110 | 0 | Method writeMethod = propertyDescriptor.getWriteMethod(); |
111 | ||
112 | 0 | if ( readMethod == null ) { |
113 | 0 | if (log.isTraceEnabled()) { |
114 | 0 | log.trace( "No read method for property: name=" |
115 | 0 | + name + " type=" + type); |
116 | } |
|
117 | 0 | return null; |
118 | } |
|
119 | ||
120 | 0 | if ( log.isTraceEnabled() ) { |
121 | 0 | log.trace( "Read method=" + readMethod.getName() ); |
122 | } |
|
123 | ||
124 | // choose response from property type |
|
125 | ||
126 | // XXX: ignore class property ?? |
|
127 | 0 | if ( Class.class.equals( type ) && "class".equals( name ) ) { |
128 | 0 | log.trace( "Ignoring class property" ); |
129 | 0 | return null; |
130 | } |
|
131 | 0 | if ( isPrimitiveType( type ) ) { |
132 | 0 | if (log.isTraceEnabled()) { |
133 | 0 | log.trace( "Primitive type: " + name); |
134 | } |
|
135 | 0 | if ( useAttributesForPrimitives ) { |
136 | 0 | if (log.isTraceEnabled()) { |
137 | 0 | log.trace( "Adding property as attribute: " + name ); |
138 | } |
|
139 | 0 | nodeDescriptor = new AttributeDescriptor(); |
140 | } else { |
|
141 | 0 | if (log.isTraceEnabled()) { |
142 | 0 | log.trace( "Adding property as element: " + name ); |
143 | } |
|
144 | 0 | nodeDescriptor = new ElementDescriptor(true); |
145 | } |
|
146 | 0 | nodeDescriptor.setTextExpression( new MethodExpression( readMethod ) ); |
147 | ||
148 | 0 | if ( writeMethod != null ) { |
149 | 0 | nodeDescriptor.setUpdater( new MethodUpdater( writeMethod ) ); |
150 | } |
|
151 | 0 | } else if ( isLoopType( type ) ) { |
152 | 0 | if (log.isTraceEnabled()) { |
153 | 0 | log.trace("Loop type: " + name); |
154 | 0 | log.trace("Wrap in collections? " + introspector.isWrapCollectionsInElement()); |
155 | } |
|
156 | 0 | ElementDescriptor loopDescriptor = new ElementDescriptor(); |
157 | 0 | loopDescriptor.setContextExpression( |
158 | 0 | new IteratorExpression( new MethodExpression( readMethod ) ) |
159 | ); |
|
160 | 0 | loopDescriptor.setWrapCollectionsInElement( |
161 | 0 | introspector.isWrapCollectionsInElement()); |
162 | // XXX: need to support some kind of 'add' or handle arrays, Lists or indexed properties |
|
163 | //loopDescriptor.setUpdater( new MethodUpdater( writeMethod ) ); |
|
164 | 0 | if ( Map.class.isAssignableFrom( type ) ) { |
165 | 0 | loopDescriptor.setQualifiedName( "entry" ); |
166 | // add elements for reading |
|
167 | 0 | loopDescriptor.addElementDescriptor( new ElementDescriptor( "key" ) ); |
168 | 0 | loopDescriptor.addElementDescriptor( new ElementDescriptor( "value" ) ); |
169 | } |
|
170 | ||
171 | 0 | ElementDescriptor elementDescriptor = new ElementDescriptor(); |
172 | 0 | elementDescriptor.setWrapCollectionsInElement( |
173 | 0 | introspector.isWrapCollectionsInElement()); |
174 | 0 | elementDescriptor.setElementDescriptors( new ElementDescriptor[] { loopDescriptor } ); |
175 | ||
176 | 0 | nodeDescriptor = elementDescriptor; |
177 | } else { |
|
178 | 0 | if (log.isTraceEnabled()) { |
179 | 0 | log.trace( "Standard property: " + name); |
180 | } |
|
181 | 0 | ElementDescriptor elementDescriptor = new ElementDescriptor(); |
182 | 0 | elementDescriptor.setContextExpression( new MethodExpression( readMethod ) ); |
183 | 0 | if ( writeMethod != null ) { |
184 | 0 | elementDescriptor.setUpdater( new MethodUpdater( writeMethod ) ); |
185 | } |
|
186 | ||
187 | 0 | nodeDescriptor = elementDescriptor; |
188 | } |
|
189 | ||
190 | 0 | if (nodeDescriptor instanceof AttributeDescriptor) { |
191 | // we want to use the attributemapper only when it is an attribute.. |
|
192 | 0 | nodeDescriptor.setLocalName( |
193 | 0 | introspector.getAttributeNameMapper().mapTypeToElementName( name ) ); |
194 | } else { |
|
195 | 0 | nodeDescriptor.setLocalName( |
196 | 0 | introspector.getElementNameMapper().mapTypeToElementName( name ) ); |
197 | } |
|
198 | ||
199 | 0 | nodeDescriptor.setPropertyName( propertyDescriptor.getName() ); |
200 | 0 | nodeDescriptor.setPropertyType( type ); |
201 | ||
202 | // XXX: associate more bean information with the descriptor? |
|
203 | //nodeDescriptor.setDisplayName( propertyDescriptor.getDisplayName() ); |
|
204 | //nodeDescriptor.setShortDescription( propertyDescriptor.getShortDescription() ); |
|
205 | ||
206 | 0 | if (log.isTraceEnabled()) { |
207 | 0 | log.trace("Created descriptor:"); |
208 | 0 | log.trace(nodeDescriptor); |
209 | } |
|
210 | 0 | return nodeDescriptor; |
211 | } |
|
212 | ||
213 | /** |
|
214 | * Configure an <code>ElementDescriptor</code> from a <code>PropertyDescriptor</code>. |
|
215 | * This uses default element updater (the write method of the property). |
|
216 | * |
|
217 | * @param elementDescriptor configure this <code>ElementDescriptor</code> |
|
218 | * @param propertyDescriptor configure from this <code>PropertyDescriptor</code> |
|
219 | * @deprecated 0.6 unused |
|
220 | */ |
|
221 | public static void configureProperty( |
|
222 | ElementDescriptor elementDescriptor, |
|
223 | PropertyDescriptor propertyDescriptor ) { |
|
224 | ||
225 | 0 | configureProperty( elementDescriptor, propertyDescriptor, null, null); |
226 | 0 | } |
227 | ||
228 | /** |
|
229 | * Configure an <code>ElementDescriptor</code> from a <code>PropertyDescriptor</code>. |
|
230 | * A custom update method may be set. |
|
231 | * |
|
232 | * @param elementDescriptor configure this <code>ElementDescriptor</code> |
|
233 | * @param propertyDescriptor configure from this <code>PropertyDescriptor</code> |
|
234 | * @param updateMethodName the name of the custom updater method to user. |
|
235 | * If null, then then |
|
236 | * @param beanClass the <code>Class</code> from which the update method should be found. |
|
237 | * This may be null only when <code>updateMethodName</code> is also null. |
|
238 | * @since 0.5 |
|
239 | * @deprecated 0.6 moved into ElementRule |
|
240 | */ |
|
241 | public static void configureProperty( |
|
242 | ElementDescriptor elementDescriptor, |
|
243 | PropertyDescriptor propertyDescriptor, |
|
244 | String updateMethodName, |
|
245 | Class beanClass ) { |
|
246 | ||
247 | 0 | Class type = propertyDescriptor.getPropertyType(); |
248 | 0 | Method readMethod = propertyDescriptor.getReadMethod(); |
249 | 0 | Method writeMethod = propertyDescriptor.getWriteMethod(); |
250 | ||
251 | 0 | elementDescriptor.setLocalName( propertyDescriptor.getName() ); |
252 | 0 | elementDescriptor.setPropertyType( type ); |
253 | ||
254 | // XXX: associate more bean information with the descriptor? |
|
255 | //nodeDescriptor.setDisplayName( propertyDescriptor.getDisplayName() ); |
|
256 | //nodeDescriptor.setShortDescription( propertyDescriptor.getShortDescription() ); |
|
257 | ||
258 | 0 | if ( readMethod == null ) { |
259 | 0 | log.trace( "No read method" ); |
260 | 0 | return; |
261 | } |
|
262 | ||
263 | 0 | if ( log.isTraceEnabled() ) { |
264 | 0 | log.trace( "Read method=" + readMethod.getName() ); |
265 | } |
|
266 | ||
267 | // choose response from property type |
|
268 | ||
269 | // XXX: ignore class property ?? |
|
270 | 0 | if ( Class.class.equals( type ) && "class".equals( propertyDescriptor.getName() ) ) { |
271 | 0 | log.trace( "Ignoring class property" ); |
272 | 0 | return; |
273 | } |
|
274 | 0 | if ( isPrimitiveType( type ) ) { |
275 | 0 | elementDescriptor.setTextExpression( new MethodExpression( readMethod ) ); |
276 | ||
277 | 0 | } else if ( isLoopType( type ) ) { |
278 | 0 | log.trace("Loop type ??"); |
279 | ||
280 | // don't wrap this in an extra element as its specified in the |
|
281 | // XML descriptor so no need. |
|
282 | 0 | elementDescriptor.setContextExpression( |
283 | 0 | new IteratorExpression( new MethodExpression( readMethod ) ) |
284 | ); |
|
285 | ||
286 | 0 | writeMethod = null; |
287 | } else { |
|
288 | 0 | log.trace( "Standard property" ); |
289 | 0 | elementDescriptor.setContextExpression( new MethodExpression( readMethod ) ); |
290 | } |
|
291 | ||
292 | // see if we have a custom method update name |
|
293 | 0 | if (updateMethodName == null) { |
294 | // set standard write method |
|
295 | 0 | if ( writeMethod != null ) { |
296 | 0 | elementDescriptor.setUpdater( new MethodUpdater( writeMethod ) ); |
297 | } |
|
298 | ||
299 | } else { |
|
300 | // see if we can find and set the custom method |
|
301 | 0 | if ( log.isTraceEnabled() ) { |
302 | 0 | log.trace( "Finding custom method: " ); |
303 | 0 | log.trace( " on:" + beanClass ); |
304 | 0 | log.trace( " name:" + updateMethodName ); |
305 | } |
|
306 | ||
307 | 0 | Method updateMethod = null; |
308 | 0 | Method[] methods = beanClass.getMethods(); |
309 | 0 | for ( int i = 0, size = methods.length; i < size; i++ ) { |
310 | 0 | Method method = methods[i]; |
311 | 0 | if ( updateMethodName.equals( method.getName() ) ) { |
312 | // we have a matching name |
|
313 | // check paramters are correct |
|
314 | 0 | if (methods[i].getParameterTypes().length == 1) { |
315 | // we'll use first match |
|
316 | 0 | updateMethod = methods[i]; |
317 | 0 | if ( log.isTraceEnabled() ) { |
318 | 0 | log.trace("Matched method:" + updateMethod); |
319 | } |
|
320 | // done since we're using the first match |
|
321 | 0 | break; |
322 | } |
|
323 | } |
|
324 | } |
|
325 | ||
326 | 0 | if (updateMethod == null) { |
327 | 0 | if ( log.isInfoEnabled() ) { |
328 | ||
329 | 0 | log.info("No method with name '" + updateMethodName + "' found for update"); |
330 | } |
|
331 | } else { |
|
332 | ||
333 | 0 | elementDescriptor.setUpdater( new MethodUpdater( updateMethod ) ); |
334 | 0 | elementDescriptor.setSingularPropertyType( updateMethod.getParameterTypes()[0] ); |
335 | 0 | if ( log.isTraceEnabled() ) { |
336 | 0 | log.trace( "Set custom updater on " + elementDescriptor); |
337 | } |
|
338 | } |
|
339 | } |
|
340 | 0 | } |
341 | ||
342 | /** |
|
343 | * Configure an <code>AttributeDescriptor</code> from a <code>PropertyDescriptor</code> |
|
344 | * |
|
345 | * @param attributeDescriptor configure this <code>AttributeDescriptor</code> |
|
346 | * @param propertyDescriptor configure from this <code>PropertyDescriptor</code> |
|
347 | * @deprecated 0.6 moved into AttributeRule |
|
348 | */ |
|
349 | public static void configureProperty( |
|
350 | AttributeDescriptor attributeDescriptor, |
|
351 | PropertyDescriptor propertyDescriptor ) { |
|
352 | 0 | Class type = propertyDescriptor.getPropertyType(); |
353 | 0 | Method readMethod = propertyDescriptor.getReadMethod(); |
354 | 0 | Method writeMethod = propertyDescriptor.getWriteMethod(); |
355 | ||
356 | 0 | if ( readMethod == null ) { |
357 | 0 | log.trace( "No read method" ); |
358 | 0 | return; |
359 | } |
|
360 | ||
361 | 0 | if ( log.isTraceEnabled() ) { |
362 | 0 | log.trace( "Read method=" + readMethod ); |
363 | } |
|
364 | ||
365 | // choose response from property type |
|
366 | ||
367 | // XXX: ignore class property ?? |
|
368 | 0 | if ( Class.class.equals( type ) && "class".equals( propertyDescriptor.getName() ) ) { |
369 | 0 | log.trace( "Ignoring class property" ); |
370 | 0 | return; |
371 | } |
|
372 | 0 | if ( isLoopType( type ) ) { |
373 | 0 | log.warn( "Using loop type for an attribute. Type = " |
374 | 0 | + type.getName() + " attribute: " + attributeDescriptor.getQualifiedName() ); |
375 | } |
|
376 | ||
377 | 0 | log.trace( "Standard property" ); |
378 | 0 | attributeDescriptor.setTextExpression( new MethodExpression( readMethod ) ); |
379 | ||
380 | 0 | if ( writeMethod != null ) { |
381 | 0 | attributeDescriptor.setUpdater( new MethodUpdater( writeMethod ) ); |
382 | } |
|
383 | ||
384 | 0 | attributeDescriptor.setLocalName( propertyDescriptor.getName() ); |
385 | 0 | attributeDescriptor.setPropertyType( type ); |
386 | ||
387 | // XXX: associate more bean information with the descriptor? |
|
388 | //nodeDescriptor.setDisplayName( propertyDescriptor.getDisplayName() ); |
|
389 | //nodeDescriptor.setShortDescription( propertyDescriptor.getShortDescription() ); |
|
390 | 0 | } |
391 | ||
392 | ||
393 | /** |
|
394 | * Add any addPropety(PropertyType) methods as Updaters |
|
395 | * which are often used for 1-N relationships in beans. |
|
396 | * <br> |
|
397 | * The tricky part here is finding which ElementDescriptor corresponds |
|
398 | * to the method. e.g. a property 'items' might have an Element descriptor |
|
399 | * which the method addItem() should match to. |
|
400 | * <br> |
|
401 | * So the algorithm we'll use |
|
402 | * by default is to take the decapitalized name of the property being added |
|
403 | * and find the first ElementDescriptor that matches the property starting with |
|
404 | * the string. This should work for most use cases. |
|
405 | * e.g. addChild() would match the children property. |
|
406 | * |
|
407 | * @param introspector use this <code>XMLIntrospector</code> for introspection |
|
408 | * @param rootDescriptor add defaults to this descriptor |
|
409 | * @param beanClass the <code>Class</code> to which descriptor corresponds |
|
410 | * @deprecated 0.6 use the method in XMLIntrospector instead |
|
411 | */ |
|
412 | public static void defaultAddMethods( |
|
413 | XMLIntrospector introspector, |
|
414 | ElementDescriptor rootDescriptor, |
|
415 | Class beanClass ) { |
|
416 | // lets iterate over all methods looking for one of the form |
|
417 | // add*(PropertyType) |
|
418 | 0 | if ( beanClass != null ) { |
419 | 0 | Method[] methods = beanClass.getMethods(); |
420 | 0 | for ( int i = 0, size = methods.length; i < size; i++ ) { |
421 | 0 | Method method = methods[i]; |
422 | 0 | String name = method.getName(); |
423 | 0 | if ( name.startsWith( "add" ) ) { |
424 | // XXX: should we filter out non-void returning methods? |
|
425 | // some beans will return something as a helper |
|
426 | 0 | Class[] types = method.getParameterTypes(); |
427 | 0 | if ( types != null) { |
428 | 0 | if ( log.isTraceEnabled() ) { |
429 | 0 | log.trace("Searching for match for " + method); |
430 | } |
|
431 | ||
432 | 0 | if ( ( types.length == 1 ) || types.length == 2 ) { |
433 | 0 | String propertyName = Introspector.decapitalize( name.substring(3) ); |
434 | 0 | if (propertyName.length() == 0) |
435 | 0 | continue; |
436 | 0 | if ( log.isTraceEnabled() ) { |
437 | 0 | log.trace( name + "->" + propertyName ); |
438 | } |
|
439 | ||
440 | // now lets try find the ElementDescriptor which displays |
|
441 | // a property which starts with propertyName |
|
442 | // and if so, we'll set a new Updater on it if there |
|
443 | // is not one already |
|
444 | 0 | ElementDescriptor descriptor = |
445 | 0 | findGetCollectionDescriptor( |
446 | 0 | introspector, |
447 | 0 | rootDescriptor, |
448 | 0 | propertyName ); |
449 | ||
450 | 0 | if ( log.isDebugEnabled() ) { |
451 | 0 | log.debug( "!! " + propertyName + " -> " + descriptor ); |
452 | 0 | log.debug( "!! " + name + " -> " |
453 | 0 | + (descriptor!=null?descriptor.getPropertyName():"") ); |
454 | } |
|
455 | 0 | if ( descriptor != null ) { |
456 | 0 | boolean isMapDescriptor |
457 | 0 | = Map.class.isAssignableFrom( descriptor.getPropertyType() ); |
458 | 0 | if ( !isMapDescriptor && types.length == 1 ) { |
459 | // this may match a standard collection or iteration |
|
460 | 0 | log.trace("Matching collection or iteration"); |
461 | ||
462 | 0 | descriptor.setUpdater( new MethodUpdater( method ) ); |
463 | 0 | descriptor.setSingularPropertyType( types[0] ); |
464 | ||
465 | 0 | if ( log.isDebugEnabled() ) { |
466 | 0 | log.debug( "!! " + method); |
467 | 0 | log.debug( "!! " + types[0]); |
468 | } |
|
469 | ||
470 | // is there a child element with no localName |
|
471 | 0 | ElementDescriptor[] children |
472 | 0 | = descriptor.getElementDescriptors(); |
473 | 0 | if ( children != null && children.length > 0 ) { |
474 | 0 | ElementDescriptor child = children[0]; |
475 | 0 | String localName = child.getLocalName(); |
476 | 0 | if ( localName == null || localName.length() == 0 ) { |
477 | 0 | child.setLocalName( |
478 | 0 | introspector.getElementNameMapper() |
479 | 0 | .mapTypeToElementName( propertyName ) ); |
480 | } |
|
481 | } |
|
482 | ||
483 | 0 | } else if ( isMapDescriptor && types.length == 2 ) { |
484 | // this may match a map |
|
485 | 0 | log.trace("Matching map"); |
486 | 0 | ElementDescriptor[] children |
487 | 0 | = descriptor.getElementDescriptors(); |
488 | // see if the descriptor's been set up properly |
|
489 | 0 | if ( children.length == 0 ) { |
490 | ||
491 | 0 | log.info( |
492 | 0 | "'entry' descriptor is missing for map. " |
493 | + "Updaters cannot be set"); |
|
494 | ||
495 | } else { |
|
496 | // loop through grandchildren |
|
497 | // adding updaters for key and value |
|
498 | 0 | ElementDescriptor[] grandchildren |
499 | 0 | = children[0].getElementDescriptors(); |
500 | 0 | MapEntryAdder adder = new MapEntryAdder(method); |
501 | for ( |
|
502 | 0 | int n=0, |
503 | 0 | noOfGrandChildren = grandchildren.length; |
504 | 0 | n < noOfGrandChildren; |
505 | 0 | n++ ) { |
506 | 0 | if ( "key".equals( |
507 | 0 | grandchildren[n].getLocalName() ) ) { |
508 | ||
509 | 0 | grandchildren[n].setUpdater( |
510 | 0 | adder.getKeyUpdater() ); |
511 | 0 | grandchildren[n].setSingularPropertyType( |
512 | 0 | types[0] ); |
513 | 0 | if ( log.isTraceEnabled() ) { |
514 | 0 | log.trace( |
515 | 0 | "Key descriptor: " + grandchildren[n]); |
516 | } |
|
517 | ||
518 | } else if ( |
|
519 | 0 | "value".equals( |
520 | 0 | grandchildren[n].getLocalName() ) ) { |
521 | ||
522 | 0 | grandchildren[n].setUpdater( |
523 | 0 | adder.getValueUpdater() ); |
524 | 0 | grandchildren[n].setSingularPropertyType( |
525 | 0 | types[1] ); |
526 | 0 | if ( log.isTraceEnabled() ) { |
527 | 0 | log.trace( |
528 | 0 | "Value descriptor: " + grandchildren[n]); |
529 | } |
|
530 | } |
|
531 | } |
|
532 | } |
|
533 | } |
|
534 | } else { |
|
535 | 0 | if ( log.isDebugEnabled() ) { |
536 | 0 | log.debug( |
537 | 0 | "Could not find an ElementDescriptor with property name: " |
538 | 0 | + propertyName + " to attach the add method: " + method |
539 | ); |
|
540 | } |
|
541 | } |
|
542 | } |
|
543 | } |
|
544 | } |
|
545 | } |
|
546 | } |
|
547 | 0 | } |
548 | ||
549 | /** |
|
550 | * Is this a loop type class? |
|
551 | * |
|
552 | * @param type is this <code>Class</code> a loop type? |
|
553 | * @return true if the type is a loop type, or if type is null |
|
554 | * @deprecated 0.7 replaced by {@link org.apache.commons.betwixt.IntrospectionConfiguration#isLoopType(Class)} |
|
555 | */ |
|
556 | public static boolean isLoopType(Class type) { |
|
557 | // check for NPEs |
|
558 | 0 | if (type == null) { |
559 | 0 | log.trace("isLoopType: type is null"); |
560 | 0 | return false; |
561 | } |
|
562 | 0 | return type.isArray() |
563 | 0 | || Map.class.isAssignableFrom( type ) |
564 | 0 | || Collection.class.isAssignableFrom( type ) |
565 | 0 | || Enumeration.class.isAssignableFrom( type ) |
566 | 0 | || Iterator.class.isAssignableFrom( type ); |
567 | } |
|
568 | ||
569 | ||
570 | /** |
|
571 | * Is this a primitive type? |
|
572 | * |
|
573 | * TODO: this method will probably be removed when primitive types |
|
574 | * are subsumed into the simple type concept. |
|
575 | * This needs moving into XMLIntrospector so that the list of simple |
|
576 | * type can be varied. |
|
577 | * @param type is this <code>Class<code> a primitive type? |
|
578 | * @return true for primitive types |
|
579 | * @deprecated 0.6 replaced by {@link org.apache.commons.betwixt.strategy.TypeBindingStrategy} |
|
580 | */ |
|
581 | public static boolean isPrimitiveType(Class type) { |
|
582 | 0 | if ( type == null ) { |
583 | 0 | return false; |
584 | ||
585 | 0 | } else if ( type.isPrimitive() ) { |
586 | 0 | return true; |
587 | ||
588 | 0 | } else if ( type.equals( Object.class ) ) { |
589 | 0 | return false; |
590 | } |
|
591 | 0 | return type.getName().startsWith( "java.lang." ) |
592 | 0 | || Number.class.isAssignableFrom( type ) |
593 | 0 | || String.class.isAssignableFrom( type ) |
594 | 0 | || Date.class.isAssignableFrom( type ) |
595 | 0 | || java.sql.Date.class.isAssignableFrom( type ) |
596 | 0 | || java.sql.Time.class.isAssignableFrom( type ) |
597 | 0 | || java.sql.Timestamp.class.isAssignableFrom( type ) |
598 | 0 | || java.math.BigDecimal.class.isAssignableFrom( type ) |
599 | 0 | || java.math.BigInteger.class.isAssignableFrom( type ); |
600 | } |
|
601 | ||
602 | // Implementation methods |
|
603 | //------------------------------------------------------------------------- |
|
604 | ||
605 | /** |
|
606 | * Attempts to find the element descriptor for the getter property that |
|
607 | * typically matches a collection or array. The property name is used |
|
608 | * to match. e.g. if an addChild() method is detected the |
|
609 | * descriptor for the 'children' getter property should be returned. |
|
610 | * |
|
611 | * @param introspector use this <code>XMLIntrospector</code> |
|
612 | * @param rootDescriptor the <code>ElementDescriptor</code> whose child element will be |
|
613 | * searched for a match |
|
614 | * @param propertyName the name of the 'adder' method to match |
|
615 | * @return <code>ElementDescriptor</code> for the matching getter |
|
616 | * @deprecated 0.6 moved into XMLIntrospector |
|
617 | */ |
|
618 | protected static ElementDescriptor findGetCollectionDescriptor( |
|
619 | XMLIntrospector introspector, |
|
620 | ElementDescriptor rootDescriptor, |
|
621 | String propertyName ) { |
|
622 | // create the Map of propertyName -> descriptor that the PluralStemmer will choose |
|
623 | 0 | Map map = new HashMap(); |
624 | //String propertyName = rootDescriptor.getPropertyName(); |
|
625 | 0 | if ( log.isTraceEnabled() ) { |
626 | 0 | log.trace( "findPluralDescriptor( " + propertyName |
627 | 0 | + " ):root property name=" + rootDescriptor.getPropertyName() ); |
628 | } |
|
629 | ||
630 | 0 | if (rootDescriptor.getPropertyName() != null) { |
631 | 0 | map.put(propertyName, rootDescriptor); |
632 | } |
|
633 | 0 | makeElementDescriptorMap( rootDescriptor, map ); |
634 | ||
635 | 0 | PluralStemmer stemmer = introspector.getPluralStemmer(); |
636 | 0 | ElementDescriptor elementDescriptor = stemmer.findPluralDescriptor( propertyName, map ); |
637 | ||
638 | 0 | if ( log.isTraceEnabled() ) { |
639 | 0 | log.trace( |
640 | 0 | "findPluralDescriptor( " + propertyName |
641 | 0 | + " ):ElementDescriptor=" + elementDescriptor ); |
642 | } |
|
643 | ||
644 | 0 | return elementDescriptor; |
645 | } |
|
646 | ||
647 | /** |
|
648 | * Creates a map where the keys are the property names and the values are the ElementDescriptors |
|
649 | * |
|
650 | * @param rootDescriptor the values of the maps are the children of this |
|
651 | * <code>ElementDescriptor</code> index by their property names |
|
652 | * @param map the map to which the elements will be added |
|
653 | * @deprecated 0.6 moved into XMLIntrospector |
|
654 | */ |
|
655 | protected static void makeElementDescriptorMap( ElementDescriptor rootDescriptor, Map map ) { |
|
656 | 0 | ElementDescriptor[] children = rootDescriptor.getElementDescriptors(); |
657 | 0 | if ( children != null ) { |
658 | 0 | for ( int i = 0, size = children.length; i < size; i++ ) { |
659 | 0 | ElementDescriptor child = children[i]; |
660 | 0 | String propertyName = child.getPropertyName(); |
661 | 0 | if ( propertyName != null ) { |
662 | 0 | map.put( propertyName, child ); |
663 | } |
|
664 | 0 | makeElementDescriptorMap( child, map ); |
665 | } |
|
666 | } |
|
667 | 0 | } |
668 | ||
669 | /** |
|
670 | * Traverse the tree of element descriptors and find the oldValue and swap it with the newValue. |
|
671 | * This would be much easier to do if ElementDescriptor supported a parent relationship. |
|
672 | * |
|
673 | * @param rootDescriptor traverse child graph for this <code>ElementDescriptor</code> |
|
674 | * @param oldValue replace this <code>ElementDescriptor</code> |
|
675 | * @param newValue replace with this <code>ElementDescriptor</code> |
|
676 | * @deprecated 0.6 now unused |
|
677 | */ |
|
678 | protected static void swapDescriptor( |
|
679 | ElementDescriptor rootDescriptor, |
|
680 | ElementDescriptor oldValue, |
|
681 | ElementDescriptor newValue ) { |
|
682 | 0 | ElementDescriptor[] children = rootDescriptor.getElementDescriptors(); |
683 | 0 | if ( children != null ) { |
684 | 0 | for ( int i = 0, size = children.length; i < size; i++ ) { |
685 | 0 | ElementDescriptor child = children[i]; |
686 | 0 | if ( child == oldValue ) { |
687 | 0 | children[i] = newValue; |
688 | 0 | break; |
689 | } |
|
690 | 0 | swapDescriptor( child, oldValue, newValue ); |
691 | } |
|
692 | } |
|
693 | 0 | } |
694 | } |