Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/aendvari/griffin/bean/transform/BeanTransformer.java


1   /*
2    * BeanTransformer.java
3    *
4    * Copyright (c) 2001, 2002 Aendvari, Ltd. All Rights Reserved.
5    *
6    * See the file LICENSE for terms of use.
7    *
8    */
9   
10  package com.aendvari.griffin.bean.transform;
11  
12  import java.beans.*;
13  import java.util.*;
14  
15  import java.lang.reflect.*;
16  import java.lang.NoSuchMethodException;
17  import java.lang.IllegalAccessException;
18  
19  import com.aendvari.common.util.*;
20  
21  import com.aendvari.common.model.*;
22  
23  
24  /**
25   * <p>
26   * Translates the contents of a <code>Java Bean</code> into the given {@link ModelNode}.
27   * See the {@link com.aendvari.common.model} package for more about the {@link ModelNode}.
28   * </p>
29   *
30   * @author  Scott Milne
31   *
32   */
33  
34  public class BeanTransformer
35  {
36    /**
37     * Converts the given bean into it's XML representation as required.
38     *
39     * @param  beanObject        The bean to convert.
40     * @param  modelNode        The {@link ModelNode} to output nodes to.
41     * @param  replaceChildren      Specifies whether to replace all children nodes.
42     *
43     * @throws              Throws an <code>Exception</code> if a conversion error occurs.
44     *
45     */
46  
47    public static void beanToModel( Object beanObject, ModelNode modelNode, boolean replaceChildren )
48      throws Exception
49    {
50      try
51      {
52        ModelTree modelTree = modelNode.getOwnerModelTree();
53  
54        // remove all children of output node
55        if (replaceChildren)
56        {
57          Iterator childIterator = modelNode.getChildNodes().iterator();
58          while (childIterator.hasNext())
59          {
60            ModelNode node = (ModelNode)childIterator.next();
61            modelNode.removeChild(node);
62          }
63        }
64  
65        // transform node, place created nodes as part of supplied node
66        beanToModel(beanObject, modelTree, modelNode);
67      }
68      /*
69       * NoSuchMethodException
70       * IllegalAccessException
71       * IntrospectionException
72       *
73       */
74      catch (Exception exception)
75      {
76        throw exception;
77      }
78    }
79  
80    /**
81     * Converts the given bean into it's XML representation as required. A new node
82     * is created under the supplied model node to contain bean.
83     *
84     * @param  beanClass        The bean to convert.
85     * @param  propertyName      The {@link ModelTree} instance to create new nodes from.
86     * @param  modelTree        The {@link ModelTree} instance to create new nodes from.
87     * @param  modelNode        The {@link ModelNode} instance to add new nodes to.
88     *
89     * @return              A {@link ModelNode} instance of the given bean.
90     * @throws              Throws an <code>Exception</code> if a conversion error occurs.
91     *
92     */
93  
94    static private ModelNode beanToModel( Object beanObject, String propertyName, ModelTree modelTree, ModelNode modelNode )
95      throws Exception
96    {
97      // create the node for the "class"
98      ModelNode propertyNode = modelTree.createNode( propertyName );
99      modelNode.appendChild(propertyNode);
100 
101     // transform node, place created nodes as part of supplied node
102     beanToModel(beanObject, modelTree, propertyNode);
103 
104     return propertyNode;
105   }
106 
107   /**
108    * Converts the given bean into it's XML representation as required.
109    *
110    * @param  beanObject        The bean to convert.
111    * @param  modelTree        The {@link ModelTree} instance to create new nodes from.
112    * @param  propertyNode      The {@link ModelNode} to output nodes to.
113    *
114    * @throws              Throws an <code>Exception</code> if a conversion error occurs.
115    *
116    */
117 
118   static private void beanToModel( Object beanObject, ModelTree modelTree, ModelNode propertyNode )
119     throws Exception
120   {
121     // create the <class> child
122     ModelNode classNode = modelTree.createNode( "class", beanObject.getClass().getName() );
123     propertyNode.appendChild(classNode);
124 
125     // create an inspected bean instance of this bean
126     InspectedBean bean = new InspectedBean(beanObject);
127 
128     // get the list of "get" methods for this bean
129     HashMap getMethods = bean.getGetMethods();
130 
131     // iterate through the methods and get their values
132     Iterator keysIterator = getMethods.keySet().iterator();
133 
134     while (keysIterator.hasNext())
135     {
136       String propertyNameKey = (String)keysIterator.next();
137 
138       Object value = bean.executeGetMethod(propertyNameKey, beanObject);
139 
140       // make sure the value was initialized
141       if( value != null )
142       {
143         // examine the property (recursive)
144         examineObject(modelTree, propertyNode, propertyNameKey, value );
145       }
146     }
147   }
148 
149   /**
150    * Examines the given object to determine its type, and then transform that object
151    * into it's XML representation as required.
152    *
153    * @param  modelTree        The {@link ModelTree} instance to create new nodes from.
154    * @param  modelNode        The {@link ModelNode} instance to add new nodes to.
155    * @param  propertyName      The name of the property were examining.
156    * @param  value          The object to examine.
157    *
158    * @throws              Throws an <code>Exception</code> if a conversion error occurs.
159    *
160    */
161 
162   static private void examineObject( ModelTree modelTree, ModelNode classNode, String propertyName, Object value )
163     throws Exception
164   {
165     try
166     {
167       Class valueClass = value.getClass();
168 
169       // if the object is a array[]
170       if (value.getClass().isArray())
171       {
172         int nLength = java.lang.reflect.Array.getLength(value);
173 
174         for( int i=0; i<nLength; i++ )
175         {
176           Object indexedObject = java.lang.reflect.Array.get(value,i);
177           examineObject(modelTree, classNode, propertyName, indexedObject );
178         }
179       }
180       // if the object is a Map type
181       else if( value instanceof Map )
182       {
183         Map map = (Map)value;
184 
185         // go through each value array checking for the value
186         Iterator keysIterator = map.keySet().iterator();
187         while (keysIterator.hasNext())
188         {
189           // create the new node
190           ModelNode propertyNode = modelTree.createNode( propertyName );
191           classNode.appendChild( propertyNode );
192 
193           // create the "key" node and examine the propery for the key
194           Object keyObject = (Object)keysIterator.next();
195           ModelNode keyNode = modelTree.createNode( "key" );
196           propertyNode.appendChild( keyNode );
197           examineObject(modelTree, keyNode, "key", keyObject );
198 
199           // create the "value" node and examine the propery for the value
200           Object valueObject = map.get(keyObject);
201           ModelNode valueNode = modelTree.createNode( "value" );
202           propertyNode.appendChild( valueNode );
203           examineObject(modelTree, valueNode, "value", valueObject );
204         }
205       }
206       // if the object is a Set type
207       else if( value instanceof Collection )
208       {
209         // go through each value array checking for the value
210         Iterator valuesIterator = ((Collection)value).iterator();
211         while (valuesIterator.hasNext())
212         {
213           // get the object for this item
214           Object setValue = (Object)valuesIterator.next();
215 
216           // create the node for this item
217           ModelNode propertyNode = modelTree.createNode( propertyName );
218           classNode.appendChild( propertyNode );
219 
220           // examine the property for this item
221           examineObject(modelTree, propertyNode, propertyName, setValue );
222         }
223       }
224       // if the property is another Bean, convert it as well
225       else if( !valueClass.isPrimitive() && !isJavaUtilClass(value) && !isJavaLangClass(value) )
226       {
227         // get the parent node
228         ModelNode parentNode = classNode.getParentNode();
229 
230         // remove the property node in the parent
231         if (classNode.getNodeName().equals(propertyName))
232         {
233           // extract this bean into the model
234           ModelNode tmpBeanNode = beanToModel( value, propertyName, modelTree, classNode );
235 
236           // replace the previous property node with the bean node
237           parentNode.replaceChild( tmpBeanNode, classNode );
238         }
239         else
240         {
241           // extract this bean into the model
242           beanToModel( value, propertyName, modelTree, classNode );
243         }
244 
245       }
246       // the property name is the same as the node name, and the node does not have a <class> child,
247       // this means we are running within a collection, set or map
248       else if( propertyName.equals(classNode.getNodeName()) && !hasClassChild(classNode) )
249       {
250         // get the parent node
251         ModelNode parentNode = classNode.getParentNode();
252 
253         // create a new text node, and replace the current one with
254         // the new one and it's text value.
255         String sValue = value.toString();
256         ModelNode propertyNode = modelTree.createNode( propertyName, sValue );
257         parentNode.replaceChild( propertyNode, classNode );
258       }
259       // otherwise, its either a primitive, or simple property object
260       else
261       {
262         // create the new node
263         String sValue = value.toString();
264         ModelNode propertyNode = modelTree.createNode( propertyName, sValue );
265         classNode.appendChild( propertyNode );
266       }
267     }
268     catch( Exception exception )
269     {
270       throw exception;
271     }
272   }
273 
274 
275   /**
276    * Determines if the given node has a <code>&lt;class&gt;</code> child within it.
277    *
278    * @param    parentNode      The node to search the children of.
279    *
280    */
281 
282   static private boolean hasClassChild( ModelNode parentNode )
283   {
284     ModelNode node = parentNode.getFirstChild();
285 
286     while (node != null)
287     {
288       String nodeName = node.getNodeName();
289 
290       if (nodeName.equals("class"))
291       {
292         return true;
293       }
294 
295       node = node.getNextSibling();
296     }
297 
298     return false;
299   }
300 
301 
302   /**
303    * Extract a bean from the XML node.
304    *
305    * @param    beanNode      The XML node of which the <class> for the bean exists.
306    *
307    * @throws              Throws an <code>Exception</code> if a conversion error occurs.
308    * @return              Returns a bean instance as an <code>Object</code>.
309    *
310    */
311 
312   public static Object createBeanFromModel( ModelNode beanNode )
313     throws Exception
314   {
315     // get the name of the class
316     String beanClassName = beanNode.getFirstChild().getNodeValue();
317 
318     // create an inspected bean instance of this bean
319     InspectedBean bean = new InspectedBean(beanClassName);
320 
321     // examine the first level of properties (recursive)
322     examineNodeValues(bean, beanNode, false);
323 
324     // obtain an instance of the bean
325     Object modelBean = bean.getBeanInstance();
326 
327     return modelBean;
328   }
329 
330   /**
331    * Extract a bean from the XMl node and places into given bean.
332    * Only the properties in the XML are "set" into the bean, the remainders
333    * are left in their original state.
334    *
335    * @param    beanObj        The bean to transfer data into.
336    * @param    beanNode      The XML node of which the <class> for the bean exists.
337    *
338    * @throws              Throws an <code>Exception</code> if a conversion error occurs.
339    * @return              Returns a bean instance as an <code>Object</code>.
340    *
341    */
342 
343   public static Object updateBeanFromModel( Object beanObj, ModelNode beanNode )
344     throws Exception
345   {
346     // create an inspected bean instance of this bean
347     InspectedBean bean = new InspectedBean(beanObj);
348 
349     // examine the first level of properties (recursive)
350     examineNodeValues(bean, beanNode, true);
351 
352     // obtain an instance of the bean
353     Object modelBean = bean.getBeanInstance();
354 
355     return modelBean;
356   }
357 
358   /**
359    * Examines the node (and it's children) for the bean properties to
360    * match with the inspected bean.
361    *
362    * Once a property is matched, it is added to the "set" method parameters of the bean.
363    *
364    * @param    inspectedBean    A {@link InspectedBean} object of the bean we are extracting.
365    * @param    modelNode      The node to examine for property values.
366    * @param    updateExamine    If this examine is for an update, set this to "true".
367    *
368    * @throws              Throws an <code>Exception</code> if a conversion error occurs.
369    *
370    */
371 
372   static private void examineNodeValues( InspectedBean inspectedBean, ModelNode objectNode, boolean updateExamine )
373     throws Exception
374   {
375     ModelNode node = objectNode.getFirstChild();
376 
377     while (node != null)
378     {
379       String nodeName = node.getNodeName();
380 
381       // by default use the node name as the property name
382       Class methodParamClass = inspectedBean.getSetMethodParamClass(nodeName);
383 
384       // get the first child node of this node
385       ModelNode firstChildNode = node.getFirstChild();
386       String firstChildNodeName = "";
387       if (firstChildNode != null)
388       {
389         firstChildNodeName = firstChildNode.getNodeName();
390       }
391 
392       // if we're updating, and the first child node name
393       // is not "class", check the bean to see if the related node name
394       // is a class or not
395       if (updateExamine && !firstChildNodeName.equals("class"))
396       {
397         InspectedBean.BeanGetMethod beanGetMethod = inspectedBean.getBeanGetMethod(nodeName);
398         if (beanGetMethod != null)
399         {
400           Method getMethod = beanGetMethod.getMethod();
401           Class returnType = getMethod.getReturnType();
402 
403           // make sure the return type is only a bean
404           if( !returnType.isPrimitive() &&
405             !isJavaUtilClass(returnType) &&
406             !isJavaLangClass(returnType) &&
407             !returnType.isArray()
408           )
409           {
410             // if it starts with class, then reset the first child node name
411             if( getMethod.getReturnType().toString().startsWith("class") )
412             {
413               firstChildNodeName = "class";
414             }
415           }
416         }
417       }
418 
419       // if the first child node is a class, examine it
420       if( firstChildNodeName.equals("class") )
421       {
422         // get the type of the class from
423         String beanClassName = firstChildNode.getNodeValue();
424 
425         // set the method param class to the value that was derived from the node
426         methodParamClass = inspectedBean.getSetMethodParamClass(nodeName);
427 
428         // this method param is not a map, so add it's value to the bean's method list of param's
429         if(
430           methodParamClass != null                 &&
431           !Map.class.isAssignableFrom(methodParamClass)      &&
432           !Collection.class.isAssignableFrom(methodParamClass)
433         )
434         {
435           Object newBean = null;
436           InspectedBean bean = null;
437 
438           // if were doing an update, use the bean property instead
439           if (updateExamine)
440           {
441             // try getting the property bean from the master bean
442             newBean = inspectedBean.executeGetMethod(nodeName, inspectedBean.getBeanObject() );
443 
444             // if null, then create an instance to place values into
445             if (newBean == null)
446             {
447               newBean = methodParamClass.newInstance();
448             }
449 
450             // we need to create a new inspector bean for this class
451             bean = new InspectedBean(newBean);
452           }
453           // a "create" is being done, so just pass in the class name
454           // and an instance of that class will be automatically be created.
455           else
456           {
457             // we need to create a new inspector bean for this class
458             bean = new InspectedBean(beanClassName);
459           }
460 
461           // using the new inspected bean, examine the child nodes
462           examineNodeValues(bean, node, updateExamine);
463 
464           newBean = bean.getBeanInstance();
465 
466           // save the method param for the property
467           inspectedBean.addSetMethodParamValue( nodeName, newBean );
468         }
469         // this class is within a collection
470         else if( methodParamClass != null && Collection.class.isAssignableFrom(methodParamClass) )
471         {
472           // we need to create a new inspector bean for this class
473           InspectedBean bean = new InspectedBean(beanClassName);
474 
475           // using the new inspected bean, examine the child nodes
476           examineNodeValues(bean, node, updateExamine);
477 
478           // create a new instance of the bean using the extracted data
479           // and add it to the method parameters as required.
480           Object newBean = bean.getBeanInstance();
481 
482           // save the method param for the property
483           inspectedBean.addSetMethodParamValue( nodeName, newBean );
484         }
485         // this class is within a map object
486         else
487         {
488           // we need to create a new inspector bean for this class
489           InspectedBean bean = new InspectedBean(beanClassName);
490 
491           // using the new inspected bean, examine the child nodes
492           examineNodeValues(bean, node, updateExamine);
493 
494           // create a new instance of the bean using the extracted data
495           // and add it to the method parameters as required.
496           Object newBean = bean.getBeanInstance();
497 
498           // since this class is within a map object, find out
499           // which child node this class is within (key/value)
500           // by asking the parent node of this class what it is.
501           ModelNode parentNode = node.getParentNode();
502 
503           if( parentNode != null )
504           {
505             // get the name of the parent node
506             String parentNodeName = parentNode.getNodeName();
507 
508             // if the parent node is a "key" set the key for this map
509             if( nodeName.equals("key") )
510             {
511               // save the method param for the property
512               inspectedBean.addSetMethodParamMapKey( parentNodeName, newBean );
513             }
514             // if the parent node is a "value" set the value for this map
515             else if( nodeName.equals("value") )
516             {
517               // save the method param for the property
518               inspectedBean.addSetMethodParamMapValue( parentNodeName, newBean );
519             }
520           }
521         }
522       }
523       // this node is a Map
524       else if( methodParamClass != null && Map.class.isAssignableFrom(methodParamClass) )
525       {
526         // create the map list for this method (not the param list)
527         inspectedBean.createSetMethodParamMap( nodeName );
528 
529         // examine this node
530         examineNodeValues(inspectedBean, node, updateExamine);
531 
532         // add all the discovered map entries to the map method params
533         inspectedBean.addSetMethodParamMap( nodeName );
534       }
535       // at this point we are dealing with a single property
536       // this can be either a bean property, or the key/value pairs
537       // of a map
538       else
539       {
540         methodParamClass = null;
541 
542         // if a valid parent node was found, try getting the methods param class
543         ModelNode parentNode = node.getParentNode();
544         if( parentNode != null )
545         {
546           methodParamClass = inspectedBean.getSetMethodParamClass(parentNode.getNodeName());
547         }
548 
549         // if a valid param class was found, test to see if it's a Map
550         if( methodParamClass != null && Map.class.isAssignableFrom(methodParamClass) )
551         {
552           // if this node is a key, add it to the map list
553           if( nodeName.equals("key") )
554           {
555             inspectedBean.addSetMethodParamMapKey( parentNode.getNodeName(), node.getNodeValue() );
556           }
557           // if this node is a value, add it to the map list
558           else if( nodeName.equals("value") )
559           {
560             inspectedBean.addSetMethodParamMapValue( parentNode.getNodeName(), node.getNodeValue() );
561           }
562         }
563         // this is a simple property, add it to the methods param list
564         else
565         {
566           // save the method param for the property
567           inspectedBean.addSetMethodParamValue( nodeName, node.getNodeValue() );
568         }
569       }
570 
571       // get next child node
572       node = node.getNextSibling();
573     }
574   }
575 
576   /**
577    * Determines if the given object is a class from the <code>java.util package</code>.
578    *
579    * @param  Object        The object to test.
580    *
581    */
582 
583   public static boolean isJavaUtilClass( Object value )
584   {
585     Class valueClass = value.getClass();
586     return isJavaUtilClass(valueClass);
587   }
588 
589   /**
590    * Determines if the given class is from the <code>java.util package</code>.
591    *
592    * @param  Class        The class to test.
593    *
594    */
595 
596   public static boolean isJavaUtilClass( Class valueClass )
597   {
598     if( Collection.class.isAssignableFrom(valueClass) ) return true;
599     if( Comparator.class.isAssignableFrom(valueClass) ) return true;
600     if( Enumeration.class.isAssignableFrom(valueClass) ) return true;
601     if( EventListener.class.isAssignableFrom(valueClass) ) return true;
602     if( Iterator.class.isAssignableFrom(valueClass) ) return true;
603     if( List.class.isAssignableFrom(valueClass) ) return true;
604     if( ListIterator.class.isAssignableFrom(valueClass) ) return true;
605     if( Map.class.isAssignableFrom(valueClass) ) return true;
606     if( Set.class.isAssignableFrom(valueClass) ) return true;
607     if( SortedMap.class.isAssignableFrom(valueClass) ) return true;
608     if( SortedSet.class.isAssignableFrom(valueClass) ) return true;
609 
610     return false;
611   }
612 
613   /**
614    * Determines if the given object is a class from the <code>java.lang package</code>
615    *
616    * @param  Object        The object to test.
617    *
618    */
619 
620   public static boolean isJavaLangClass( Object value )
621   {
622     Class valueClass = value.getClass();
623     return isJavaLangClass(valueClass);
624   }
625 
626   /**
627    * Determines if the given class is from the <code>java.lang package</code>.
628    *
629    * @param  Class        The class to test.
630    *
631    */
632 
633   public static boolean isJavaLangClass( Class valueClass )
634   {
635     if( Boolean.class.isAssignableFrom(valueClass) ) return true;
636     if( Byte.class.isAssignableFrom(valueClass) ) return true;
637     if( Character.class.isAssignableFrom(valueClass) ) return true;
638     if( Double.class.isAssignableFrom(valueClass) ) return true;
639     if( Float.class.isAssignableFrom(valueClass) ) return true;
640     if( Integer.class.isAssignableFrom(valueClass) ) return true;
641     if( Long.class.isAssignableFrom(valueClass) ) return true;
642     if( Short.class.isAssignableFrom(valueClass) ) return true;
643     if( String.class.isAssignableFrom(valueClass) ) return true;
644 
645     return false;
646   }
647 }
648