1   /**
2    * Distribution License:
3    * JSword is free software; you can redistribute it and/or modify it under
4    * the terms of the GNU Lesser General Public License, version 2.1 as published by
5    * the Free Software Foundation. This program is distributed in the hope
6    * that it will be useful, but WITHOUT ANY WARRANTY; without even the
7    * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
8    * See the GNU Lesser General Public License for more details.
9    *
10   * The License is available on the internet at:
11   *       http://www.gnu.org/copyleft/lgpl.html
12   * or by writing to:
13   *      Free Software Foundation, Inc.
14   *      59 Temple Place - Suite 330
15   *      Boston, MA 02111-1307, USA
16   *
17   * Copyright: 2005
18   *     The copyright to this program is held by it's authors.
19   *
20   * ID: $Id: ClassUtil.java 2230 2012-02-08 00:00:10Z dmsmith $
21   */
22  package org.crosswire.common.util;
23  
24  import java.io.File;
25  import java.io.IOException;
26  import java.util.zip.ZipEntry;
27  import java.util.zip.ZipFile;
28  
29  /**
30   * Various Java Class Utilities.
31   * 
32   * @see gnu.lgpl.License for license details.<br>
33   *      The copyright to this program is held by it's authors.
34   * @author Joe Walker [joe at eireneh dot com]
35   */
36  public final class ClassUtil {
37      /**
38       * Prevent instantiation
39       */
40      private ClassUtil() {
41      }
42  
43      /**
44       * Gets the Class for the className in a way that works well for extensions.
45       * See: http://www.javageeks.com/Papers/ClassForName/ClassForName.pdf
46       * 
47       * @param className
48       *            the class to get
49       * @return the found Class
50       * @throws ClassNotFoundException
51       */
52      public static Class<?> forName(String className) throws ClassNotFoundException {
53          return Thread.currentThread().getContextClassLoader().loadClass(className);
54      }
55  
56      /**
57       * This function finds the first matching filename for a Java class file
58       * from the classpath, if none is found it returns null.
59       */
60      public static String findClasspathEntry(String classname, String classpath) {
61          String full = null;
62  
63          String[] paths = StringUtil.split(classpath, File.pathSeparator);
64          for (int i = 0; i < paths.length; i++) {
65              // Search the jar
66              if (paths[i].endsWith(EXTENSION_ZIP) || paths[i].endsWith(EXTENSION_JAR)) {
67                  ZipFile zip = null;
68                  try {
69                      String fileName = classname.replace(',', '/') + EXTENSION_CLASS;
70                      zip = new ZipFile(paths[i]);
71                      ZipEntry entry = zip.getEntry(fileName);
72  
73                      if (entry != null && !entry.isDirectory()) {
74                          if (full != null && !full.equals(fileName)) {
75                              log.warn("Warning duplicate " + classname + " found: " + full + " and " + paths[i]);
76                          } else {
77                              full = paths[i];
78                          }
79                      }
80                  } catch (IOException ex) {
81                      // If that zip file failed, then ignore it and move on.
82                      log.warn("Missing zip file for " + classname + " and " + paths[i]);
83                  } finally {
84                      IOUtil.close(zip);
85                  }
86              } else {
87                  StringBuilder path = new StringBuilder(256);
88  
89                  // Search for the file
90                  String extra = classname.replace('.', File.separatorChar);
91  
92                  path.append(paths[i]);
93                  if (paths[i].charAt(paths[i].length() - 1) != File.separatorChar) {
94                      path.append(File.separatorChar);
95                  }
96  
97                  path.append(extra);
98                  path.append(EXTENSION_CLASS);
99                  String fileName = path.toString();
100 
101                 if (new File(fileName).isFile()) {
102                     if (full != null && !full.equals(fileName)) {
103                         log.warn("Warning duplicate " + classname + " found: " + full + " and " + paths[i]);
104                     } else {
105                         full = paths[i];
106                     }
107                 }
108             }
109         }
110 
111         return full;
112     }
113 
114     /**
115      * This function find the first matching filename for a Java class file from
116      * the classpath, if none is found it returns null.
117      */
118     public static String findClasspathEntry(String classname) {
119         String classpath = System.getProperty("java.class.path", "");
120         return findClasspathEntry(classname, classpath);
121     }
122 
123     /**
124      * <p>
125      * Gets the class name minus the package name for an <code>Object</code>.
126      * </p>
127      * 
128      * @param object
129      *            the class to get the short name for, may be null
130      * @param valueIfNull
131      *            the value to return if null
132      * @return the class name of the object without the package name, or the
133      *         null value
134      */
135     public static String getShortClassName(Object object, String valueIfNull) {
136         if (object == null) {
137             return valueIfNull;
138         }
139         return getShortClassName(object.getClass().getName());
140     }
141 
142     /**
143      * <p>
144      * Gets the class name minus the package name from a <code>Class</code>.
145      * </p>
146      * 
147      * @param cls
148      *            the class to get the short name for, must not be
149      *            <code>null</code>
150      * @return the class name without the package name
151      * @throws IllegalArgumentException
152      *             if the class is <code>null</code>
153      */
154     public static String getShortClassName(Class<?> cls) {
155         if (cls == null) {
156             throw new IllegalArgumentException("The class must not be null");
157         }
158         return getShortClassName(cls.getName());
159     }
160 
161     /**
162      * <p>
163      * Gets the class name minus the package name from a String.
164      * </p>
165      * 
166      * <p>
167      * The string passed in is assumed to be a class name - it is not checked.
168      * </p>
169      * 
170      * @param className
171      *            the className to get the short name for, must not be empty or
172      *            <code>null</code>
173      * @return the class name of the class without the package name
174      * @throws IllegalArgumentException
175      *             if the className is empty
176      */
177     public static String getShortClassName(String className) {
178         if (className == null || className.length() == 0) {
179             throw new IllegalArgumentException("The class name must not be empty");
180         }
181         char[] chars = className.toCharArray();
182         int lastDot = 0;
183         for (int i = 0; i < chars.length; i++) {
184             if (chars[i] == PACKAGE_SEPARATOR_CHAR) {
185                 lastDot = i + 1;
186             } else if (chars[i] == INNER_CLASS_SEPARATOR_CHAR) {
187                 chars[i] = PACKAGE_SEPARATOR_CHAR;
188             }
189         }
190         return new String(chars, lastDot, chars.length - lastDot);
191     }
192 
193     /**
194      * <p>
195      * The package separator character: <code>&#x2e;</code>.
196      * </p>
197      */
198     private static final char PACKAGE_SEPARATOR_CHAR = '.';
199 
200     /**
201      * <p>
202      * The inner class separator character: <code>$</code>.
203      * </p>
204      */
205     private static final char INNER_CLASS_SEPARATOR_CHAR = '$';
206 
207     private static final String EXTENSION_CLASS = ".class";
208     private static final String EXTENSION_JAR = ".jar";
209     private static final String EXTENSION_ZIP = ".zip";
210 
211     /**
212      * The log stream
213      */
214     private static final Logger log = Logger.getLogger(ClassUtil.class);
215 }
216