Tools.java
001 /*
002  *  Tools.java
003  *
004  *  Copyright (c) 1995-2012, The University of Sheffield. See the file
005  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
006  *
007  *  This file is part of GATE (see http://gate.ac.uk/), and is free
008  *  software, licenced under the GNU Library General Public License,
009  *  Version 2, June 1991 (in the distribution as file licence.html,
010  *  and also available at http://gate.ac.uk/gate/licence.html).
011  *
012  *  Valentin Tablan, Jan/2000
013  *
014  *  $Id: Tools.java 17600 2014-03-08 18:47:11Z markagreenwood $
015  */
016 
017 package gate.util;
018 
019 import gate.Gate;
020 
021 import java.io.File;
022 import java.io.PrintStream;
023 import java.lang.reflect.Constructor;
024 import java.net.JarURLConnection;
025 import java.net.URL;
026 import java.util.ArrayList;
027 import java.util.Date;
028 import java.util.Enumeration;
029 import java.util.List;
030 import java.util.jar.JarEntry;
031 import java.util.jar.JarFile;
032 
033 public class Tools {
034 
035   /** Debug flag */
036   private static final boolean DEBUG = false;
037 
038   public Tools() {
039   }
040   static long sym=0;
041 
042   /** Returns a Long wich is unique during the current run.
043     * Maybe we should use serializaton in order to save the state on
044     * System.exit...
045     */
046   static public synchronized Long gensym(){
047     return new Long(sym++);
048   }
049 
050   static public synchronized Long genTime(){
051 
052     return new Long(new Date().getTime());
053   }
054 
055 
056   /** Specifies whether Gate should or shouldn't know about Unicode */
057   static public void setUnicodeEnabled(boolean value){
058     unicodeEnabled = value;
059   }
060 
061   /** Checks wheter Gate is Unicode enabled */
062   static public boolean isUnicodeEnabled(){
063     return unicodeEnabled;
064   }
065 
066   /** Does Gate know about Unicode? */
067   static private boolean unicodeEnabled = false;
068 
069 
070   /**
071    * Finds all subclasses of a given class or interface. It will only search
072    * within the loaded packages and not the entire classpath.
073    @param parentClass the class for which subclasses are sought
074    @return a list of {@link Class} objects.
075    */
076   static public List<Class<?>> findSubclasses(Class<?> parentClass){
077     Package[] packages = Package.getPackages();
078     List<Class<?>> result = new ArrayList<Class<?>>();
079     for(int i = 0; i < packages.length; i++){
080       String packageDir = packages[i].getName();
081       //look in the file system
082       if(!packageDir.startsWith("/")) packageDir = "/" + packageDir;
083       packageDir = packageDir.replace('.', Strings.getPathSep().charAt(0));
084       URL packageURL = Gate.getClassLoader().getResource(packageDir);
085       if(packageURL != null){
086         File directory = Files.fileFromURL(packageURL);
087         if(directory.exists()){
088           String [] files = directory.list();
089           for (int j=0; j < files.length; j++){
090             // we are only interested in .class files
091             if(files[j].endsWith(".class")){
092               // removes the .class extension
093               String classname = files[j].substring(0, files[j].length() 6);
094               try {
095                 // Try to create an instance of the object
096                 Class<?> aClass = Class.forName(packages[i"." + classname,
097                                              true, Gate.getClassLoader());
098                 if(parentClass.isAssignableFrom(aClass)) result.add(aClass);
099               }catch(ClassNotFoundException cnfex){}
100             }
101           }
102         }else{
103           //look in jar files
104           try{
105             JarURLConnection conn = (JarURLConnection)packageURL.openConnection();
106             String starts = conn.getEntryName();
107             JarFile jFile = conn.getJarFile();
108             Enumeration<JarEntry> e = jFile.entries();
109             while (e.hasMoreElements()){
110               String entryname = e.nextElement().getName();
111               if (entryname.startsWith(starts&&
112                   //not sub dir
113                   (entryname.lastIndexOf('/')<=starts.length()) &&
114                   entryname.endsWith(".class")){
115                 String classname = entryname.substring(0, entryname.length() 6);
116                 if (classname.startsWith("/")) classname = classname.substring(1);
117                 classname = classname.replace('/','.');
118                 try {
119                   // Try to create an instance of the object
120                   Class<?> aClass = Class.forName(packages[i"." + classname,
121                                                true, Gate.getClassLoader());
122                   if(parentClass.isAssignableFrom(aClass)) result.add(aClass);
123                 }catch(ClassNotFoundException cnfex){}
124               }
125             }
126           }catch(java.io.IOException ioe){}
127         }
128       }
129     }
130     return result;
131   }
132   
133   /**
134    <p>Find the constructor to use to create an instance of
135    <code>targetClass</code> from one of <code>paramClass</code>.
136    * We use the same rules as Java in finding the constructor
137    * to call, i.e. we find the constructor that javac would call for
138    * the expression
139    <code>parameterValue = new PropertyType(parameterValue)</code>:</p>
140    <ol>
141    <li>find all the single-argument constructors for propertyType
142    * whose argument type <code>T1</code> is assignable from
143    <code>paramType</code></li>
144    <li>from these, choose the one whose argument type <code>T2</code>
145    * is more specific than the argument type <code>S</code> of every
146    * other one i.e.
147    * for all S, <code>S.isAssignableFrom(T2)</code></li>
148    </ol>
149    <p>If there is no applicable single argument constructor that is
150    * more specific than all the others, then the above expression
151    * would be a compile error (constructor <code>ParamType(X)</code> is
152    * ambiguous).</p>
153    *
154    <p>(The "most specific" check is to catch situations such as
155    * paramClass implements <code>SortedSet</code>,
156    * targetClass = <code>TreeSet</code>.  Here both 
157    <code>TreeSet(SortedSet)</code> and <code>TreeSet(Collection)</code>
158    * are applicable but the former is more specific.  However, if
159    * paramType also implements <code>Comparator</code> then there are
160    * three applicable constructors, taking <code>SortedSet</code>,
161    <code>Collection</code> and <code>Comparator</code> respectively
162    * and none of these types is assignable to both the others.  This
163    * is ambiguous according to the Java Language Specification,
164    * 2nd edition, section 15.12.2)</p>
165    *
166    @param targetClass the class we wish to construct
167    @param paramClass the type of the object to pass as a parameter to
168    *         the constructor.
169    @return the most specific constructor of <code>targetClass</code>
170    *         that is applicable to an argument of <code>paramClass</code>
171    @throws NoSuchMethodException if there are no applicable constructors,
172    *         or if there is no single most-specific one.
173    */
174   public static Constructor<?> getMostSpecificConstructor(Class<?> targetClass,
175           Class<?> paramClassthrows NoSuchMethodException {
176     if(targetClass.isInterface()) {
177       throw new NoSuchMethodException(targetClass.getName() +
178               " is an interface, so cannot have constructors");
179     }
180     Constructor<?>[] targetClassConstructors =
181         targetClass.getConstructors();
182     List<Constructor<?>> applicableConstructors =
183         new ArrayList<Constructor<?>>();
184     // find all applicable constructors
185     for(Constructor<?> c : targetClassConstructors) {
186       Class<?>[] constructorParams = c.getParameterTypes();
187       if(constructorParams.length == 1
188               && constructorParams[0].isAssignableFrom(paramClass)) {
189         applicableConstructors.add(c);
190       }
191     }
192     // simple cases - none applicable
193     if(applicableConstructors.size() == 0) {
194       throw new NoSuchMethodException(
195               "No applicable constructors for "
196               + targetClass.getName() "("
197               + paramClass.getName() ")");
198     }
199     Constructor<?> mostSpecificConstructor = null;
200     // other simple case - only one applicable
201     if(applicableConstructors.size() == 1) {
202       mostSpecificConstructor = applicableConstructors.get(0);
203     }
204     else {
205       // complicated case - need to find the most specific.
206       // since the constructor parameter types are all assignable
207       // from paramType there can be at most one that is assignable
208       // from *all* others
209       C1: for(Constructor<?> c1 : applicableConstructors) {
210         Class<?> c1ParamType = c1.getParameterTypes()[0];
211         for(Constructor<?> c2 : applicableConstructors) {
212           Class<?> c2ParamType = c2.getParameterTypes()[0];
213           if(!c2ParamType.isAssignableFrom(c1ParamType)) {
214             continue C1;
215           }
216         }
217         mostSpecificConstructor = c1;
218         break C1;
219       }
220     }
221     // we tried all the constructors and didn't find the most-specific
222     // one
223     if(mostSpecificConstructor == null) {
224       if(DEBUG) {
225         Out.println("Ambiguous constructors for "
226               + targetClass.getName() "("
227               + paramClass.getName() ")");
228         Out.println("Choice was between " + applicableConstructors);
229       }
230       throw new NoSuchMethodException(
231               "Ambiguous constructors for "
232               + targetClass.getName() "("
233               + paramClass.getName() ")");
234     }
235     
236     if(DEBUG) {
237       Out.println("Most specific constructor for " +
238               targetClass.getName() "(" + paramClass.getName() ") is "
239               + mostSpecificConstructor);
240     }
241     return mostSpecificConstructor;
242   }
243   
244   /**
245    * Prints the stack trace of the current thread to the specified print stream.
246    @param pStream
247    */
248   public static final void printStackTrace(PrintStream pStream){
249     StackTraceElement stackTraceElems[] = Thread.currentThread().getStackTrace();
250     for(StackTraceElement ste : stackTraceElems){
251       pStream.println(ste.toString());
252     }
253   }
254 // class Tools