1 /*
2 * Copyright 2006-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;
18
19 import java.io.Serializable;
20 import java.lang.reflect.Method;
21 import java.util.ArrayList;
22 import java.util.HashMap;
23 import java.util.Iterator;
24 import java.util.List;
25 import java.util.Map;
26
27 import net.sf.ezmorph.object.IdentityObjectMorpher;
28
29 /**
30 * Convenient class that manages Morphers.<br>
31 * A MorpherRehistry manages a group of Morphers. A Morpher will always be
32 * associated with a target class, it is possible to have several Morphers
33 * registered for a target class, if this is the case, the first Morpher will be
34 * used when performing a conversion and no specific Morpher is selected in
35 * advance.<br>
36 * {@link MorphUtils} may be used to register standard Morphers for primitive
37 * types and primitive wrappers, as well as arrays of those types.
38 *
39 * @author Andres Almiray <aalmiray@users.sourceforge.net>
40 */
41 public class MorpherRegistry implements Serializable
42 {
43 private static final long serialVersionUID = -3894767123320768419L;
44 private Map morphers = new HashMap();
45
46 public MorpherRegistry()
47 {
48
49 }
50
51 /**
52 * Deregisters all morphers.
53 */
54 public synchronized void clear()
55 {
56 morphers.clear();
57 }
58
59 /**
60 * Deregister all Morphers of a type.<br>
61 *
62 * @param class the target type the Morphers morph to
63 */
64 public synchronized void clear( Class type )
65 {
66 List registered = (List) morphers.get( type );
67 if( registered != null ){
68 morphers.remove( type );
69 }
70 }
71
72 /**
73 * Deregister the specified Morpher.<br>
74 * The registry will remove the target <code>Class</code> from the morphers
75 * Map if it has no other registered morphers.
76 *
77 * @param morpher the target Morpher to remove
78 */
79 public synchronized void deregisterMorpher( Morpher morpher )
80 {
81 List registered = (List) morphers.get( morpher.morphsTo() );
82 if( registered != null && !registered.isEmpty() ){
83 registered.remove( morpher );
84 if( registered.isEmpty() ){
85 morphers.remove( morpher.morphsTo() );
86 }
87 }
88 }
89
90 /**
91 * Returns a morpher for <code>clazz</code>.<br>
92 * If several morphers are found for that class, it returns the first. If no
93 * Morpher is found it will return the IdentityObjectMorpher.
94 *
95 * @param clazz the target class for which a Morpher may be associated
96 */
97 public synchronized Morpher getMorpherFor( Class clazz )
98 {
99 List registered = (List) morphers.get( clazz );
100 if( registered == null || registered.isEmpty() ){
101 // no morpher registered for clazz
102 return IdentityObjectMorpher.getInstance();
103 }else{
104 return (Morpher) registered.get( 0 );
105 }
106 }
107
108 /**
109 * Returns all morphers for <code>clazz</code>.<br>
110 * If no Morphers are found it will return an array containing the
111 * IdentityObjectMorpher.
112 *
113 * @param clazz the target class for which a Morpher or Morphers may be
114 * associated
115 */
116 public synchronized Morpher[] getMorphersFor( Class clazz )
117 {
118 List registered = (List) morphers.get( clazz );
119 if( registered == null || registered.isEmpty() ){
120 // no morphers registered for clazz
121 return new Morpher[] { IdentityObjectMorpher.getInstance() };
122 }else{
123 Morpher[] morphs = new Morpher[registered.size()];
124 int k = 0;
125 for( Iterator i = registered.iterator(); i.hasNext(); ){
126 morphs[k++] = (Morpher) i.next();
127 }
128 return morphs;
129 }
130 }
131
132 /**
133 * Morphs and object to the specified target class.<br>
134 * This method uses reflection to invoke primitive Morphers and Morphers that
135 * do not implement ObjectMorpher.
136 *
137 * @param target the target class to morph to
138 * @param value the value to morph
139 * @return an instance of the target class if a suitable Morpher was found
140 * @throws MorphException if an error occurs during the conversion
141 */
142 public Object morph( Class target, Object value )
143 {
144 if( value == null ){
145 // give the first morpher in the list a shot to convert
146 // the value as we can't access type information on it
147 Morpher morpher = getMorpherFor( target );
148 if( morpher instanceof ObjectMorpher ){
149 return ((ObjectMorpher) morpher).morph( value );
150 }else{
151 try{
152 Method morphMethod = morpher.getClass()
153 .getDeclaredMethod( "morph", new Class[] { Object.class } );
154 return morphMethod.invoke( morpher, new Object[] { value } );
155 }
156 catch( Exception e ){
157 throw new MorphException( e );
158 }
159 }
160 }
161
162 Morpher[] morphers = getMorphersFor( target );
163 for( int i = 0; i < morphers.length; i++ ){
164 Morpher morpher = morphers[i];
165 if( morpher.supports( value.getClass() ) ){
166 if( morpher instanceof ObjectMorpher ){
167 return ((ObjectMorpher) morpher).morph( value );
168 }else{
169 try{
170 Method morphMethod = morpher.getClass()
171 .getDeclaredMethod( "morph", new Class[] { Object.class } );
172 return morphMethod.invoke( morpher, new Object[] { value } );
173 }
174 catch( Exception e ){
175 throw new MorphException( e );
176 }
177 }
178 }
179 }
180 return value;
181 }
182
183 /**
184 * Register a Morpher for a target <code>Class</code>.<br>
185 * The target class is the class this Morpher morphs to. If there are another
186 * morphers registered to that class, it will be appended to a List.
187 *
188 * @param morpher a Morpher to register. The method <code>morphsTo()</code>
189 * is used to associate the Morpher to a target Class
190 */
191 public void registerMorpher( Morpher morpher )
192 {
193 registerMorpher( morpher, false );
194 }
195
196 /**
197 * Register a Morpher for a target <code>Class</code>.<br>
198 * The target class is the class this Morpher morphs to. If there are another
199 * morphers registered to that class, it will be appended to a List.
200 *
201 * @param morpher a Morpher to register. The method <code>morphsTo()</code>
202 * is used to associate the Morpher to a target Class
203 * @param override if registering teh Morpher should override all previously
204 * registered morphers for the target type
205 */
206 public synchronized void registerMorpher( Morpher morpher, boolean override )
207 {
208 List registered = (List) morphers.get( morpher.morphsTo() );
209 if( override || registered == null ){
210 registered = new ArrayList();
211 morphers.put( morpher.morphsTo(), registered );
212 }
213 if( !registered.contains( morpher ) ){
214 registered.add( morpher );
215 }
216 }
217 }