Coverage Report - net.sf.ezmorph.bean.BeanMorpher
 
Classes in this File Line Coverage Branch Coverage Complexity
BeanMorpher
95%
106/111
97%
111/114
7
 
 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  3
 public final class BeanMorpher implements ObjectMorpher
 47  
 {
 48  13
    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  131
       this( beanClass, morpherRegistry, false );
 60  83
    }
 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  102
    public BeanMorpher( Class beanClass, MorpherRegistry morpherRegistry, boolean lenient )
 69  33
    {
 70  135
       validateClass( beanClass );
 71  91
       if( morpherRegistry == null ){
 72  4
          throw new MorphException( "morpherRegistry is null" );
 73  
       }
 74  87
       this.beanClass = beanClass;
 75  87
       this.morpherRegistry = morpherRegistry;
 76  87
       this.lenient = lenient;
 77  87
    }
 78  
 
 79  
    public Object morph( Object sourceBean )
 80  
    {
 81  63
       if( sourceBean == null ){
 82  4
          return null;
 83  
       }
 84  59
       if( !supports( sourceBean.getClass() ) ){
 85  7
          throw new MorphException( "unsupported class: " + sourceBean.getClass()
 86  3
                .getName() );
 87  
       }
 88  
 
 89  55
       Object targetBean = null;
 90  
 
 91  
       try{
 92  55
          targetBean = beanClass.newInstance();
 93  55
          PropertyDescriptor[] targetPds = PropertyUtils.getPropertyDescriptors( beanClass );
 94  471
          for( int i = 0; i < targetPds.length; i++ ){
 95  420
             PropertyDescriptor targetPd = targetPds[i];
 96  420
             String name = targetPd.getName();
 97  420
             if( targetPd.getWriteMethod() == null ){
 98  97
                log.info( "Property '" + beanClass.getName() + "." + name
 99  42
                      + "' has no write method. SKIPPED." );
 100  55
                continue;
 101  
             }
 102  
 
 103  365
             Class sourceType = null;
 104  365
             if( sourceBean instanceof DynaBean ){
 105  57
                DynaBean dynaBean = (DynaBean) sourceBean;
 106  102
                DynaProperty dynaProperty = dynaBean.getDynaClass()
 107  45
                      .getDynaProperty( name );
 108  57
                if( dynaProperty == null ){
 109  15
                   log.warn( "DynaProperty '" + name + "' does not exist. SKIPPED." );
 110  80
                   continue;
 111  77
                }
 112  43
                sourceType = dynaProperty.getType();
 113  
             }else{
 114  232
                PropertyDescriptor sourcePd = PropertyUtils.getPropertyDescriptor( sourceBean, name );
 115  307
                if( sourcePd == null ){
 116  9
                   log.warn( "Property '" + sourceBean.getClass()
 117  6
                         .getName() + "." + name + "' does not exist. SKIPPED." );
 118  3
                   continue;
 119  228
                }else if( sourcePd.getReadMethod() == null ){
 120  76
                   log.warn( "Property '" + sourceBean.getClass()
 121  0
                         .getName() + "." + name + "' has no read method. SKIPPED." );
 122  0
                   continue;
 123  88
                }
 124  316
                sourceType = sourcePd.getPropertyType();
 125  88
             }
 126  
 
 127  270
             Class targetType = targetPd.getPropertyType();
 128  271
             Object value = PropertyUtils.getProperty( sourceBean, name );
 129  271
             setProperty( targetBean, name, sourceType, targetType, value );
 130  
          }
 131  0
       }
 132  3
       catch( MorphException me ){
 133  15
          throw me;
 134  
       }
 135  12
       catch( Exception e ){
 136  0
          throw new MorphException( e );
 137  
       }
 138  
 
 139  39
       return targetBean;
 140  34
    }
 141  
 
 142  
    public Class morphsTo()
 143  
    {
 144  102
       return beanClass;
 145  25
    }
 146  
 
 147  
    public boolean supports( Class clazz )
 148  
    {
 149  78
       return !clazz.isArray();
 150  
    }
 151  
 
 152  88
    private void setProperty( Object targetBean, String name, Class sourceType, Class targetType,
 153  17
          Object value ) throws IllegalAccessException, InvocationTargetException,
 154  0
          NoSuchMethodException
 155  
    {
 156  287
       if( targetType.isAssignableFrom( sourceType ) ){
 157  51
          if( value == null && targetType.isPrimitive() ){
 158  71
             value = morpherRegistry.morph( targetType, value );
 159  
          }
 160  59
          PropertyUtils.setProperty( targetBean, name, value );
 161  
       }else{
 162  282
          if( targetType.equals( Object.class ) ){
 163  37
             // no conversion
 164  40
             PropertyUtils.setProperty( targetBean, name, value );
 165  
          }else{
 166  195
             if( value == null ){
 167  111
                if( targetType.isPrimitive() ){
 168  122
                   PropertyUtils.setProperty( targetBean, name, morpherRegistry.morph( targetType,
 169  50
                         value ) );
 170  1
                }
 171  
             }else{
 172  84
                if( IdentityObjectMorpher.getInstance() == morpherRegistry.getMorpherFor( targetType ) ){
 173  7
                   if( !lenient ){
 174  6
                      throw new MorphException( "Can't find a morpher for target class "
 175  3
                            + targetType.getName() + " (" + name + ")" );
 176  
                   }else{
 177  30
                      log.info( "Can't find a morpher for target class " + targetType.getName()
 178  3
                            + " (" + name + ") SKIPPED" );
 179  
                   }
 180  
                }else{
 181  156
                   PropertyUtils.setProperty( targetBean, name, morpherRegistry.morph( targetType,
 182  78
                         value ) );
 183  87
                }
 184  
             }
 185  
          }
 186  
       }
 187  300
    }
 188  1
 
 189  32
    private void validateClass( Class clazz )
 190  1
    {
 191  133
       if( clazz == null ){
 192  4
          throw new MorphException( "target class is null" );
 193  129
       }else if( clazz.isPrimitive() ){
 194  4
          throw new MorphException( "target class is a primitive" );
 195  125
       }else if( clazz.isArray() ){
 196  4
          throw new MorphException( "target class is an array" );
 197  121
       }else if( clazz.isInterface() ){
 198  3
          throw new MorphException( "target class is an interface" );
 199  93
       }else if( DynaBean.class.isAssignableFrom( clazz ) ){
 200  28
          throw new MorphException( "target class is a DynaBean" );
 201  88
       }else if( Number.class.isAssignableFrom( clazz ) || Boolean.class.isAssignableFrom( clazz )
 202  105
             || Character.class.isAssignableFrom( clazz ) ){
 203  10
          throw new MorphException( "target class is a wrapper" );
 204  101
       }else if( String.class.isAssignableFrom( clazz ) ){
 205  4
          throw new MorphException( "target class is a String" );
 206  75
       }else if( Collection.class.isAssignableFrom( clazz ) ){
 207  25
          throw new MorphException( "target class is a Collection" );
 208  72
       }else if( Map.class.isAssignableFrom( clazz ) ){
 209  3
          throw new MorphException( "target class is a Map" );
 210  
       }
 211  69
    }
 212  
 }