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 paramClass) throws 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
|