1. /* ====================================================================
  2. * The Apache Software License, Version 1.1
  3. *
  4. * Copyright (c) 2002-2003 The Apache Software Foundation. All rights
  5. * reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. *
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in
  16. * the documentation and/or other materials provided with the
  17. * distribution.
  18. *
  19. * 3. The end-user documentation included with the redistribution, if
  20. * any, must include the following acknowledgement:
  21. * "This product includes software developed by the
  22. * Apache Software Foundation (http://www.apache.org/)."
  23. * Alternately, this acknowledgement may appear in the software itself,
  24. * if and wherever such third-party acknowledgements normally appear.
  25. *
  26. * 4. The names "The Jakarta Project", "Commons", and "Apache Software
  27. * Foundation" must not be used to endorse or promote products derived
  28. * from this software without prior written permission. For written
  29. * permission, please contact apache@apache.org.
  30. *
  31. * 5. Products derived from this software may not be called "Apache"
  32. * nor may "Apache" appear in their names without prior written
  33. * permission of the Apache Software Foundation.
  34. *
  35. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  36. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  37. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  38. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  39. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  41. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  42. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  43. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  44. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  45. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  46. * SUCH DAMAGE.
  47. * ====================================================================
  48. *
  49. * This software consists of voluntary contributions made by many
  50. * individuals on behalf of the Apache Software Foundation. For more
  51. * information on the Apache Software Foundation, please see
  52. * <http://www.apache.org/>.
  53. */
  54. package org.apache.commons.lang;
  55. import java.util.ArrayList;
  56. import java.util.Iterator;
  57. import java.util.List;
  58. /**
  59. * <p>Operates on classes without using reflection.</p>
  60. *
  61. * <p>This class handles invalid <code>null</code> inputs as best it can.
  62. * Each method documents its behaviour in more detail.</p>
  63. *
  64. * @author Stephen Colebourne
  65. * @author Gary Gregory
  66. * @since 2.0
  67. * @version $Id: ClassUtils.java,v 1.21 2003/08/22 17:25:33 ggregory Exp $
  68. */
  69. public class ClassUtils {
  70. /**
  71. * <p>The package separator character: <code>.</code>.</p>
  72. */
  73. public static final char PACKAGE_SEPARATOR_CHAR = '.';
  74. /**
  75. * <p>The package separator String: <code>.</code>.</p>
  76. */
  77. public static final String PACKAGE_SEPARATOR = String.valueOf(PACKAGE_SEPARATOR_CHAR);
  78. /**
  79. * <p>The inner class separator character: <code>$</code>.</p>
  80. */
  81. public static final char INNER_CLASS_SEPARATOR_CHAR = '$';
  82. /**
  83. * <p>The inner class separator String: <code>$</code>.</p>
  84. */
  85. public static final String INNER_CLASS_SEPARATOR = String.valueOf(INNER_CLASS_SEPARATOR_CHAR);
  86. /**
  87. * <p>ClassUtils instances should NOT be constructed in standard programming.
  88. * Instead, the class should be used as
  89. * <code>ClassUtils.getShortClassName(cls)</code>.</p>
  90. *
  91. * <p>This constructor is public to permit tools that require a JavaBean
  92. * instance to operate.</p>
  93. */
  94. public ClassUtils() {
  95. }
  96. // Short class name
  97. // ----------------------------------------------------------------------
  98. /**
  99. * <p>Gets the class name minus the package name for an <code>Object</code>.</p>
  100. *
  101. * @param object the class to get the short name for, may be null
  102. * @param valueIfNull the value to return if null
  103. * @return the class name of the object without the package name, or the null value
  104. */
  105. public static String getShortClassName(Object object, String valueIfNull) {
  106. if (object == null) {
  107. return valueIfNull;
  108. }
  109. return getShortClassName(object.getClass().getName());
  110. }
  111. /**
  112. * <p>Gets the class name minus the package name from a <code>Class</code>.</p>
  113. *
  114. * @param cls the class to get the short name for, must not be
  115. * <code>null</code>
  116. * @return the class name without the package name
  117. * @throws IllegalArgumentException if the class is <code>null</code>
  118. */
  119. public static String getShortClassName(Class cls) {
  120. if (cls == null) {
  121. throw new IllegalArgumentException("The class must not be null");
  122. }
  123. return getShortClassName(cls.getName());
  124. }
  125. /**
  126. * <p>Gets the class name minus the package name from a String.</p>
  127. *
  128. * <p>The string passed in is assumed to be a class name - it is not checked.</p>
  129. *
  130. * @param className the className to get the short name for,
  131. * must not be empty or <code>null</code>
  132. * @return the class name of the class without the package name
  133. * @throws IllegalArgumentException if the className is empty
  134. */
  135. public static String getShortClassName(String className) {
  136. if (StringUtils.isEmpty(className)) {
  137. throw new IllegalArgumentException("The class name must not be empty");
  138. }
  139. char[] chars = className.toCharArray();
  140. int lastDot = 0;
  141. for (int i = 0; i < chars.length; i++) {
  142. if (chars[i] == PACKAGE_SEPARATOR_CHAR) {
  143. lastDot = i + 1;
  144. } else if (chars[i] == INNER_CLASS_SEPARATOR_CHAR) { // handle inner classes
  145. chars[i] = PACKAGE_SEPARATOR_CHAR;
  146. }
  147. }
  148. return new String(chars, lastDot, chars.length - lastDot);
  149. }
  150. // Package name
  151. // ----------------------------------------------------------------------
  152. /**
  153. * <p>Gets the package name of an <code>Object</code>.</p>
  154. *
  155. * @param object the class to get the package name for, may be null
  156. * @param valueIfNull the value to return if null
  157. * @return the package name of the object, or the null value
  158. */
  159. public static String getPackageName(Object object, String valueIfNull) {
  160. if (object == null) {
  161. return valueIfNull;
  162. }
  163. return getPackageName(object.getClass().getName());
  164. }
  165. /**
  166. * <p>Gets the package name of a <code>Class</code>.</p>
  167. *
  168. * @param cls the class to get the package name for,
  169. * must not be <code>null</code>
  170. * @return the package name
  171. * @throws IllegalArgumentException if the class is <code>null</code>
  172. */
  173. public static String getPackageName(Class cls) {
  174. if (cls == null) {
  175. throw new IllegalArgumentException("The class must not be null");
  176. }
  177. return getPackageName(cls.getName());
  178. }
  179. /**
  180. * <p>Gets the package name from a <code>String</code>.</p>
  181. *
  182. * <p>The string passed in is assumed to be a class name - it is not checked.</p>
  183. *
  184. * @param className the className to get the package name for,
  185. * must not be empty or <code>null</code>
  186. * @return the package name
  187. * @throws IllegalArgumentException if the className is empty
  188. */
  189. public static String getPackageName(String className) {
  190. if (StringUtils.isEmpty(className)) {
  191. throw new IllegalArgumentException("The class name must not be empty");
  192. }
  193. int i = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR);
  194. if (i == -1) {
  195. return "";
  196. }
  197. return className.substring(0, i);
  198. }
  199. // Superclasses/Superinterfaces
  200. // ----------------------------------------------------------------------
  201. /**
  202. * <p>Gets a <code>List</code> of superclasses for the given class.</p>
  203. *
  204. * @param cls the class to look up, must not be <code>null</code>
  205. * @return the <code>List</code> of superclasses in order going up from this one
  206. * <code>null</code> if null input
  207. */
  208. public static List getAllSuperclasses(Class cls) {
  209. if (cls == null) {
  210. return null;
  211. }
  212. List classes = new ArrayList();
  213. Class superclass = cls.getSuperclass();
  214. while (superclass != null) {
  215. classes.add(superclass);
  216. superclass = superclass.getSuperclass();
  217. }
  218. return classes;
  219. }
  220. /**
  221. * <p>Gets a <code>List</code> of all interfaces implemented by the given
  222. * class and its superclasses.</p>
  223. *
  224. * <p>The order is determined by looking through each interface in turn as
  225. * declared in the source file and following its hieracrchy up. Then each
  226. * superclass is considered in the same way. Later duplicates are ignored,
  227. * so the order is maintained.</p>
  228. *
  229. * @param cls the class to look up, must not be <code>null</code>
  230. * @return the <code>List</code> of interfaces in order,
  231. * <code>null</code> if null input
  232. */
  233. public static List getAllInterfaces(Class cls) {
  234. if (cls == null) {
  235. return null;
  236. }
  237. List list = new ArrayList();
  238. while (cls != null) {
  239. Class[] interfaces = cls.getInterfaces();
  240. for (int i = 0; i < interfaces.length; i++) {
  241. if (list.contains(interfaces[i]) == false) {
  242. list.add(interfaces[i]);
  243. }
  244. List superInterfaces = getAllInterfaces(interfaces[i]);
  245. for (Iterator it = superInterfaces.iterator(); it.hasNext();) {
  246. Class intface = (Class) it.next();
  247. if (list.contains(intface) == false) {
  248. list.add(intface);
  249. }
  250. }
  251. }
  252. cls = cls.getSuperclass();
  253. }
  254. return list;
  255. }
  256. // /**
  257. // * <p>Gets a <code>List</code> of subclasses of the specified class.</p>
  258. // *
  259. // * <p>This method searches the classpath to find all the subclasses
  260. // * of a particular class available. No classes are loaded, the
  261. // * returned list contains class names, not classes.</p>
  262. // *
  263. // * @param cls the class to find subclasses for
  264. // * @return the <code>List</code> of subclass String class names
  265. // * @throws IllegalArgumentException if the class is <code>null</code>
  266. // */
  267. // public static List getAllSubclassNames(Class cls) {
  268. // if (cls == null) {
  269. // throw new IllegalArgumentException("The class must not be null");
  270. // }
  271. // // TODO Use JavaWorld tip for searching the classpath
  272. // return null;
  273. // }
  274. // /**
  275. // * <p>Gets a <code>List</code> of subclasses of the specified class.</p>
  276. // *
  277. // * <p>This method searches the classpath to find all the subclasses
  278. // * of a particular class available.</p>
  279. // *
  280. // * @param cls the class to find subclasses for
  281. // * @return the <code>List</code> of subclasses
  282. // * @throws IllegalArgumentException if the class is <code>null</code>
  283. // */
  284. // public static List getAllSubclasses(Class cls) {
  285. // List names = getAllSubclassNames(cls);
  286. // return convertClassNamesToClasses(names);
  287. // }
  288. // /**
  289. // * <p>Gets a <code>List</code> of implementations of the specified interface.</p>
  290. // *
  291. // * <p>This method searches the classpath to find all the implementations
  292. // * of a particular interface available. No classes are loaded, the
  293. // * returned list contains class names, not classes.</p>
  294. // *
  295. // * @param cls the class to find sub classes for
  296. // * @return the <code>List</code> of implementation String class names
  297. // * @throws IllegalArgumentException if the class is <code>null</code>
  298. // */
  299. // public static List getAllImplementationClassNames(Class cls) {
  300. // if (cls == null) {
  301. // throw new IllegalArgumentException("The class must not be null");
  302. // }
  303. // // TODO Use JavaWorld tip for searching the classpath
  304. // return null;
  305. // }
  306. // Convert list
  307. // ----------------------------------------------------------------------
  308. /**
  309. * <p>Given a <code>List</code> of class names, this method converts them into classes.</p>
  310. *
  311. * <p>A new <code>List</code> is returned. If the class name cannot be found, <code>null</code>
  312. * is stored in the <code>List</code>. If the class name in the <code>List</code> is
  313. * <code>null</code>, <code>null</code> is stored in the output <code>List</code>.</p>
  314. *
  315. * @param classNames the classNames to change
  316. * @return a <code>List</code> of Class objects corresponding to the class names,
  317. * <code>null</code> if null input
  318. * @throws ClassCastException if classNames contains a non String entry
  319. */
  320. public static List convertClassNamesToClasses(List classNames) {
  321. if (classNames == null) {
  322. return null;
  323. }
  324. List classes = new ArrayList(classNames.size());
  325. for (Iterator it = classNames.iterator(); it.hasNext();) {
  326. String className = (String) it.next();
  327. try {
  328. classes.add(Class.forName(className));
  329. } catch (Exception ex) {
  330. classes.add(null);
  331. }
  332. }
  333. return classes;
  334. }
  335. /**
  336. * <p>Given a <code>List</code> of <code>Class</code> objects, this method converts
  337. * them into class names.</p>
  338. *
  339. * <p>A new <code>List</code> is returned. <code>null</code> objects will be copied into
  340. * the returned list as <code>null</code>.</p>
  341. *
  342. * @param classes the classes to change
  343. * @return a <code>List</code> of Class objects corresponding to the class names,
  344. * <code>null</code> if null input
  345. * @throws ClassCastException if classNames contains a non Class or null entry
  346. */
  347. public static List convertClassesToClassNames(List classes) {
  348. if (classes == null) {
  349. return null;
  350. }
  351. List classNames = new ArrayList(classes.size());
  352. for (Iterator it = classes.iterator(); it.hasNext();) {
  353. Class cls = (Class) it.next();
  354. if (cls == null) {
  355. classNames.add(null);
  356. } else {
  357. classNames.add(cls.getName());
  358. }
  359. }
  360. return classNames;
  361. }
  362. // Is assignable
  363. // ----------------------------------------------------------------------
  364. /**
  365. * <p>Checks if an array of Classes can be assigned to another array of Classes.</p>
  366. *
  367. * <p>This method calls {@link #isAssignable(Class, Class) isAssignable} for each
  368. * Class pair in the input arrays. It can be used to check if a set of arguments
  369. * (the first parameter) are suitably compatable with a set of method parameter types
  370. * (the second parameter).</p>
  371. *
  372. * <p>Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this
  373. * method takes into account widenings of primitive classes and
  374. * <code>null</code>s.</p>
  375. *
  376. * <p>Primitive widenings allow an int to be assigned to a <code>long</code>,
  377. * <code>float</code> or <code>double</code>. This method returns the correct
  378. * result for these cases.</p>
  379. *
  380. * <p><code>Null</code> may be assigned to any reference type. This method will
  381. * return <code>true</code> if <code>null</code> is passed in and the toClass is
  382. * non-primitive.</p>
  383. *
  384. * <p>Specifically, this method tests whether the type represented by the
  385. * specified <code>Class</code> parameter can be converted to the type
  386. * represented by this <code>Class</code> object via an identity conversion
  387. * widening primitive or widening reference conversion. See
  388. * <em><a href="http://java.sun.com/docs/books/jls/">The Java Language Specification</a></em>,
  389. * sections 5.1.1, 5.1.2 and 5.1.4 for details.</p>
  390. *
  391. * @param classArray the array of Classes to check, may be <code>null</code>
  392. * @param toClassArray the array of Classes to try to assign into, may be <code>null</code>
  393. * @return <code>true</code> if assignment possible
  394. */
  395. public static boolean isAssignable(Class[] classArray, Class[] toClassArray) {
  396. if (ArrayUtils.isSameLength(classArray, toClassArray) == false) {
  397. return false;
  398. }
  399. if (classArray == null) {
  400. classArray = ArrayUtils.EMPTY_CLASS_ARRAY;
  401. }
  402. if (toClassArray == null) {
  403. toClassArray = ArrayUtils.EMPTY_CLASS_ARRAY;
  404. }
  405. for (int i = 0; i < classArray.length; i++) {
  406. if (isAssignable(classArray[i], toClassArray[i]) == false) {
  407. return false;
  408. }
  409. }
  410. return true;
  411. }
  412. /**
  413. * <p>Checks if one <code>Class</code> can be assigned to a variable of
  414. * another <code>Class</code>.</p>
  415. *
  416. * <p>Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method,
  417. * this method takes into account widenings of primitive classes and
  418. * <code>null</code>s.</p>
  419. *
  420. * <p>Primitive widenings allow an int to be assigned to a long, float or
  421. * double. This method returns the correct result for these cases.</p>
  422. *
  423. * <p><code>Null</code> may be assigned to any reference type. This method
  424. * will return <code>true</code> if <code>null</code> is passed in and the
  425. * toClass is non-primitive.</p>
  426. *
  427. * <p>Specifically, this method tests whether the type represented by the
  428. * specified <code>Class</code> parameter can be converted to the type
  429. * represented by this <code>Class</code> object via an identity conversion
  430. * widening primitive or widening reference conversion. See
  431. * <em><a href="http://java.sun.com/docs/books/jls/">The Java Language Specification</a></em>,
  432. * sections 5.1.1, 5.1.2 and 5.1.4 for details.</p>
  433. *
  434. * @param cls the Class to check, may be null
  435. * @param toClass the Class to try to assign into, returns false if null
  436. * @return <code>true</code> if assignment possible
  437. */
  438. public static boolean isAssignable(Class cls, Class toClass) {
  439. if (toClass == null) {
  440. return false;
  441. }
  442. // have to check for null, as isAssignableFrom doesn't
  443. if (cls == null) {
  444. return !(toClass.isPrimitive());
  445. }
  446. if (cls.equals(toClass)) {
  447. return true;
  448. }
  449. if (cls.isPrimitive()) {
  450. if (toClass.isPrimitive() == false) {
  451. return false;
  452. }
  453. if (Integer.TYPE.equals(cls)) {
  454. return Long.TYPE.equals(toClass)
  455. || Float.TYPE.equals(toClass)
  456. || Double.TYPE.equals(toClass);
  457. }
  458. if (Long.TYPE.equals(cls)) {
  459. return Float.TYPE.equals(toClass)
  460. || Double.TYPE.equals(toClass);
  461. }
  462. if (Boolean.TYPE.equals(cls)) {
  463. return false;
  464. }
  465. if (Double.TYPE.equals(cls)) {
  466. return false;
  467. }
  468. if (Float.TYPE.equals(cls)) {
  469. return Double.TYPE.equals(toClass);
  470. }
  471. if (Character.TYPE.equals(cls)) {
  472. return Integer.TYPE.equals(toClass)
  473. || Long.TYPE.equals(toClass)
  474. || Float.TYPE.equals(toClass)
  475. || Double.TYPE.equals(toClass);
  476. }
  477. if (Short.TYPE.equals(cls)) {
  478. return Integer.TYPE.equals(toClass)
  479. || Long.TYPE.equals(toClass)
  480. || Float.TYPE.equals(toClass)
  481. || Double.TYPE.equals(toClass);
  482. }
  483. if (Byte.TYPE.equals(cls)) {
  484. return Short.TYPE.equals(toClass)
  485. || Integer.TYPE.equals(toClass)
  486. || Long.TYPE.equals(toClass)
  487. || Float.TYPE.equals(toClass)
  488. || Double.TYPE.equals(toClass);
  489. }
  490. // should never get here
  491. return false;
  492. }
  493. return toClass.isAssignableFrom(cls);
  494. }
  495. // Inner class
  496. // ----------------------------------------------------------------------
  497. /**
  498. * <p>Is the specified class an inner class or static nested class.</p>
  499. *
  500. * @param cls the class to check
  501. * @return <code>true</code> if the class is an inner or static nested class,
  502. * false if not or <code>null</code>
  503. */
  504. public static boolean isInnerClass(Class cls) {
  505. if (cls == null) {
  506. return false;
  507. }
  508. return (cls.getName().indexOf(INNER_CLASS_SEPARATOR_CHAR) >= 0);
  509. }
  510. }