View Javadoc

1   /*
2    * Copyright 2004-2005, 2007 the original author or authors.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5    * use this file except in compliance with the License. You may obtain a copy of
6    * 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, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations under
14   * the License.
15   */
16  package net.sf.morph.reflect.reflectors;
17  
18  import java.lang.reflect.Modifier;
19  import java.util.Calendar;
20  import java.util.GregorianCalendar;
21  import java.util.Map;
22  import java.util.Set;
23  
24  import net.sf.composite.util.ObjectUtils;
25  import net.sf.morph.reflect.InstantiatingReflector;
26  import net.sf.morph.reflect.ReflectionException;
27  import net.sf.morph.util.Assert;
28  import net.sf.morph.util.ClassUtils;
29  import net.sf.morph.util.TransformerUtils;
30  import net.sf.morph.util.TypeMap;
31  
32  /**
33   * A basic instantiating reflector that allows an arbitrary number of requested
34   * class types to be mapped to instantiated types.  The default instance of this
35   * class can instantiate common interfaces such as {@link java.util.Calendar}
36   * and {@link java.lang.CharSequence}.
37   *
38   * @author Matt Sgarlata
39   * @since Feb 27, 2005
40   */
41  public class SimpleInstantiatingReflector extends BaseReflector implements InstantiatingReflector {
42  
43  	private Map requestedToInstantiatedTypeMap;
44  
45  	/**
46  	 * Create a new SimpleInstantiatingReflector.
47  	 */
48  	public SimpleInstantiatingReflector() {
49  		super();
50  	}
51  
52  	/**
53  	 * Create a new SimpleInstantiatingReflector for a single type.
54  	 * @param instantiatedType
55  	 */
56  	public SimpleInstantiatingReflector(Class instantiatedType) {
57  		this(instantiatedType, instantiatedType);
58  	}
59  
60  	/**
61  	 * Create a new SimpleInstantiatingReflector for a single type mapping.
62  	 * @param requestedType
63  	 * @param instantiatedType
64  	 */
65  	public SimpleInstantiatingReflector(Class requestedType, Class instantiatedType) {
66  		super();
67  		setRequestedType(requestedType);
68  		setInstantiatedType(instantiatedType);
69  	}
70  
71  	/**
72  	 * Create a new SimpleInstantiatingReflector for the specified type map.
73  	 * @param requestedToInstantiatedClassMap
74  	 */
75  	public SimpleInstantiatingReflector(Map requestedToInstantiatedClassMap) {
76  		super();
77  		this.requestedToInstantiatedTypeMap = requestedToInstantiatedClassMap;
78  	}
79  
80  	/**
81  	 * {@inheritDoc}
82  	 * @see net.sf.morph.reflect.reflectors.BaseReflector#getReflectableClassesImpl()
83  	 */
84  	protected Class[] getReflectableClassesImpl() throws Exception {
85  		Set reflectableClasses = getRequestedToInstantiatedTypeMap().keySet();
86  		return (Class[]) reflectableClasses.toArray(new Class[reflectableClasses.size()]);
87  	}
88  
89  	/**
90  	 * {@inheritDoc}
91  	 * @see net.sf.morph.reflect.reflectors.BaseReflector#newInstanceImpl(java.lang.Class, java.lang.Object)
92  	 */
93  	protected Object newInstanceImpl(Class requestedType, Object parameters) throws Exception {
94  		Class typeToInstantiate = TransformerUtils.getMappedDestinationType(
95  				getRequestedToInstantiatedTypeMap(), requestedType);
96  		if (typeToInstantiate == null) {
97  			throw new Exception("Unable to instantiate " + requestedType);
98  		}
99  		return super.newInstanceImpl(typeToInstantiate, parameters);
100 	}
101 
102 	/**
103 	 * Returns the instantiated type if only a single mapping of requested type
104 	 * to instantiated type has been specified.
105 	 *
106 	 * @return the instantiated type, if one has been specified or <br>
107 	 *         <code>null</code> if no instantiated type has been specified
108 	 * @throws IllegalStateException
109 	 *             if there is more than one mapping of requested type to
110 	 *             instantiated type
111 	 */
112 	public final Class getInstantiatedType() {
113 		if (ObjectUtils.isEmpty(getRequestedToInstantiatedTypeMap())) {
114 			return null;
115 		}
116 		if (getRequestedToInstantiatedTypeMap().size() == 1) {
117 			return (Class) getRequestedToInstantiatedTypeMap().values().iterator().next();
118 		}
119 		throw new IllegalStateException("This reflector has multiple "
120 			+ "mappings of requested types to instantiated types, so a "
121 			+ "single instantiated type cannot be returned.  The mappings "
122 			+ "are " + getRequestedToInstantiatedTypeMap());
123 	}
124 
125 	/**
126 	 * Sets the instantiated type if only a single mapping of requested type to
127 	 * instantiated type is needed.
128 	 *
129 	 * @param instantiatedType
130 	 *            the instantiated type
131 	 */
132 	public final void setInstantiatedType(Class instantiatedType) {
133 		if (instantiatedType == null || instantiatedType.isInterface() || Modifier.isAbstract(instantiatedType.getModifiers())) {
134 			throw new IllegalArgumentException("instantiatedType " + instantiatedType);
135 		}
136 		Class requestedType;
137 		try {
138 			requestedType = getRequestedType();
139 		}
140 		catch (IllegalStateException e) {
141 			requestedType = null;
142 		}
143 		getRequestedToInstantiatedTypeMap().clear();
144 		getRequestedToInstantiatedTypeMap().put(requestedType, instantiatedType);
145 	}
146 
147 	/**
148 	 * Returns the requested type if only a single mapping of requested type to
149 	 * instantiated type has been specified.
150 	 *
151 	 * @return the requested type, if one has been specified or <br>
152 	 *         <code>null</code> if no requested type has been specified
153 	 * @throws IllegalStateException
154 	 *             if there is more than one mapping of requested type to
155 	 *             instantiated type
156 	 */
157 	public final Class getRequestedType() {
158 		if (ObjectUtils.isEmpty(getRequestedToInstantiatedTypeMap())) {
159 			return null;
160 		}
161 		if (getRequestedToInstantiatedTypeMap().size() == 1) {
162 			return (Class) getRequestedToInstantiatedTypeMap().keySet().iterator().next();
163 		}
164 		throw new IllegalStateException("This reflector has multiple "
165 			+ "mappings of requested types to instantiated types, so a "
166 			+ "single requested type cannot be returned.  The mappings "
167 			+ "are " + getRequestedToInstantiatedTypeMap());
168 	}
169 
170 	/**
171 	 * Sets the requested type if only a single mapping of requested type to
172 	 * instantiated type is needed.
173 	 *
174 	 * @param requestedType
175 	 *            the requested type
176 	 */
177 	public final void setRequestedType(Class requestedType) {
178 		Assert.notNull(requestedType, "requestedType");
179 		Class instantiatedType;
180 		try {
181 			instantiatedType = getInstantiatedType();
182 		}
183 		catch (IllegalStateException e) {
184 			instantiatedType = null;
185 		}
186 		getRequestedToInstantiatedTypeMap().clear();
187 		getRequestedToInstantiatedTypeMap().put(requestedType, instantiatedType);
188 	}
189 
190 	/**
191 	 * Returns the mapping of requested types to instantiated types.
192 	 *
193 	 * @return the mapping of requested types to instantiated types
194 	 */
195 	public synchronized Map getRequestedToInstantiatedTypeMap() {
196 		if (requestedToInstantiatedTypeMap == null) {
197 			Map map = new TypeMap();
198 			map.put(Calendar.class, GregorianCalendar.class);
199 
200 			if (ClassUtils.isJdk14OrHigherPresent()) {
201 				try {
202 					map.put(Class.forName("java.lang.CharSequence"), StringBuffer.class);
203 				}
204 				// this shouldn't ever happen because we explicitly check that the
205 				// CharSequence class exists before requesting a reference to it
206 				catch (ClassNotFoundException e) {
207 					throw new ReflectionException(e);
208 				}
209 			}
210 			setRequestedToInstantiatedTypeMap(map);
211 		}
212 		return requestedToInstantiatedTypeMap;
213 	}
214 
215 	/**
216 	 * Sets the mapping of requested types to instantiated types.
217 	 *
218 	 * @param interfaceToImplementationMap
219 	 *            the mapping of requested types to instantiated types
220 	 */
221 	public synchronized void setRequestedToInstantiatedTypeMap(
222 		Map interfaceToImplementationMap) {
223 		this.requestedToInstantiatedTypeMap = new TypeMap(interfaceToImplementationMap);
224 	}
225 }