1   /*
2    *
3    *  Copyright (c) 1998-2001, The University of Sheffield.
4    *
5    *  This file is part of GATE (see http://gate.ac.uk/), and is free
6    *  software, licenced under the GNU Library General Public License,
7    *  Version 2, June 1991 (in the distribution as file licence.html,
8    *  and also available at http://gate.ac.uk/gate/licence.html).
9    *
10   *  Valentin Tablan, 18/Feb/2002
11   *
12   *  $Id: Javac.java,v 1.15 2003/07/29 12:46:53 valyt Exp $
13   */
14  package gate.util;
15  
16  import com.sun.tools.javac.Main;
17  
18  import java.io.*;
19  import java.util.*;
20  
21  import gate.util.*;
22  import gate.creole.ExecutionException;
23  import gate.*;
24  
25  /**
26   * This class copiles a set of java sources by accessing the java compiler
27   * from tools.jar file in the jdk.
28   * All processing is done without touching the disk.
29   */
30  public class Javac implements GateConstants{
31  
32    /**
33     * Compiles a set of java sources and loads the compiled classes in the gate
34     * class loader.
35     * @param sources a map from fully qualified classname to java source
36     * @throws GateException in case of a compilation error or warning.
37     * In the case of warnings the compiled classes are loaded before the error is
38     * raised.
39     */
40    public static void loadClasses(Map sources)throws GateException{
41      if(classLoader == null) classLoader = Gate.getClassLoader();
42      File workDir;
43      File srcDir;
44      File classesDir;
45      try{
46        workDir = File.createTempFile("gate", "");
47        if(!workDir.delete()) throw new GateRuntimeException(
48              "Cannot delete a temporary file!");
49        if(! workDir.mkdir())throw new GateRuntimeException(
50              "Cannot create a temporary directory!");
51        srcDir = new File(workDir, "src");
52        if(! srcDir.mkdir())throw new GateRuntimeException(
53              "Cannot create a temporary directory!");
54        classesDir = new File(workDir, "classes");
55        if(! classesDir.mkdir())throw new GateRuntimeException(
56              "Cannot create a temporary directory!");
57      }catch(IOException ioe){
58        throw new ExecutionException(ioe);
59      }
60  
61      List sourceFiles = new ArrayList();
62      List sourceListings = new ArrayList();
63  
64      Iterator fileIter = sources.keySet().iterator();
65      while(fileIter.hasNext()){
66        String className = (String)fileIter.next();
67        List pathComponents = getPathComponents(className);
68        String source = (String)sources.get(className);
69        File directory = getDirectory(srcDir, pathComponents);
70        String fileName = (String) pathComponents.get(pathComponents.size() - 1);
71        File srcFile = new File(directory, fileName + ".java");
72        try{
73          FileWriter fw = new FileWriter(srcFile);
74          fw.write(source);
75          fw.flush();fw.close();
76          sourceFiles.add(srcFile.getCanonicalPath());
77          sourceListings.add(source);
78        }catch(IOException ioe){
79          throw new GateException(ioe);
80        }
81      }
82      //all source files have now been saved to disk
83      //Prepare the arguments for the javac invocation
84      List args = new ArrayList();
85      args.add("-sourcepath");
86      args.add(srcDir.getAbsolutePath());
87      args.add("-d");
88      args.add(classesDir.getAbsolutePath());
89      //call the compiler for each class in turn so we can get the errors
90      //make a copy of the arguments in case we need to call calss by class
91      List argsSave = new ArrayList(args);
92      args.addAll(sourceFiles);
93      //steal the Err stream
94      PrintStream oldErr = System.err;
95      System.setErr(new PrintStream(new ByteArrayOutputStream()));
96  
97      int res = Main.compile((String[])args.toArray(new String[args.size()]));
98      //restore the err stream
99      System.setErr(oldErr);
100 
101     boolean errors = res != 0;
102     if(errors){
103       //we got errors: call class by class
104       args = argsSave;
105       for(int i = 0; i < sourceFiles.size(); i++){
106         String aSourceFile = (String)sourceFiles.get(i);
107         args.add(aSourceFile);
108         //call the compiler
109         res = Main.compile((String[])args.toArray(new String[args.size()]));
110         if(res != 0){
111           //javac writes the error to System.err; let's print the source as well
112           Err.prln("\nThe offending input was:\n");
113           String source = (String)sourceListings.get(i);
114           source = Strings.addLineNumbers(source);
115           Err.prln(source);
116         }
117         args.remove(args.size() -1);
118       }
119 
120     }
121 
122 //    args.addAll(sourceFiles);
123 
124     //load the newly compiled classes
125     //load all classes from the classes directory
126     try{
127       loadAllClasses(classesDir, null);
128     }catch(IOException ioe){
129       throw new GateException(ioe);
130     }
131 
132     //delete the work directory
133     Files.rmdir(workDir);
134 
135     if(errors) throw new GateException(
136           "There were errors; see error log for details!");
137   }
138 
139   /**
140    * Breaks a class name into path components.
141    * @param classname
142    * @return
143    */
144   protected static List getPathComponents(String classname){
145     //break the classname into pieces
146     StringTokenizer strTok = new StringTokenizer(classname, ".", false);
147     List pathComponents = new ArrayList();
148     while(strTok.hasMoreTokens()){
149       String pathComponent = strTok.nextToken();
150       pathComponents.add(pathComponent);
151     }
152     return pathComponents;
153   }
154 
155   /**
156    * Gets a file inside a parent directory from a list of path components.
157    * @param workDir
158    * @param pathComponents
159    * @return
160    */
161   protected static File getDirectory(File workDir, List pathComponents){
162     File currentDir = workDir;
163     for(int i = 0; i < pathComponents.size() - 1; i++){
164       String dirName = (String)pathComponents.get(i);
165       //create a new dir in the current directory
166       currentDir = new File(currentDir, dirName);
167       if(currentDir.exists()){
168         if(currentDir.isDirectory()){
169           //nothing to do
170         }else{
171           throw new GateRuntimeException(
172             "Path exists but is not a directory ( " +
173             currentDir.toString() + ")!");
174         }
175       }else{
176         if (!currentDir.mkdir())
177           throw new GateRuntimeException(
178               "Cannot create a temporary directory!");
179       }
180     }
181     return currentDir;
182   }
183 
184   /**
185    * Loads the entire hierarchy of classes found in a parent directory.
186    * @param classesDirectory
187    */
188   protected static void loadAllClasses(File classesDirectory,
189                                        String packageName) throws IOException{
190     File[] files = classesDirectory.listFiles();
191     //adjust the package name
192     if(packageName == null){
193       //top level directory -> not a package name
194       packageName = "";
195     }else{
196       //internal directory -> a package name
197       packageName += packageName.length() == 0 ?
198                      classesDirectory.getName() :
199                      "." + classesDirectory.getName();
200     }
201 
202     for(int i = 0; i < files.length; i++){
203       if(files[i].isDirectory()) loadAllClasses(files[i], packageName);
204       else{
205         String filename = files[i].getName();
206         if(filename.endsWith(".class")){
207           String className = packageName + "." +
208                              filename.substring(0, filename.length() - 6);
209           //load the class from the file
210           byte[] bytes = Files.getByteArray(files[i]);
211           classLoader.defineGateClass(className, bytes, 0, bytes.length);
212         }
213       }
214     }
215 
216   }
217   protected static GateClassLoader classLoader;
218 }
219