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