View Javadoc

1   /*
2    * Copyright 2004-2005, 2007-2008 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.util;
17  
18  import java.math.BigDecimal;
19  import java.math.BigInteger;
20  import java.util.Arrays;
21  import java.util.Collections;
22  import java.util.HashMap;
23  import java.util.Map;
24  import java.util.Set;
25  
26  import net.sf.composite.util.ObjectUtils;
27  import net.sf.morph.MorphException;
28  import net.sf.morph.reflect.ReflectionException;
29  import net.sf.morph.transform.TransformationException;
30  
31  /**
32   * Class manipulation utilities.  Note that some code was copied from the
33   * Spring framework.  Some other code was copied from Apache Ant.
34   * 
35   * @author Matt Sgarlata
36   * @author Keith Donald
37   * @author Rob Harrop
38   * @author Juergen Hoeller
39   * @author Matt Benson
40   * @since Nov 6, 2004
41   */
42  public abstract class ClassUtils extends net.sf.composite.util.ClassUtils {
43  
44  	/**
45  	 * All the base array classes.  Multidimensional arrays are subclasses of
46  	 * these fundamental array types.
47  	 */
48  	public static final Class[] ARRAY_TYPES = {
49  		Object[].class,
50  		long[].class,
51  		int[].class,
52  		short[].class,
53  		char[].class,
54  		byte[].class,
55  		double[].class,
56  		float[].class,
57  		boolean[].class
58  	};
59  
60  	private static final Set ALL_CLASSES;
61  	private static final Set IMMUTABLE_TYPES;
62  	private static final Map PRIMITIVE_TYPE_MAP;
63  
64  	static {
65  		//from Ant:
66  		Class[] primitives = {
67  				Boolean.TYPE, Byte.TYPE, Character.TYPE, Short.TYPE, Integer.TYPE,
68  				Long.TYPE, Float.TYPE, Double.TYPE };
69  		Class[] wrappers = {
70  				Boolean.class, Byte.class, Character.class, Short.class, Integer.class,
71  				Long.class, Float.class, Double.class };
72  		Map ptm = new HashMap(8);
73  		for (int i = 0; i < primitives.length; i++) {
74  			ptm.put(primitives[i], wrappers[i]);
75  		}
76  		PRIMITIVE_TYPE_MAP = Collections.unmodifiableMap(ptm);
77  
78  		//we couldn't use all Numbers for immutables even if we wanted to:
79  		//Java 1.6 adds AtomicInteger and AtomicLong, which ARE mutable!
80  		Set immutable = ContainerUtils.createOrderedSet();
81  		immutable.addAll(Arrays.asList(primitives));
82  		immutable.addAll(Arrays.asList(wrappers));
83  		immutable.add(String.class);
84  
85  		//TBD: BigInteger and BigDecimal are not mutable but are also not FINAL:
86  		immutable.add(BigInteger.class);
87  		immutable.add(BigDecimal.class);
88  
89  		immutable.add(null);
90  		immutable.add(Class.class);
91  		IMMUTABLE_TYPES = Collections.unmodifiableSet(immutable);
92  
93  		//add primitives, null, and Objects to ALL_CLASSES:
94  		Set allClasses = ContainerUtils.createOrderedSet();
95  		allClasses.add(Object.class);
96  		allClasses.addAll(Arrays.asList(primitives));
97  		allClasses.add(null);
98  		ALL_CLASSES = Collections.unmodifiableSet(allClasses);
99  	}
100 
101 	/**
102 	 * Returns an array version of the given class (for example, converts Long to Long[]).
103 	 */
104 	public static Class getArrayClass(Class componentType) {
105 		return createArray(componentType, 0).getClass();
106 	}
107 	
108 	/**
109 	 * Returns a new instance of the class denoted by <code>type</code>. The
110 	 * type may be expressed as a Class object, a String or a StringBuffer.
111 	 * 
112 	 * @param type
113 	 *            an object that specifies the class of the new instance
114 	 * @return an instance of the specified class
115 	 * @throws ReflectionException
116 	 *             if a new instance of the requested class could not be created
117 	 * @throws TransformationException
118 	 *             if the class denoted by the given type could not be retrieved
119 	 * @throws IllegalArgumentException
120 	 *             if the type parameter is null or not a Class, String or
121 	 *             StringBuffer
122 	 */
123 	public static Object newInstance(Object type) {
124 		try {
125 			return convertToClass(type).newInstance();
126 		}
127 		catch (MorphException e) {
128 			throw e;
129 		}
130 		catch (IllegalArgumentException e) {
131 			throw e;
132 		}
133 		catch (Exception e) {
134 			throw new ReflectionException("Could not create a new instance of "
135 				+ ObjectUtils.getObjectDescription(type), e);
136 		}
137 	}
138 
139 	/**
140 	 * Converts the given object to a Class object. The conversion will only
141 	 * succeed if the object is a Class, String or StringBuffer.
142 	 * 
143 	 * @param type
144 	 *            an object that specifies the class
145 	 * @return the specified class
146 	 * @throws TransformationException
147 	 *             if the class could not be retrieved for some reason
148 	 * @throws IllegalArgumentException
149 	 *             if the type parameter is null or not a Class, String or
150 	 *             StringBuffer
151 	 */
152 	public static Class convertToClass(Object type) {
153 		if (type == null) {
154 			throw new IllegalArgumentException(
155 				"You must specify the class to instantiate");
156 		}
157 		if (!(type instanceof String || type instanceof StringBuffer || type instanceof Class)) {
158 			throw new IllegalArgumentException(
159 				"The type to be instantiated must be specified as a Class, String or StringBuffer object");
160 		}
161 
162 		try {
163 			return type instanceof Class ? (Class) type : Class.forName(type.toString());
164 		}
165 		catch (Exception e) {
166 			throw new TransformationException(
167 				"Could not convert " + ObjectUtils.getObjectDescription(type)
168 					+ " to a Class object: " + e.getMessage(), e);
169 		}
170 	}
171 	
172 	/**
173 	 * Indicates whether the Servlet API is available.
174 	 * 
175 	 * @return <code>true</code> if the servlet API is available or <br>
176 	 *         <code>false</code> otherwise
177 	 */
178 	public static boolean isServletApiPresent() {
179 		return isClassPresent("javax.servlet.http.HttpServletRequest");
180 	}
181 
182 	/**
183 	 * Indicates whether the JSP API is available.
184 	 * 
185 	 * @return <code>true</code> if the JSP API is available or <br>
186 	 *         <code>false</code> otherwise
187 	 */
188 	public static boolean isJspApiPresent() {
189 		return isClassPresent("javax.servlet.jsp.PageContext");
190 	}
191 
192 	/**
193 	 * Indicates whether the Apache Commons BeanUtils API is available.
194 	 * 
195 	 * @return <code>true</code> if the BeanUtils API is available or <br>
196 	 *         <code>false</code> otherwise
197 	 */
198 	public static boolean isBeanUtilsPresent() {
199 		return isClassPresent("org.apache.commons.beanutils.DynaBean");
200 	}
201 
202 	/**
203 	 * Indicates whether Velocity is available.
204 	 * 
205 	 * @return <code>true</code> if Velocity is available or <br>
206 	 *         <code>false</code> otherwise
207 	 */
208 	public static boolean isVelocityPresent() {
209 		return isClassPresent("org.apache.velocity.VelocityContext");
210 	}
211 
212 	/**
213 	 * Indicates whether Commons Collections 3.x is available on the classpath.
214 	 * 
215 	 * @return <code>true</code> Commons Collections 3.x is available on the
216 	 *         classpath<br>
217 	 *         <code>false</code> otherwise
218 	 */
219 	public static boolean isCommonsCollections3Present() {
220 		return isClassPresent("org.apache.commons.collections.set.ListOrderedSet");
221 	}
222 
223 	/**
224 	 * Determines if <code>type</code> is equal to or a subtype of any of the
225 	 * types in <code>typeArray</code>.
226 	 * 
227 	 * @param type
228 	 *            the type to test
229 	 * @param typeArray
230 	 *            the array of types
231 	 * @return <code>true</code>, if <code>type</code> if <code>type</code>
232 	 *         is equal to or a subtype of any of the types in
233 	 *         <code>typeArray</code> or <br>
234 	 *         <code>false</code>, otherwise
235 	 * @throws IllegalArgumentException
236 	 *             if any of the types in the provided <code>typeArray</code>
237 	 *             are <code>null</code>
238 	 */
239 	public static boolean inheritanceContains(Class[] typeArray, Class type) {
240 		if (typeArray == null) {
241 			return false;
242 		}
243 		for (int i = 0; i < typeArray.length; i++) {
244 			if (type == null) {
245 				if (typeArray[i] == null) {
246 					return true;
247 				}
248 			}
249 			else if (typeArray[i] != null &&
250 				typeArray[i].isAssignableFrom(type)) {
251 				return true;
252 			}
253 		}
254 		return false;
255 	}
256 	
257 	/**
258 	 * Returns the class of the given object.
259 	 * 
260 	 * @param object
261 	 *            the object
262 	 * @return <code>null</code>, if <code>type</code> is <code>null</code>
263 	 *         or <br>
264 	 *         the class of the given object, otherwise
265 	 */
266 	public static Class getClass(Object object) {
267 		return object == null ? null : object.getClass();
268 	}
269 	
270 	/**
271 	 * Determines whether the given <code>destinationType</code> is one of the
272 	 * primitive immutable types provided by the JDK (i.e. a Number or a
273 	 * String).  Note that JDK 1.6 adds AtomicLong and AtomicInteger, which
274 	 * are <em>not</em> immutable.
275 	 * 
276 	 * @param destinationType
277 	 *            the type to examine
278 	 * @return <code>true</code> if the <code>destinationType</code> is an immutable
279 	 *         number or a String or <br>
280 	 *         <code>false</code>, otherwise
281 	 */
282 	public static boolean isImmutable(Class destinationType) {
283 		return IMMUTABLE_TYPES.contains(destinationType);
284 	}
285 
286 	/**
287 	 * Determines whether the given object is an immutable object.
288 	 * @param o
289 	 * @return
290 	 */
291 	public static boolean isImmutableObject(Object o) {
292 		return isImmutable(getClass(o));
293 	}
294 
295 	/**
296 	 * Get the known immutable types.
297 	 * @return Class[]
298 	 */
299 	public static Class[] getImmutableTypes() {
300 		return (Class[]) IMMUTABLE_TYPES.toArray(new Class[IMMUTABLE_TYPES.size()]);
301 	}
302 
303 	/**
304 	 * Get the wrapper type for the specified class (if any).
305 	 * @param c a (presumably primitive) Class.
306 	 * @return the wrapper class for <code>c</code>, if <code>c</code> is primitive, else null. 
307 	 */
308 	public static Class getPrimitiveWrapper(Class c) {
309 		return (Class) PRIMITIVE_TYPE_MAP.get(c);
310 	}
311 
312 	/**
313 	 * Get all the primitive classes.
314 	 * @return Class[]
315 	 */
316 	public static Class[] getPrimitiveTypes() {
317 		return (Class[]) PRIMITIVE_TYPE_MAP.keySet().toArray(new Class[PRIMITIVE_TYPE_MAP.size()]);
318 	}
319 
320 	/**
321 	 * Get all the primitive wrapper classes.
322 	 * @return Class[]
323 	 */
324 	public static Class[] getWrapperTypes() {
325 		return (Class[]) PRIMITIVE_TYPE_MAP.values().toArray(new Class[PRIMITIVE_TYPE_MAP.size()]);
326 	}
327 
328 	/**
329 	 * Returns the set of classes for which any class will match.
330 	 * @return Class[]
331 	 */
332 	public static Class[] getAllClasses() {
333 		return (Class[]) ALL_CLASSES.toArray(new Class[ALL_CLASSES.size()]);
334 	}
335 
336 //	public static Class inheritanceIntersection(Class[] types) {
337 //	Assert.contentsNotNull(types);
338 //	
339 //	if (ObjectUtils.isEmpty(types) || types.length == 1) {
340 //		return types;
341 //	}
342 //	
343 //	// types.length >= 2
344 //	Class type = types[0];
345 //	for (int i=1; i<types.length; i++) {
346 //		Class nextType = 
347 //	}
348 //	
349 //	else if (types.length == 2) {
350 //		Class type1 = types[0];
351 //		Class type2 = types[1];
352 //		if (type1.isAssignableFrom(type2)) {
353 //			return type2;
354 //		}
355 //	}
356 //	else { //types.length >= 3
357 //		Arrays.
358 //		return inheritanceIntersection()
359 //	}
360 //	// if we get to here, types.length >= 2
361 //	
362 //}
363 
364 }