View Javadoc

1   /*
2    * Copyright 2006-2007-2007 the original author or authors.
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  
17  package net.sf.ezmorph.bean;
18  
19  import java.beans.PropertyDescriptor;
20  import java.lang.reflect.InvocationTargetException;
21  import java.util.Collection;
22  import java.util.Map;
23  
24  import net.sf.ezmorph.MorphException;
25  import net.sf.ezmorph.MorpherRegistry;
26  import net.sf.ezmorph.ObjectMorpher;
27  import net.sf.ezmorph.object.IdentityObjectMorpher;
28  
29  import org.apache.commons.beanutils.DynaBean;
30  import org.apache.commons.beanutils.DynaProperty;
31  import org.apache.commons.beanutils.PropertyUtils;
32  import org.apache.commons.logging.Log;
33  import org.apache.commons.logging.LogFactory;
34  
35  /**
36   * Converts a JavaBean into another JavaBean or DynaBean.<br>
37   * This Morpher will try to match every property from the target JavaBean's
38   * class to the properties of the source JavaBean. If any target property
39   * differs in type from the source property, it will try to morph it. If a
40   * Morpher is not found for that type, the conversion will be aborted with a
41   * MorphException; this may be changed by setting the Morpher to be lenient, in
42   * that way it will ignore the property (the resulting value will be null).
43   *
44   * @author Andres Almiray <aalmiray@users.sourceforge.net>
45   */
46  public final class BeanMorpher implements ObjectMorpher
47  {
48     private static final Log log = LogFactory.getLog( BeanMorpher.class );
49     private final Class beanClass;
50     private boolean lenient;
51     private final MorpherRegistry morpherRegistry;
52  
53     /**
54      * @param beanClass the target class to morph to
55      * @param morpherRegistry a registry of morphers
56      */
57     public BeanMorpher( Class beanClass, MorpherRegistry morpherRegistry )
58     {
59        this( beanClass, morpherRegistry, false );
60     }
61  
62     /**
63      * @param beanClass the target class to morph to
64      * @param morpherRegistry a registry of morphers
65      * @param lenient if an exception should be raised if no morpher is found for
66      *        a target property
67      */
68     public BeanMorpher( Class beanClass, MorpherRegistry morpherRegistry, boolean lenient )
69     {
70        validateClass( beanClass );
71        if( morpherRegistry == null ){
72           throw new MorphException( "morpherRegistry is null" );
73        }
74        this.beanClass = beanClass;
75        this.morpherRegistry = morpherRegistry;
76        this.lenient = lenient;
77     }
78  
79     public Object morph( Object sourceBean )
80     {
81        if( sourceBean == null ){
82           return null;
83        }
84        if( !supports( sourceBean.getClass() ) ){
85           throw new MorphException( "unsupported class: " + sourceBean.getClass()
86                 .getName() );
87        }
88  
89        Object targetBean = null;
90  
91        try{
92           targetBean = beanClass.newInstance();
93           PropertyDescriptor[] targetPds = PropertyUtils.getPropertyDescriptors( beanClass );
94           for( int i = 0; i < targetPds.length; i++ ){
95              PropertyDescriptor targetPd = targetPds[i];
96              String name = targetPd.getName();
97              if( targetPd.getWriteMethod() == null ){
98                 log.info( "Property '" + beanClass.getName() + "." + name
99                       + "' has no write method. SKIPPED." );
100                continue;
101             }
102 
103             Class sourceType = null;
104             if( sourceBean instanceof DynaBean ){
105                DynaBean dynaBean = (DynaBean) sourceBean;
106                DynaProperty dynaProperty = dynaBean.getDynaClass()
107                      .getDynaProperty( name );
108                if( dynaProperty == null ){
109                   log.warn( "DynaProperty '" + name + "' does not exist. SKIPPED." );
110                   continue;
111                }
112                sourceType = dynaProperty.getType();
113             }else{
114                PropertyDescriptor sourcePd = PropertyUtils.getPropertyDescriptor( sourceBean, name );
115                if( sourcePd == null ){
116                   log.warn( "Property '" + sourceBean.getClass()
117                         .getName() + "." + name + "' does not exist. SKIPPED." );
118                   continue;
119                }else if( sourcePd.getReadMethod() == null ){
120                   log.warn( "Property '" + sourceBean.getClass()
121                         .getName() + "." + name + "' has no read method. SKIPPED." );
122                   continue;
123                }
124                sourceType = sourcePd.getPropertyType();
125             }
126 
127             Class targetType = targetPd.getPropertyType();
128             Object value = PropertyUtils.getProperty( sourceBean, name );
129             setProperty( targetBean, name, sourceType, targetType, value );
130          }
131       }
132       catch( MorphException me ){
133          throw me;
134       }
135       catch( Exception e ){
136          throw new MorphException( e );
137       }
138 
139       return targetBean;
140    }
141 
142    public Class morphsTo()
143    {
144       return beanClass;
145    }
146 
147    public boolean supports( Class clazz )
148    {
149       return !clazz.isArray();
150    }
151 
152    private void setProperty( Object targetBean, String name, Class sourceType, Class targetType,
153          Object value ) throws IllegalAccessException, InvocationTargetException,
154          NoSuchMethodException
155    {
156       if( targetType.isAssignableFrom( sourceType ) ){
157          if( value == null && targetType.isPrimitive() ){
158             value = morpherRegistry.morph( targetType, value );
159          }
160          PropertyUtils.setProperty( targetBean, name, value );
161       }else{
162          if( targetType.equals( Object.class ) ){
163             // no conversion
164             PropertyUtils.setProperty( targetBean, name, value );
165          }else{
166             if( value == null ){
167                if( targetType.isPrimitive() ){
168                   PropertyUtils.setProperty( targetBean, name, morpherRegistry.morph( targetType,
169                         value ) );
170                }
171             }else{
172                if( IdentityObjectMorpher.getInstance() == morpherRegistry.getMorpherFor( targetType ) ){
173                   if( !lenient ){
174                      throw new MorphException( "Can't find a morpher for target class "
175                            + targetType.getName() + " (" + name + ")" );
176                   }else{
177                      log.info( "Can't find a morpher for target class " + targetType.getName()
178                            + " (" + name + ") SKIPPED" );
179                   }
180                }else{
181                   PropertyUtils.setProperty( targetBean, name, morpherRegistry.morph( targetType,
182                         value ) );
183                }
184             }
185          }
186       }
187    }
188 
189    private void validateClass( Class clazz )
190    {
191       if( clazz == null ){
192          throw new MorphException( "target class is null" );
193       }else if( clazz.isPrimitive() ){
194          throw new MorphException( "target class is a primitive" );
195       }else if( clazz.isArray() ){
196          throw new MorphException( "target class is an array" );
197       }else if( clazz.isInterface() ){
198          throw new MorphException( "target class is an interface" );
199       }else if( DynaBean.class.isAssignableFrom( clazz ) ){
200          throw new MorphException( "target class is a DynaBean" );
201       }else if( Number.class.isAssignableFrom( clazz ) || Boolean.class.isAssignableFrom( clazz )
202             || Character.class.isAssignableFrom( clazz ) ){
203          throw new MorphException( "target class is a wrapper" );
204       }else if( String.class.isAssignableFrom( clazz ) ){
205          throw new MorphException( "target class is a String" );
206       }else if( Collection.class.isAssignableFrom( clazz ) ){
207          throw new MorphException( "target class is a Collection" );
208       }else if( Map.class.isAssignableFrom( clazz ) ){
209          throw new MorphException( "target class is a Map" );
210       }
211    }
212 }