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.io.Serializable;
20  import java.lang.reflect.Array;
21  import java.util.ArrayList;
22  import java.util.HashMap;
23  import java.util.List;
24  import java.util.Map;
25  
26  import net.sf.ezmorph.MorphException;
27  import net.sf.ezmorph.MorphUtils;
28  import net.sf.ezmorph.MorpherRegistry;
29  
30  import org.apache.commons.beanutils.DynaBean;
31  import org.apache.commons.beanutils.DynaClass;
32  import org.apache.commons.beanutils.DynaProperty;
33  import org.apache.commons.lang.builder.EqualsBuilder;
34  import org.apache.commons.lang.builder.HashCodeBuilder;
35  import org.apache.commons.lang.builder.ToStringBuilder;
36  import org.apache.commons.lang.builder.ToStringStyle;
37  
38  /**
39   * @author Andres Almiray <aalmiray@users.sourceforge.net>
40   */
41  public final class MorphDynaBean implements DynaBean, Serializable
42  {
43     private static final long serialVersionUID = -605547389232706344L;
44     private MorphDynaClass dynaClass;
45     private Map dynaValues = new HashMap();
46     private MorpherRegistry morpherRegistry;
47  
48     public MorphDynaBean()
49     {
50        this( null );
51     }
52  
53     public MorphDynaBean( MorpherRegistry morpherRegistry )
54     {
55        setMorpherRegistry( morpherRegistry );
56     }
57  
58     public boolean contains( String name, String key )
59     {
60        DynaProperty dynaProperty = getDynaProperty( name );
61  
62        Class type = dynaProperty.getType();
63        if( !Map.class.isAssignableFrom( type ) ){
64           throw new MorphException( "Non-Mapped property name: " + name + " key: " + key );
65        }
66  
67        Object value = dynaValues.get( name );
68        if( value == null ){
69           value = new HashMap();
70           dynaValues.put( name, value );
71        }
72        return ((Map) value).containsKey( key );
73     }
74  
75     public boolean equals( Object obj )
76     {
77        if( this == obj ){
78           return true;
79        }
80  
81        if( obj == null ){
82           return false;
83        }
84  
85        if( !(obj instanceof MorphDynaBean) ){
86           return false;
87        }
88  
89        MorphDynaBean other = (MorphDynaBean) obj;
90        EqualsBuilder builder = new EqualsBuilder().append( this.dynaClass, other.dynaClass );
91        DynaProperty[] props = dynaClass.getDynaProperties();
92        for( int i = 0; i < props.length; i++ ){
93           DynaProperty prop = props[i];
94           builder.append( dynaValues.get( prop.getName() ), dynaValues.get( prop.getName() ) );
95        }
96        return builder.isEquals();
97     }
98  
99     public Object get( String name )
100    {
101       Object value = dynaValues.get( name );
102 
103       if( value != null ){
104          return value;
105       }
106 
107       Class type = getDynaProperty( name ).getType();
108       if( !type.isPrimitive() ){
109          return value;
110       }else{
111          return morpherRegistry.morph( type, value );
112       }
113    }
114 
115    public Object get( String name, int index )
116    {
117       DynaProperty dynaProperty = getDynaProperty( name );
118 
119       Class type = dynaProperty.getType();
120       if( !type.isArray() && !List.class.isAssignableFrom( type ) ){
121          throw new MorphException( "Non-Indexed property name: " + name + " index: " + index );
122       }
123 
124       Object value = dynaValues.get( name );
125 
126       if( value.getClass()
127             .isArray() ){
128          value = Array.get( value, index );
129       }else if( value instanceof List ){
130          value = ((List) value).get( index );
131       }
132 
133       return value;
134    }
135 
136    public Object get( String name, String key )
137    {
138       DynaProperty dynaProperty = getDynaProperty( name );
139 
140       Class type = dynaProperty.getType();
141       if( !Map.class.isAssignableFrom( type ) ){
142          throw new MorphException( "Non-Mapped property name: " + name + " key: " + key );
143       }
144 
145       Object value = dynaValues.get( name );
146       if( value == null ){
147          value = new HashMap();
148          dynaValues.put( name, value );
149       }
150       return ((Map) value).get( key );
151    }
152 
153    public DynaClass getDynaClass()
154    {
155       return this.dynaClass;
156    }
157 
158    public MorpherRegistry getMorpherRegistry()
159    {
160       return morpherRegistry;
161    }
162 
163    public int hashCode()
164    {
165       HashCodeBuilder builder = new HashCodeBuilder().append( dynaClass );
166       DynaProperty[] props = dynaClass.getDynaProperties();
167       for( int i = 0; i < props.length; i++ ){
168          DynaProperty prop = props[i];
169          builder.append( dynaValues.get( prop.getName() ) );
170       }
171       return builder.toHashCode();
172    }
173 
174    public void remove( String name, String key )
175    {
176       DynaProperty dynaProperty = getDynaProperty( name );
177 
178       Class type = dynaProperty.getType();
179       if( !Map.class.isAssignableFrom( type ) ){
180          throw new MorphException( "Non-Mapped property name: " + name + " key: " + key );
181       }
182 
183       Object value = dynaValues.get( name );
184       if( value == null ){
185          value = new HashMap();
186          dynaValues.put( name, value );
187       }
188       ((Map) value).remove( key );
189    }
190 
191    public void set( String name, int index, Object value )
192    {
193       DynaProperty dynaProperty = getDynaProperty( name );
194 
195       Class type = dynaProperty.getType();
196       if( !type.isArray() && !List.class.isAssignableFrom( type ) ){
197          throw new MorphException( "Non-Indexed property name: " + name + " index: " + index );
198       }
199 
200       Object prop = dynaValues.get( name );
201       if( prop == null ){
202          if( List.class.isAssignableFrom( type ) ){
203             prop = new ArrayList();
204          }else{
205             prop = Array.newInstance( type.getComponentType(), index + 1 );
206          }
207          dynaValues.put( name, prop );
208       }
209 
210       if( prop.getClass()
211             .isArray() ){
212          if( index >= Array.getLength( prop ) ){
213             Object tmp = Array.newInstance( type.getComponentType(), index + 1 );
214             System.arraycopy( prop, 0, tmp, 0, Array.getLength( prop ) );
215             prop = tmp;
216             dynaValues.put( name, tmp );
217          }
218          Array.set( prop, index, value );
219       }else if( prop instanceof List ){
220          List l = (List) prop;
221          if( index >= l.size() ){
222             for( int i = l.size(); i <= index + 1; i++ ){
223                l.add( null );
224             }
225          }
226          ((List) prop).set( index, value );
227       }
228    }
229 
230    public void set( String name, Object value )
231    {
232       DynaProperty property = getDynaProperty( name );
233 
234       if( value == null || !isDynaAssignable( property.getType(), value.getClass() ) ){
235          value = morpherRegistry.morph( property.getType(), value );
236       }
237 
238       dynaValues.put( name, value );
239    }
240 
241    public void set( String name, String key, Object value )
242    {
243       DynaProperty dynaProperty = getDynaProperty( name );
244 
245       Class type = dynaProperty.getType();
246       if( !Map.class.isAssignableFrom( type ) ){
247          throw new MorphException( "Non-Mapped property name: " + name + " key: " + key );
248       }
249 
250       Object prop = dynaValues.get( name );
251       if( prop == null ){
252          prop = new HashMap();
253          dynaValues.put( name, prop );
254       }
255       ((Map) prop).put( key, value );
256    }
257 
258    public synchronized void setDynaBeanClass( MorphDynaClass dynaClass )
259    {
260       if( this.dynaClass == null ){
261          this.dynaClass = dynaClass;
262       }
263    }
264 
265    public void setMorpherRegistry( MorpherRegistry morpherRegistry )
266    {
267       if( morpherRegistry == null ){
268          this.morpherRegistry = new MorpherRegistry();
269          MorphUtils.registerStandardMorphers( this.morpherRegistry );
270       }else{
271          this.morpherRegistry = morpherRegistry;
272       }
273    }
274 
275    public String toString()
276    {
277       return new ToStringBuilder( this, ToStringStyle.MULTI_LINE_STYLE ).append( dynaValues )
278             .toString();
279    }
280 
281    protected DynaProperty getDynaProperty( String name )
282    {
283       DynaProperty property = getDynaClass().getDynaProperty( name );
284       if( property == null ){
285          throw new MorphException( "Unspecified property for " + name );
286       }
287       return property;
288    }
289 
290    protected boolean isDynaAssignable( Class dest, Class src )
291    {
292       boolean assignable = dest.isAssignableFrom( src );
293       if( assignable ){
294          return true;
295       }
296       assignable = (dest == Boolean.TYPE && src == Boolean.class) ? true : assignable;
297       assignable = (dest == Byte.TYPE && src == Byte.class) ? true : assignable;
298       assignable = (dest == Character.TYPE && src == Character.class) ? true : assignable;
299       assignable = (dest == Short.TYPE && src == Short.class) ? true : assignable;
300       assignable = (dest == Integer.TYPE && src == Integer.class) ? true : assignable;
301       assignable = (dest == Long.TYPE && src == Long.class) ? true : assignable;
302       assignable = (dest == Float.TYPE && src == Float.class) ? true : assignable;
303       assignable = (dest == Double.TYPE && src == Double.class) ? true : assignable;
304 
305       if( src == Double.TYPE || Double.class.isAssignableFrom( src ) ){
306          assignable = (isByte( dest ) || isShort( dest ) || isInteger( dest ) || isLong( dest ) || isFloat( dest )) ? true
307                : assignable;
308       }
309       if( src == Float.TYPE || Float.class.isAssignableFrom( src ) ){
310          assignable = (isByte( dest ) || isShort( dest ) || isInteger( dest ) || isLong( dest )) ? true
311                : assignable;
312       }
313       if( src == Long.TYPE || Long.class.isAssignableFrom( src ) ){
314          assignable = (isByte( dest ) || isShort( dest ) || isInteger( dest )) ? true : assignable;
315       }
316       if( src == Integer.TYPE || Integer.class.isAssignableFrom( src ) ){
317          assignable = (isByte( dest ) || isShort( dest )) ? true : assignable;
318       }
319       if( src == Short.TYPE || Short.class.isAssignableFrom( src ) ){
320          assignable = (isByte( dest )) ? true : assignable;
321       }
322 
323       return assignable;
324    }
325 
326    private boolean isByte( Class clazz )
327    {
328       return Byte.class.isAssignableFrom( clazz ) || clazz == Byte.TYPE;
329    }
330 
331    private boolean isFloat( Class clazz )
332    {
333       return Float.class.isAssignableFrom( clazz ) || clazz == Float.TYPE;
334    }
335 
336    private boolean isInteger( Class clazz )
337    {
338       return Integer.class.isAssignableFrom( clazz ) || clazz == Integer.TYPE;
339    }
340 
341    private boolean isLong( Class clazz )
342    {
343       return Long.class.isAssignableFrom( clazz ) || clazz == Long.TYPE;
344    }
345 
346    private boolean isShort( Class clazz )
347    {
348       return Short.class.isAssignableFrom( clazz ) || clazz == Short.TYPE;
349    }
350 }