0001: /**********************************************************************
0002: Copyright (c) 2004 Andy Jefferson and others. All rights reserved.
0003: Licensed under the Apache License, Version 2.0 (the "License");
0004: you may not use this file except in compliance with the License.
0005: You may obtain a copy of the License at
0006:
0007: http://www.apache.org/licenses/LICENSE-2.0
0008:
0009: Unless required by applicable law or agreed to in writing, software
0010: distributed under the License is distributed on an "AS IS" BASIS,
0011: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0012: See the License for the specific language governing permissions and
0013: limitations under the License.
0014:
0015:
0016: Contributors:
0017: ...
0018: **********************************************************************/package org.jpox.util;
0019:
0020: import java.io.BufferedInputStream;
0021: import java.io.ByteArrayOutputStream;
0022: import java.io.File;
0023: import java.io.IOException;
0024: import java.io.InputStream;
0025: import java.lang.reflect.Constructor;
0026: import java.lang.reflect.Field;
0027: import java.lang.reflect.InvocationTargetException;
0028: import java.lang.reflect.Method;
0029: import java.lang.reflect.ParameterizedType;
0030: import java.lang.reflect.Type;
0031: import java.net.URL;
0032: import java.security.AccessController;
0033: import java.security.PrivilegedAction;
0034: import java.util.ArrayList;
0035: import java.util.Arrays;
0036: import java.util.Collection;
0037: import java.util.Enumeration;
0038: import java.util.HashSet;
0039: import java.util.Iterator;
0040: import java.util.List;
0041: import java.util.Map;
0042: import java.util.jar.JarEntry;
0043: import java.util.jar.JarFile;
0044:
0045: import org.jpox.ClassLoaderResolver;
0046: import org.jpox.ClassNameConstants;
0047: import org.jpox.TypeManager;
0048: import org.jpox.exceptions.JPOXException;
0049:
0050: /**
0051: * Utilities for handling classes.
0052: * These are to supplement the methods provided by the Class object.
0053: *
0054: * @version $Revision: 1.56 $
0055: **/
0056: public class ClassUtils {
0057: /** Localisation utility for output messages */
0058: protected static final Localiser LOCALISER = Localiser
0059: .getInstance("org.jpox.Localisation");
0060:
0061: /** caching for constructors - using caching, the perf is at least doubled **/
0062: protected static Map constructorsCache = new SoftValueMap();
0063:
0064: /**
0065: * Accessor for a new instance of an object.
0066: * Uses reflection to generate the instance using the passed constructor parameter arguments.
0067: * @param type Type of object (the class).
0068: * @param parameterTypes Classes of params for the constructor
0069: * @param parameters The parameters for the constructor
0070: * @return The object
0071: * @throws JPOXException If an error occurs creating the instance
0072: */
0073: public static Object newInstance(Class type,
0074: Class[] parameterTypes, Object[] parameters) {
0075: Object obj;
0076: try {
0077: String name = "" + type.hashCode();
0078: if (parameterTypes != null) {
0079: for (int i = 0; i < parameterTypes.length; i++) {
0080: name += "-" + parameterTypes[i].hashCode();
0081: }
0082: }
0083: Constructor ctor = (Constructor) constructorsCache
0084: .get(name);
0085: if (ctor == null) {
0086: ctor = type.getConstructor(parameterTypes);
0087: constructorsCache.put(name, ctor);
0088: }
0089: obj = ctor.newInstance(parameters);
0090: } catch (NoSuchMethodException e) {
0091: throw new JPOXException(
0092: LOCALISER.msg("030004", type.getName(), Arrays
0093: .asList(parameterTypes).toString()
0094: + " "
0095: + Arrays.asList(type.getConstructors())
0096: .toString()), new Exception[] { e })
0097: .setFatal();
0098: } catch (IllegalAccessException e) {
0099: throw new JPOXException(LOCALISER.msg("030005", type
0100: .getName()), new Exception[] { e }).setFatal();
0101: } catch (InstantiationException e) {
0102: throw new JPOXException(LOCALISER.msg("030006", type
0103: .getName()), new Exception[] { e }).setFatal();
0104: } catch (InvocationTargetException e) {
0105: Throwable t = e.getTargetException();
0106: if (t instanceof RuntimeException) {
0107: throw (RuntimeException) t;
0108: } else if (t instanceof Error) {
0109: throw (Error) t;
0110: } else {
0111: throw new JPOXException(LOCALISER.msg("030007", type
0112: .getName(), t)).setFatal();
0113: }
0114: }
0115: return obj;
0116: }
0117:
0118: /**
0119: * Convenience method to return the constructor of the passed class that accepts the supplied
0120: * argument types. Allows for primitive to primitive wrapper conversion. Typically used by
0121: * the JDOQL ResultClass mapping process.
0122: * @param cls The class
0123: * @param types The constructor argument types
0124: * @return The constructor
0125: */
0126: public static Constructor getConstructorWithArguments(Class cls,
0127: Class[] types) {
0128: try {
0129: Constructor[] constructors = cls.getConstructors();
0130: if (constructors != null) {
0131: // Check the types of the constructor and find one that matches allowing for
0132: // primitive to wrapper conversion
0133: for (int i = 0; i < constructors.length; i++) {
0134: Class[] ctrParams = constructors[i]
0135: .getParameterTypes();
0136: boolean ctrIsValid = true;
0137:
0138: // Discard any constructor with a different number of params
0139: if (ctrParams != null
0140: && ctrParams.length == types.length) {
0141: for (int j = 0; j < ctrParams.length; j++) {
0142: // Compare the type with the object or any primitive
0143: Class primType = ClassUtils
0144: .getPrimitiveTypeForType(types[j]);
0145: if (types[j] == null
0146: && ctrParams[j].isPrimitive()) {
0147: // Null type for field so has to accept nulls, and primitives don't do that
0148: ctrIsValid = false;
0149: break;
0150: } else if (types[j] != null
0151: && ctrParams[j] != types[j]
0152: && (primType == null || primType != null
0153: && ctrParams[j] != primType)) {
0154: // Different type in this parameter position
0155: ctrIsValid = false;
0156: break;
0157: }
0158: }
0159: } else {
0160: ctrIsValid = false;
0161: }
0162:
0163: if (ctrIsValid) {
0164: return constructors[i];
0165: }
0166: }
0167: }
0168: } catch (SecurityException se) {
0169: // Can't access the constructors
0170: }
0171:
0172: return null;
0173: }
0174:
0175: /**
0176: * Obtain a method from a class or superclasses using reflection.
0177: * The method will have the specified name and will take a single argument.
0178: * Allows for the argument type being primitive or its associated wrapper.
0179: * @param cls the class to find the declared fields and populate the map
0180: * @param methodName the method name to find
0181: * @param argType the argument type
0182: */
0183: public static Method getMethodWithArgument(Class cls,
0184: String methodName, Class argType) {
0185: Method m = ClassUtils.getMethodForClass(cls, methodName,
0186: new Class[] { argType });
0187: if (m == null) {
0188: Class primitive = ClassUtils
0189: .getPrimitiveTypeForType(argType);
0190: if (primitive != null) {
0191: m = ClassUtils.getMethodForClass(cls, methodName,
0192: new Class[] { primitive });
0193: }
0194: }
0195: return m;
0196: }
0197:
0198: /**
0199: * Obtain a method from a class or superclasses using reflection
0200: * @param cls the class where to look for the method
0201: * @param methodName the method name to find
0202: * @param argtypes the classes argument of the method
0203: * @return The Method
0204: */
0205: public static Method getMethodForClass(Class cls,
0206: String methodName, Class[] argtypes) {
0207: try {
0208: return cls.getDeclaredMethod(methodName, argtypes);
0209: } catch (NoSuchMethodException e) {
0210: if (cls.getSuperclass() != null) {
0211: return ClassUtils.getMethodForClass(
0212: cls.getSuperclass(), methodName, argtypes);
0213: }
0214: } catch (Exception e) {
0215: // do nothing
0216: }
0217: return null;
0218: }
0219:
0220: /**
0221: * Utility to perform the same function as the JDK 1.4 method
0222: * Boolean.valueOf(boolean), so we have no real dependency on JDK 1.4.
0223: * Please remove when our minimum requirement is JDK1.4 or above.
0224: * @param value The value
0225: * @return The Boolean value
0226: */
0227: public static Boolean booleanValueOf(boolean value) {
0228: if (value) {
0229: return Boolean.TRUE;
0230: } else {
0231: return Boolean.FALSE;
0232: }
0233: }
0234:
0235: /**
0236: * Utility to get the Class name given the file name and the file name of
0237: * the root of the package structure.
0238: * @param filename The filename of the class file
0239: * @param rootfilename The filename of the root of the package structure
0240: * @return The Class name
0241: **/
0242: public static String getClassnameForFilename(String filename,
0243: String rootfilename) {
0244: if (filename == null) {
0245: return null;
0246: }
0247:
0248: String classname = filename;
0249:
0250: // Omit the root part of the filename
0251: if (rootfilename != null) {
0252: classname = classname.substring(rootfilename.length());
0253: }
0254:
0255: // Remove the ".class" suffix
0256: classname = classname.substring(0, classname.length() - 6);
0257:
0258: String file_separator = System.getProperty("file.separator");
0259:
0260: // Remove any leading separator
0261: if (classname.indexOf(file_separator) == 0) {
0262: classname = classname.substring(file_separator.length());
0263: }
0264:
0265: // Change all separator characters to "."
0266: classname = StringUtils.replaceAll(classname, file_separator,
0267: ".");
0268:
0269: return classname;
0270: }
0271:
0272: /**
0273: * Method to return the class files below the specified directory.
0274: * @param dir The directory
0275: * @param normal_classes Whether to include normal classes
0276: * @param inner_classes Whether to include inner classes
0277: * @return The class files (Collection of File objects).
0278: **/
0279: public static Collection getClassFilesForDirectory(File dir,
0280: boolean normal_classes, boolean inner_classes) {
0281: if (dir == null) {
0282: return null;
0283: }
0284:
0285: Collection classes = new HashSet();
0286: File[] files = dir.listFiles();
0287: if (files != null) {
0288: for (int i = 0; i < files.length; i++) {
0289: if (files[i].isFile()) {
0290: // If this is a class file, add it
0291: if (files[i].getName().endsWith(".class")) {
0292: boolean is_inner_class = isInnerClass(files[i]
0293: .getName());
0294: if ((normal_classes && !is_inner_class)
0295: || (inner_classes && is_inner_class)) {
0296: classes.add(files[i]);
0297: }
0298: }
0299: } else {
0300: // Check for classes in subdirectories
0301: Collection child_classes = getClassFilesForDirectory(
0302: files[i], normal_classes, inner_classes);
0303: if (child_classes != null
0304: && child_classes.size() > 0) {
0305: classes.addAll(child_classes);
0306: }
0307: }
0308: }
0309: }
0310:
0311: return classes;
0312: }
0313:
0314: /**
0315: * Convenience accessor for the names of all class files in the jar file with the specified name.
0316: * The returned class names are of the form "org.jpox.MyClass".
0317: * @param jarFileName Name of the jar file
0318: * @return The class names
0319: */
0320: public static String[] getClassNamesForJarFile(String jarFileName) {
0321: try {
0322: JarFile jar = new JarFile(jarFileName);
0323: return getClassNamesForJarFile(jar);
0324: } catch (IOException ioe) {
0325: JPOXLogger.GENERAL.warn("Error opening the jar file "
0326: + jarFileName + " : " + ioe.getMessage());
0327: }
0328: return null;
0329: }
0330:
0331: /**
0332: * Convenience accessor for the names of all class files in the jar file with the specified URL.
0333: * The returned class names are of the form "org.jpox.MyClass".
0334: * @param jarFileURL URL for the jar file
0335: * @return The class names
0336: */
0337: public static String[] getClassNamesForJarFile(URL jarFileURL) {
0338: File jarFile = new File(jarFileURL.getFile()); // TODO Check for errors
0339: try {
0340: JarFile jar = new JarFile(jarFile);
0341: return getClassNamesForJarFile(jar);
0342: } catch (IOException ioe) {
0343: JPOXLogger.GENERAL.warn("Error opening the jar file "
0344: + jarFileURL.getFile() + " : " + ioe.getMessage());
0345: }
0346: return null;
0347: }
0348:
0349: /**
0350: * Convenience method to return the names of classes specified in the jar file.
0351: * All inner classes are ignored.
0352: * @param jar Jar file
0353: * @return The class names
0354: */
0355: private static String[] getClassNamesForJarFile(JarFile jar) {
0356: Enumeration jarEntries = jar.entries();
0357: HashSet classes = new HashSet();
0358: String file_separator = System.getProperty("file.separator");
0359: while (jarEntries.hasMoreElements()) {
0360: String entry = ((JarEntry) jarEntries.nextElement())
0361: .getName();
0362: if (entry.endsWith(".class")
0363: && !ClassUtils.isInnerClass(entry)) {
0364: String className = entry.substring(0,
0365: entry.length() - 6); // Omit ".class"
0366: className = StringUtils.replaceAll(className,
0367: file_separator, ".");
0368: classes.add(className);
0369: }
0370: }
0371: return (String[]) classes.toArray(new String[classes.size()]);
0372: }
0373:
0374: /**
0375: * Convenience accessor for the names of all "package.jdo" files in the jar file with the specified name.
0376: * @param jarFileName Name of the jar file
0377: * @return The "package.jdo" file names
0378: */
0379: public static String[] getPackageJdoFilesForJarFile(
0380: String jarFileName) {
0381: try {
0382: JarFile jar = new JarFile(jarFileName);
0383: return getFileNamesWithSuffixForJarFile(jar, "package.jdo");
0384: } catch (IOException ioe) {
0385: JPOXLogger.GENERAL.warn("Error opening the jar file "
0386: + jarFileName + " : " + ioe.getMessage());
0387: }
0388: return null;
0389: }
0390:
0391: /**
0392: * Convenience accessor for the names of all "package.jdo" files in the jar file with the specified URL.
0393: * @param jarFileURL URL for the jar file
0394: * @return The "package.jdo" file names
0395: */
0396: public static String[] getPackageJdoFilesForJarFile(URL jarFileURL) {
0397: File jarFile = new File(jarFileURL.getFile()); // TODO Check for errors
0398: try {
0399: JarFile jar = new JarFile(jarFile);
0400: return getFileNamesWithSuffixForJarFile(jar, "package.jdo");
0401: } catch (IOException ioe) {
0402: JPOXLogger.GENERAL.warn("Error opening the jar file "
0403: + jarFileURL.getFile() + " : " + ioe.getMessage());
0404: }
0405: return null;
0406: }
0407:
0408: /**
0409: * Convenience method to return the names of files specified in the jar file that end with
0410: * the specified suffix.
0411: * @param jar Jar file
0412: * @param suffix Suffix for the file (can be the filename without the path)
0413: * @return The fully-qualified names of the files with this suffix in the jar file
0414: */
0415: private static String[] getFileNamesWithSuffixForJarFile(
0416: JarFile jar, String suffix) {
0417: Enumeration jarEntries = jar.entries();
0418: HashSet files = new HashSet();
0419: while (jarEntries.hasMoreElements()) {
0420: String entry = ((JarEntry) jarEntries.nextElement())
0421: .getName();
0422: if (entry.endsWith(suffix)) {
0423: files.add(entry);
0424: }
0425: }
0426: return (String[]) files.toArray(new String[files.size()]);
0427: }
0428:
0429: /**
0430: * Method to check whether a classname is for an inner class.
0431: * Currently checks for the presence of $ in the name.
0432: * @param class_name The class name
0433: * @return Whether it is an inner class
0434: **/
0435: public static boolean isInnerClass(String class_name) {
0436: if (class_name == null) {
0437: return false;
0438: } else if (class_name.indexOf('$') >= 0) {
0439: return true;
0440: }
0441: return false;
0442: }
0443:
0444: /**
0445: * Method to check for a default constructor on a class.
0446: * Particular relevance for JDO is the requirement for a default
0447: * constructor on all Persistence-Capable classes. Doesn't check
0448: * superclasses for the default constructor.
0449: * @param the_class The class
0450: * @return Whether it has a default constructor
0451: **/
0452: public static boolean hasDefaultConstructor(Class the_class) {
0453: if (the_class == null) {
0454: return false;
0455: }
0456: try {
0457: the_class.getDeclaredConstructor(null);
0458: } catch (Exception e) {
0459: return false;
0460: }
0461:
0462: return true;
0463: }
0464:
0465: /**
0466: * Method to return the superclasses for a class.
0467: * The superclasses will be ordered.
0468: * @param the_class The class
0469: * @return The superclass of this class.
0470: **/
0471: public static Collection getSuperclasses(Class the_class) {
0472: List super classes = new ArrayList();
0473:
0474: Class c = the_class;
0475: boolean more_super classes = true;
0476: while (more_super classes) {
0477: Class super class = c.getSuperclass();
0478: if (super class != null) {
0479: super classes.add(super class);
0480: c = super class;
0481: } else {
0482: more_super classes = false;
0483: }
0484: }
0485: return super classes;
0486: }
0487:
0488: /**
0489: * Method to return the superinterfaces for a class.
0490: * The superinterfaces will be ordered.
0491: * @param the_class The class
0492: * @return The superinterfaces of this class.
0493: **/
0494: public static Collection getSuperinterfaces(Class the_class) {
0495: List super intfs = new ArrayList();
0496:
0497: Class c = the_class;
0498: Class[] super interfaces = c.getInterfaces();
0499: if (super interfaces != null) {
0500: for (int i = 0; i < super interfaces.length; i++) {
0501: if (!super intfs.contains(super interfaces[i])) {
0502: super intfs.add(super interfaces[i]);
0503: super intfs.addAll(ClassUtils
0504: .getSuperinterfaces(super interfaces[i]));
0505: }
0506: }
0507: }
0508: return super intfs;
0509: }
0510:
0511: /**
0512: * Obtain a field from a class or superclasses using reflection.
0513: * @param cls the class to find the field from
0514: * @param fieldName the field name to find
0515: * @return The Field
0516: */
0517: public static Field getFieldForClass(Class cls, String fieldName) {
0518: try {
0519: return cls.getDeclaredField(fieldName);
0520: } catch (NoSuchFieldException e) {
0521: if (cls.getSuperclass() != null) {
0522: return ClassUtils.getFieldForClass(cls.getSuperclass(),
0523: fieldName);
0524: }
0525: } catch (Exception e) {
0526: // do nothing
0527: }
0528: return null;
0529: }
0530:
0531: /**
0532: * Convenience method to return the object wrapper type for a primitive type name.
0533: * If the type is not a primitive then just returns the type name
0534: * @param typeName The primitive type name
0535: * @return The object wrapper type name for this primitive
0536: */
0537: public static String getWrapperTypeNameForPrimitiveTypeName(
0538: String typeName) {
0539: if (typeName.equals("boolean")) {
0540: return ClassNameConstants.JAVA_LANG_BOOLEAN;
0541: } else if (typeName.equals("byte")) {
0542: return ClassNameConstants.JAVA_LANG_BYTE;
0543: } else if (typeName.equals("char")) {
0544: return ClassNameConstants.JAVA_LANG_CHARACTER;
0545: } else if (typeName.equals("double")) {
0546: return ClassNameConstants.JAVA_LANG_DOUBLE;
0547: } else if (typeName.equals("float")) {
0548: return ClassNameConstants.JAVA_LANG_FLOAT;
0549: } else if (typeName.equals("int")) {
0550: return ClassNameConstants.JAVA_LANG_INTEGER;
0551: } else if (typeName.equals("long")) {
0552: return ClassNameConstants.JAVA_LANG_LONG;
0553: } else if (typeName.equals("short")) {
0554: return ClassNameConstants.JAVA_LANG_SHORT;
0555: } else {
0556: return typeName;
0557: }
0558: }
0559:
0560: /**
0561: * Convenience method to return the object wrapper type for a primitive type.
0562: * @param type The primitive type
0563: * @return The object wrapper type for this primitive
0564: */
0565: public static Class getWrapperTypeForPrimitiveType(Class type) {
0566: if (type == boolean.class) {
0567: return Boolean.class;
0568: } else if (type == byte.class) {
0569: return Byte.class;
0570: } else if (type == char.class) {
0571: return Character.class;
0572: } else if (type == double.class) {
0573: return Double.class;
0574: } else if (type == float.class) {
0575: return Float.class;
0576: } else if (type == int.class) {
0577: return Integer.class;
0578: } else if (type == long.class) {
0579: return Long.class;
0580: } else if (type == short.class) {
0581: return Short.class;
0582: }
0583: return null;
0584: }
0585:
0586: /**
0587: * Method to return the primitive equivalent of the specified type (if any).
0588: * Returns null if there is no primitive equivalent.
0589: * @param type The type
0590: * @return The primitive equivalent.
0591: */
0592: public static Class getPrimitiveTypeForType(Class type) {
0593: if (type == Boolean.class) {
0594: return boolean.class;
0595: } else if (type == Byte.class) {
0596: return byte.class;
0597: } else if (type == Character.class) {
0598: return char.class;
0599: } else if (type == Double.class) {
0600: return double.class;
0601: } else if (type == Float.class) {
0602: return float.class;
0603: } else if (type == Integer.class) {
0604: return int.class;
0605: } else if (type == Long.class) {
0606: return long.class;
0607: } else if (type == Short.class) {
0608: return short.class;
0609: } else {
0610: return null;
0611: }
0612: }
0613:
0614: /**
0615: * Convenience method to return if the passed type (name) is a primitive wrapper type.
0616: * @param typeName Name of the type
0617: * @return Whether it is a primitive wrapper
0618: */
0619: public static boolean isPrimitiveWrapperType(String typeName) {
0620: if (typeName.equals(ClassNameConstants.JAVA_LANG_BOOLEAN)
0621: || typeName.equals(ClassNameConstants.JAVA_LANG_BYTE)
0622: || typeName
0623: .equals(ClassNameConstants.JAVA_LANG_CHARACTER)
0624: || typeName.equals(ClassNameConstants.JAVA_LANG_DOUBLE)
0625: || typeName.equals(ClassNameConstants.JAVA_LANG_FLOAT)
0626: || typeName
0627: .equals(ClassNameConstants.JAVA_LANG_INTEGER)
0628: || typeName.equals(ClassNameConstants.JAVA_LANG_LONG)
0629: || typeName.equals(ClassNameConstants.JAVA_LANG_SHORT)) {
0630: return true;
0631: }
0632: return false;
0633: }
0634:
0635: /**
0636: * Convenience method to return if the passed type (name) is a primitive array type.
0637: * @param typeName Name of the type
0638: * @return Whether it is a primitive array
0639: */
0640: public static boolean isPrimitiveArrayType(String typeName) {
0641: if (typeName.equals(ClassNameConstants.BOOLEAN_ARRAY)
0642: || typeName.equals(ClassNameConstants.BYTE_ARRAY)
0643: || typeName.equals(ClassNameConstants.CHAR_ARRAY)
0644: || typeName.equals(ClassNameConstants.DOUBLE_ARRAY)
0645: || typeName.equals(ClassNameConstants.FLOAT_ARRAY)
0646: || typeName.equals(ClassNameConstants.INT_ARRAY)
0647: || typeName.equals(ClassNameConstants.LONG_ARRAY)
0648: || typeName.equals(ClassNameConstants.SHORT_ARRAY)) {
0649: return true;
0650: }
0651: return false;
0652: }
0653:
0654: /**
0655: * Convenience method to return if the passed type (name) is a primitive type.
0656: * @param typeName Name of the type
0657: * @return Whether it is a primitive
0658: */
0659: public static boolean isPrimitiveType(String typeName) {
0660: if (typeName.equals(ClassNameConstants.BOOLEAN)
0661: || typeName.equals(ClassNameConstants.BYTE)
0662: || typeName.equals(ClassNameConstants.CHAR)
0663: || typeName.equals(ClassNameConstants.DOUBLE)
0664: || typeName.equals(ClassNameConstants.FLOAT)
0665: || typeName.equals(ClassNameConstants.INT)
0666: || typeName.equals(ClassNameConstants.LONG)
0667: || typeName.equals(ClassNameConstants.SHORT)) {
0668: return true;
0669: }
0670: return false;
0671: }
0672:
0673: /**
0674: * Convenience method to convert the passed value to an object of the specified type (if possible).
0675: * If no such conversion is supported will return null. If the required type is a primitive will
0676: * return an object of the wrapper type.
0677: * @param value The value
0678: * @param cls The class
0679: * @return The converted value (or null)
0680: */
0681: public static Object convertValue(Object value, Class cls) {
0682: if (value == null) {
0683: return null;
0684: }
0685:
0686: Class type = cls;
0687: if (cls.isPrimitive()) {
0688: type = getWrapperTypeForPrimitiveType(cls);
0689: }
0690: if (type.isAssignableFrom(value.getClass())) {
0691: // Already in the correct type
0692: return value;
0693: }
0694:
0695: if (type == Long.class && value instanceof Number) {
0696: return new Long(((Number) value).longValue());
0697: } else if (type == Integer.class && value instanceof Number) {
0698: return new Integer(((Number) value).intValue());
0699: } else if (type == Short.class && value instanceof Number) {
0700: return new Short(((Number) value).shortValue());
0701: } else if (type == Float.class && value instanceof Number) {
0702: return new Float(((Number) value).doubleValue());
0703: } else if (type == Double.class && value instanceof Number) {
0704: return new Double(((Number) value).doubleValue());
0705: }
0706: return null;
0707: }
0708:
0709: /**
0710: * Convenience method to return if two types are compatible.
0711: * Returns true if both types are primitive/wrappers and are of the same type.
0712: * Returns true if clsName2 is the same or a subclass of cls1.
0713: * Otherwise returns false;
0714: * @param cls1 First class
0715: * @param clsName2 Name of the second class
0716: * @param clr ClassLoader resolver to use
0717: * @return Whether they are compatible
0718: */
0719: public static boolean typesAreCompatible(Class cls1,
0720: String clsName2, ClassLoaderResolver clr) {
0721: if (clr.isAssignableFrom(cls1, clsName2)) {
0722: return true;
0723: }
0724:
0725: // Cater for primitive and primitive wrappers being compatible
0726: if (cls1.isPrimitive()) {
0727: return clr.isAssignableFrom(ClassUtils
0728: .getWrapperTypeForPrimitiveType(cls1), clsName2);
0729: } else if (ClassUtils.isPrimitiveWrapperType(cls1.getName())) {
0730: return clr.isAssignableFrom(ClassUtils
0731: .getPrimitiveTypeForType(cls1), clsName2);
0732: }
0733:
0734: return false;
0735: }
0736:
0737: /**
0738: * Convenience method to return if two types are compatible.
0739: * Returns true if both types are primitive/wrappers and are of the same type.
0740: * Returns true if cls2 is the same or a subclass of cls1.
0741: * Otherwise returns false;
0742: * @param cls1 First class
0743: * @param cls2 Second class
0744: * @return Whether they are compatible
0745: */
0746: public static boolean typesAreCompatible(Class cls1, Class cls2) {
0747: if (cls1.isAssignableFrom(cls2)) {
0748: return true;
0749: }
0750:
0751: // Cater for primitive and primitive wrappers being compatible
0752: if (cls1.isPrimitive()) {
0753: return ClassUtils.getWrapperTypeForPrimitiveType(cls1)
0754: .isAssignableFrom(cls2);
0755: }
0756:
0757: return false;
0758: }
0759:
0760: /**
0761: * Utility to create the full class name given the package and class name.
0762: * Some examples
0763: * <PRE>
0764: * packageName=test className=Test, returns result=test.Test
0765: * packageName=test className=test1.Test, returns result=test1.Test
0766: * packageName=<null> className=Test, returns result=Test
0767: * packageName=<null> className=test1.Test, returns result=test1.Test
0768: * </PRE>
0769: * @param pkg_name package name.
0770: * @param cls_name class name.
0771: * @return generated full class name.
0772: */
0773: public static String createFullClassName(String pkg_name,
0774: String cls_name) {
0775: if (StringUtils.isWhitespace(cls_name)) {
0776: throw new IllegalArgumentException(
0777: "Class name not specified");
0778: } else if (StringUtils.isWhitespace(pkg_name)) {
0779: return cls_name;
0780: } else if (cls_name.indexOf('.') >= 0) {
0781: return cls_name;
0782: }
0783: return pkg_name + "." + cls_name;
0784: }
0785:
0786: /**
0787: * Convenience method to return the passed type as a java.lang type wherever possible.
0788: * The passed type will be stripped of any package name and will be checked if it is
0789: * a known java.lang class. This is used where the user has specified a class name
0790: * for a collection or map element/key/value type and meant a java.lang class but didn't
0791: * fully qualify it.
0792: * @param type The type name
0793: * @return The java.lang equivalent (or the input type if not possible)
0794: */
0795: public static String getJavaLangClassForType(String type) {
0796: // Strip off any package name
0797: String baseType = null;
0798: if (type.lastIndexOf('.') < 0) {
0799: baseType = type;
0800: } else {
0801: baseType = type.substring(type.lastIndexOf('.') + 1);
0802: }
0803:
0804: // Check against our known (supported) java.lang classes
0805: if (baseType.equals("String") || baseType.equals("Object")
0806: || baseType.equals("Boolean")
0807: || baseType.equals("Byte")
0808: || baseType.equals("Character")
0809: || baseType.equals("Double")
0810: || baseType.equals("Float")
0811: || baseType.equals("Integer")
0812: || baseType.equals("Long") || baseType.equals("Short")
0813: || baseType.equals("Number")
0814: || baseType.equals("StringBuffer")) {
0815: return "java.lang." + baseType;
0816: }
0817: return type;
0818: }
0819:
0820: /**
0821: * Method to check if 2 classes are direct descendents. So one of them is a
0822: * superclass of the other.
0823: * @param clr ClassLoaderResolver for loading the classes
0824: * @param class_name_1 Name of first class
0825: * @param class_name_2 Name of second class
0826: * @return Whether they are direct descendents.
0827: */
0828: public static boolean classesAreDescendents(
0829: ClassLoaderResolver clr, String class_name_1,
0830: String class_name_2) {
0831: Class class_1 = clr.classForName(class_name_1);
0832: Class class_2 = clr.classForName(class_name_2);
0833: if (class_1 == null || class_2 == null) {
0834: return false;
0835: }
0836:
0837: // Check for direct descendents
0838: if (class_1.isAssignableFrom(class_2)
0839: || class_2.isAssignableFrom(class_1)) {
0840: return true;
0841: }
0842:
0843: return false;
0844: }
0845:
0846: /**
0847: * Utility to use Reflection to dump out the details of a class.
0848: * Will list all superclasses, interfaces, methods and fields.
0849: * Can be used, for example, in checking the methods adding by the
0850: * enhancement process. The information is dumped out the JPOX GENERAL log.
0851: * @param cls The class to dump out to the log
0852: */
0853: public static void dumpClassInformation(Class cls) {
0854: JPOXLogger.GENERAL
0855: .info("----------------------------------------");
0856: JPOXLogger.GENERAL.info("Class Information for class "
0857: + cls.getName());
0858:
0859: // Superclasses
0860: Collection super classes = ClassUtils.getSuperclasses(cls);
0861: Iterator super class_iter = super classes.iterator();
0862: while (super class_iter.hasNext()) {
0863: Class super class = (Class) super class_iter.next();
0864: JPOXLogger.GENERAL.info(" Superclass : "
0865: + super class.getName());
0866: }
0867:
0868: // Interfaces
0869: Class[] interfaces = cls.getInterfaces();
0870: if (interfaces != null) {
0871: for (int i = 0; i < interfaces.length; i++) {
0872: JPOXLogger.GENERAL.info(" Interface : "
0873: + interfaces[i].getName());
0874: }
0875: }
0876:
0877: // Methods
0878: try {
0879: Method[] methods = cls.getDeclaredMethods();
0880: for (int i = 0; i < methods.length; i++) {
0881: JPOXLogger.GENERAL.info(" Method : "
0882: + methods[i].toString());
0883: }
0884: } catch (Exception e) {
0885: }
0886:
0887: // Fields
0888: try {
0889: Field[] fields = cls.getDeclaredFields();
0890: for (int i = 0; i < fields.length; i++) {
0891: JPOXLogger.GENERAL.info(" Field : "
0892: + fields[i].toString());
0893: }
0894: } catch (Exception e) {
0895: }
0896: JPOXLogger.GENERAL
0897: .info("----------------------------------------");
0898: }
0899:
0900: /**
0901: * Generate a JavaBeans compatible getter name
0902: * @param fieldName the field name
0903: * @param isBoolean whether the field is primitive boolean type
0904: * @return the getter name
0905: */
0906: public static String getJavaBeanGetterName(String fieldName,
0907: boolean isBoolean) {
0908: if (fieldName == null) {
0909: return null;
0910: }
0911: String prefix = isBoolean ? "is" : "get";
0912: String name = fieldName.toUpperCase().charAt(0)
0913: + fieldName.substring(1);
0914: return prefix + name;
0915: }
0916:
0917: /**
0918: * Generate a JavaBeans compatible setter name
0919: * @param fieldName the field name
0920: * @return the setter name
0921: */
0922: public static String getJavaBeanSetterName(String fieldName) {
0923: if (fieldName == null) {
0924: return null;
0925: }
0926: String prefix = "set";
0927: String name = fieldName.toUpperCase().charAt(0)
0928: + fieldName.substring(1);
0929: return prefix + name;
0930: }
0931:
0932: /**
0933: * Generate a field name for JavaBeans compatible getter method
0934: * @param methodName the method name
0935: * @return the field name
0936: */
0937: public static String getFieldNameForJavaBeanGetter(String methodName) {
0938: if (methodName == null) {
0939: return null;
0940: }
0941: if (methodName.startsWith("get")) {
0942: if (methodName.length() < 4) {
0943: return null;
0944: } else if (methodName.length() == 4) {
0945: return methodName.toLowerCase().substring(3);
0946: } else {
0947: if (Character.isUpperCase(methodName.charAt(3))
0948: && Character.isUpperCase(methodName.charAt(4))) {
0949: // Capitalised name (e.g URL) so dont lowercase first character
0950: return methodName.substring(3);
0951: } else {
0952: return methodName.toLowerCase().charAt(3)
0953: + methodName.substring(4);
0954: }
0955: }
0956: } else if (methodName.startsWith("is")) {
0957: if (methodName.length() < 3) {
0958: return null;
0959: } else if (methodName.length() == 3) {
0960: return methodName.toLowerCase().substring(2);
0961: } else {
0962: if (Character.isUpperCase(methodName.charAt(2))
0963: && Character.isUpperCase(methodName.charAt(3))) {
0964: // Capitalised name (e.g URL) so dont lowercase first character
0965: return methodName.substring(2);
0966: } else {
0967: return methodName.toLowerCase().charAt(2)
0968: + methodName.substring(3);
0969: }
0970: }
0971: }
0972: return null;
0973: }
0974:
0975: /**
0976: * Generate a field name for JavaBeans compatible setter method
0977: * @param methodName the method name
0978: * @return the field name
0979: */
0980: public static String getFieldNameForJavaBeanSetter(String methodName) {
0981: if (methodName == null) {
0982: return null;
0983: }
0984: if (methodName.startsWith("set")) {
0985: if (methodName.length() < 4) {
0986: return null;
0987: } else if (methodName.length() == 4) {
0988: return methodName.toLowerCase().substring(3);
0989: } else {
0990: if (Character.isUpperCase(methodName.charAt(3))
0991: && Character.isUpperCase(methodName.charAt(4))) {
0992: // Capitalised name (e.g URL) so dont lowercase first character
0993: return methodName.substring(3);
0994: } else {
0995: return methodName.toLowerCase().charAt(3)
0996: + methodName.substring(4);
0997: }
0998: }
0999: }
1000: return null;
1001: }
1002:
1003: /**
1004: * Utility to find the class name of a class given the absolute file name of its class file.
1005: * Creates a loader and loads the class directly to find it.
1006: * @param fileURL URL for the class file
1007: * @return The name of the class
1008: * @throws ClassNotFoundException Thrown when the file is not found
1009: */
1010: public static String getClassNameForFileURL(final URL fileURL)
1011: throws ClassNotFoundException {
1012: ClassLoader loader = (ClassLoader) AccessController
1013: .doPrivileged(new PrivilegedAction() {
1014: public Object run() {
1015: return new ClassLoader() {
1016: protected Class findClass(String name)
1017: throws ClassNotFoundException {
1018: // Always load the file
1019: InputStream in = null;
1020: try {
1021: in = new BufferedInputStream(
1022: fileURL.openStream());
1023: ByteArrayOutputStream byteStr = new ByteArrayOutputStream();
1024: int byt = -1;
1025: while ((byt = in.read()) != -1) {
1026: byteStr.write(byt);
1027: }
1028: byte byteArr[] = byteStr
1029: .toByteArray();
1030: return defineClass(null, byteArr,
1031: 0, byteArr.length);
1032: } catch (final RuntimeException rex) {
1033: throw rex;
1034: } catch (final Exception ex) {
1035: ex.printStackTrace();
1036: throw new ClassNotFoundException(
1037: name);
1038: } finally {
1039: if (in != null) {
1040: try {
1041: in.close();
1042: } catch (final IOException ioe) {
1043: ioe.printStackTrace();
1044: }
1045: }
1046: }
1047: }
1048: };
1049: }
1050: });
1051: Class cls = loader.loadClass("garbage"); // The passed in name is not of relevance
1052: return (cls != null ? cls.getName() : null);
1053: }
1054:
1055: /**
1056: * Utility to return the package name for a class.
1057: * Allows for the result of class.getPackage() being null.
1058: * @param cls The class
1059: * @return The name of its package (or null if no package e.g a primitive)
1060: */
1061: public static String getPackageNameForClass(Class cls) {
1062: // Check getPackage and use that if specified.
1063: if (cls.getPackage() != null) {
1064: return cls.getPackage().getName();
1065: }
1066: int separator = cls.getName().lastIndexOf('.');
1067: if (separator < 0) {
1068: return null;
1069: }
1070: return cls.getName().substring(0, separator);
1071: }
1072:
1073: /**
1074: * Utility to return the class name without the package name for a class.
1075: * @param cls The class
1076: * @return The name of the class without its package
1077: */
1078: public static String getClassNameForClass(Class cls) {
1079: // Just strip off all up to the last separator since Class.getPackage is unreliable
1080: int separator = cls.getName().lastIndexOf('.');
1081: if (separator < 0) {
1082: return cls.getName();
1083: }
1084: return cls.getName().substring(separator + 1);
1085: }
1086:
1087: /**
1088: * Convenience method to filter out any supported classes from a list.
1089: * @param typeMgr TypeManager defining the types supported
1090: * @param classNames Names of the classes
1091: * @return Names of the classes (omitting supported types)
1092: */
1093: public static String[] getUnsupportedClassNames(
1094: TypeManager typeMgr, String[] classNames) {
1095: // Filter out any "simple" type classes
1096: int filteredClasses = 0;
1097: for (int i = 0; i < classNames.length; ++i) {
1098: if (typeMgr.isSupportedType(classNames[i])) {
1099: classNames[i] = null;
1100: ++filteredClasses;
1101: }
1102: }
1103: if (filteredClasses == 0) {
1104: return classNames;
1105: }
1106: String[] restClasses = new String[classNames.length
1107: - filteredClasses];
1108: int m = 0;
1109: for (int i = 0; i < classNames.length; ++i) {
1110: if (classNames[i] != null) {
1111: restClasses[m++] = classNames[i];
1112: }
1113: }
1114: return restClasses;
1115: }
1116:
1117: /**
1118: * Convenience method to extract the element type of a collection when using JDK1.5 generics given the
1119: * input field.
1120: * @param field The field
1121: * @return The name of the element class
1122: */
1123: public static String getCollectionElementType(Field field) {
1124: return getCollectionElementType(field.getType(), field
1125: .getGenericType());
1126: }
1127:
1128: /**
1129: * Convenience method to extract the element type of a collection when using JDK1.5 generics given the
1130: * input field.
1131: * @param type the field type
1132: * @param genericType the generic type
1133: * @return The name of the element class
1134: */
1135: public static String getCollectionElementType(Class type,
1136: Type genericType) {
1137: if (!Collection.class.isAssignableFrom(type)) {
1138: return null;
1139: }
1140:
1141: String elementType = null;
1142: if (genericType instanceof ParameterizedType) {
1143: ParameterizedType paramtype = (ParameterizedType) genericType;
1144: if (paramtype.getActualTypeArguments().length == 1) {
1145: if (paramtype.getActualTypeArguments()[0] instanceof Class) {
1146: elementType = ((Class) paramtype
1147: .getActualTypeArguments()[0]).getName();
1148: }
1149: }
1150: }
1151: return elementType;
1152: }
1153:
1154: /**
1155: * Convenience method to extract the element type of a collection when using JDK1.5 generics, given
1156: * the input method (getter).
1157: * @param method The method
1158: * @return The name of the element class
1159: */
1160: public static String getCollectionElementType(Method method) {
1161: if (!Collection.class.isAssignableFrom(method.getReturnType())) {
1162: return null;
1163: }
1164:
1165: String elementType = null;
1166: if (method.getGenericReturnType() instanceof ParameterizedType) {
1167: ParameterizedType paramtype = (ParameterizedType) method
1168: .getGenericReturnType();
1169: if (paramtype.getActualTypeArguments().length == 1) {
1170: if (paramtype.getActualTypeArguments()[0] instanceof Class) {
1171: elementType = ((Class) paramtype
1172: .getActualTypeArguments()[0]).getName();
1173: }
1174: }
1175: }
1176: return elementType;
1177: }
1178:
1179: /**
1180: * Convenience method to extract the key type of a map when using JDK1.5 generics given the
1181: * input field.
1182: * @param field The field
1183: * @return The name of the key class
1184: */
1185: public static String getMapKeyType(Field field) {
1186: return getMapKeyType(field.getType(), field.getGenericType());
1187: }
1188:
1189: /**
1190: * Convenience method to extract the key type of a map when using JDK1.5 generics given the
1191: * input field.
1192: * @param type the field type
1193: * @param genericType the generic type
1194: * @return The name of the key class
1195: */
1196: public static String getMapKeyType(Class type, Type genericType) {
1197: if (!Map.class.isAssignableFrom(type)) {
1198: return null;
1199: }
1200:
1201: String keyType = null;
1202: if (genericType instanceof ParameterizedType) {
1203: ParameterizedType paramtype = (ParameterizedType) genericType;
1204: if (paramtype.getActualTypeArguments().length == 2) {
1205: if (paramtype.getActualTypeArguments()[0] instanceof Class) {
1206: keyType = ((Class) paramtype
1207: .getActualTypeArguments()[0]).getName();
1208: }
1209: }
1210: }
1211: return keyType;
1212: }
1213:
1214: /**
1215: * Convenience method to extract the key type of a map when using JDK1.5 generics given the
1216: * input method.
1217: * @param method The method
1218: * @return The name of the key class
1219: */
1220: public static String getMapKeyType(Method method) {
1221: if (!Map.class.isAssignableFrom(method.getReturnType())) {
1222: return null;
1223: }
1224:
1225: String keyType = null;
1226: if (method.getGenericReturnType() instanceof ParameterizedType) {
1227: ParameterizedType paramtype = (ParameterizedType) method
1228: .getGenericReturnType();
1229: if (paramtype.getActualTypeArguments().length == 2) {
1230: if (paramtype.getActualTypeArguments()[0] instanceof Class) {
1231: keyType = ((Class) paramtype
1232: .getActualTypeArguments()[0]).getName();
1233: }
1234: }
1235: }
1236: return keyType;
1237: }
1238:
1239: /**
1240: * Convenience method to extract the value type of a map when using JDK1.5 generics given the
1241: * input field
1242: * @param field The field
1243: * @return The name of the value class
1244: */
1245: public static String getMapValueType(Field field) {
1246: return getMapValueType(field.getType(), field.getGenericType());
1247: }
1248:
1249: /**
1250: * Convenience method to extract the value type of a map when using JDK1.5 generics given the
1251: * input field
1252: * @param type the field type
1253: * @param genericType the generic type
1254: * @return The name of the value class
1255: */
1256: public static String getMapValueType(Class type, Type genericType) {
1257: if (!Map.class.isAssignableFrom(type)) {
1258: return null;
1259: }
1260:
1261: String valueType = null;
1262: if (genericType instanceof ParameterizedType) {
1263: ParameterizedType paramtype = (ParameterizedType) genericType;
1264: if (paramtype.getActualTypeArguments().length == 2) {
1265: if (paramtype.getActualTypeArguments()[1] instanceof Class) {
1266: valueType = ((Class) paramtype
1267: .getActualTypeArguments()[1]).getName();
1268: }
1269: }
1270: }
1271: return valueType;
1272: }
1273:
1274: /**
1275: * Convenience method to extract the value type of a map when using JDK1.5 generics given the
1276: * input method.
1277: * @param method The method
1278: * @return The name of the value class
1279: */
1280: public static String getMapValueType(Method method) {
1281: if (!Map.class.isAssignableFrom(method.getReturnType())) {
1282: return null;
1283: }
1284:
1285: String valueType = null;
1286: if (method.getGenericReturnType() instanceof ParameterizedType) {
1287: ParameterizedType paramtype = (ParameterizedType) method
1288: .getGenericReturnType();
1289: if (paramtype.getActualTypeArguments().length == 2) {
1290: if (paramtype.getActualTypeArguments()[1] instanceof Class) {
1291: valueType = ((Class) paramtype
1292: .getActualTypeArguments()[1]).getName();
1293: }
1294: }
1295: }
1296: return valueType;
1297: }
1298:
1299: /**
1300: * Convenience accessor for the modifiers of a field in a class.
1301: * @param clr ClassLoader resolver
1302: * @param className Name of the class
1303: * @param fieldName Name of the field
1304: * @return The modifiers
1305: */
1306: public static int getModifiersForFieldOfClass(
1307: ClassLoaderResolver clr, String className, String fieldName) {
1308: try {
1309: Class cls = clr.classForName(className);
1310: Field fld = cls.getDeclaredField(fieldName);
1311: return fld.getModifiers();
1312: } catch (Exception e) {
1313: // Class or field not found
1314: }
1315: return -1;
1316: }
1317: }
|