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.8 2002/10/21 16:21:17 valyt Exp $
13   */
14  package gate.util;
15  
16  
17  import com.sun.tools.javac.v8.util.*;
18  import com.sun.tools.javac.v8.comp.*;
19  import com.sun.tools.javac.v8.code.*;
20  import com.sun.tools.javac.v8.*;
21  
22  import java.io.*;
23  import java.util.Map;
24  import java.util.ArrayList;
25  
26  import gate.util.*;
27  import gate.*;
28  
29  /**
30   * This class copiles a set of java sources by accessing the java compiler
31   * from tools.jar file in the jdk.
32   * All processing is done without touching the disk.
33   */
34  public class Javac{
35  
36    protected static class MemoryLog extends Log{
37      MemoryLog(Map sources){
38        super();
39        this.sources = sources;
40        errorsString = new StringBuffer();
41      }
42  
43      public void error(int pos, String key, String arg0, String arg1,
44                        String arg2, String arg3, String arg4, String arg5,
45                        String arg6) {
46        if(nerrors < MaxErrors){
47          String msg = getText("compiler.err." + key, arg0, arg1, arg2, arg3,
48                  arg4, arg5, arg6);
49  
50          if (pos == Position.NOPOS) {
51            errorsString.append(getText("compiler.err.error", null, null, null,
52                                    null, null, null, null));
53            errorsString.append(msg + Strings.getNl());
54          }else{
55            int line = Position.line(pos);
56            int col = Position.column(pos);
57            errorsString.append("Compilation error in " +
58                                className + ":" + line + ": " + msg +
59                                Strings.getNl() + Strings.getNl());
60  
61            String sourceCode = (String)sources.get(className);
62            errorsString.append("The offending input was :" + Strings.getNl() +
63                                (sourceCode == null || sourceCode.equals("") ?
64                                 "<not available>" :
65                                 Strings.addLineNumbers(sourceCode)) +
66                                Strings.getNl());
67          }
68          prompt();
69          nerrors++;
70        }
71      }
72  
73      //redirect automatic error reporting from System.err to memory
74      public void print(String s) {
75        errorsString.append(s);
76      }//print
77      Map sources;
78      StringBuffer errorsString;
79      String className;
80    }
81  
82    protected static class GJC extends JavaCompiler{
83      GJC(MemoryLog log, Symtab syms, Hashtable options, Map sources){
84        super(log, syms, options);
85        this.sources = sources;
86        this.memLog = log;
87      }
88  
89      /**
90       * Overidden so that it reads the sources from the provided Map rather than
91       * from the disk.
92       * @param fileName the name of the file that should contain the source.
93       * @return an input stream for the java source.
94       */
95      public InputStream openSource(String fileName) {
96  //Out.prln("Read request for: " + fileName);
97        String className = fileName.substring(0, fileName.lastIndexOf(".java"));
98        className = className.replace('/', '.');
99        className = className.replace('\\', '.');
100       String classSource = (String)sources.get(className);
101       memLog.className = className;
102 //Out.prln("Source for: " + className + "\n" + classSource);
103       return new ByteArrayInputStream(classSource.getBytes());
104     }
105 
106     void printCount(String kind, int count) {
107       System.out.println("Count: " + count);
108     }
109 
110     /**
111      * Overidden so it loads the compiled class in the gate classloader rather
112      * than writting it on the disk.
113      * @param c the class symbol
114      * @throws IOException
115      */
116     public void writeClass(com.sun.tools.javac.v8.code.Symbol.ClassSymbol c)
117                 throws IOException {
118       //the compiler will try to write the class file too;
119       //we'll just load the class instead
120       ByteArrayOutputStream os = new ByteArrayOutputStream(4000);
121       new ClassWriter(Hashtable.make()).writeClassFile(os, c);
122       os.flush();
123       byte[] bytes = os.toByteArray();
124 //      String className = c.className();
125       //this is the full name with all the $ signs in the right place
126       String className = c.flatName().toJava();
127 
128 //      //replace the final '.' with '$' for inner classes
129 //      if(c.isInner()){
130 //        int loc = className.lastIndexOf('.');
131 //        if(loc != -1) className = className.substring(0, loc) + "$" +
132 //                                  className.substring(loc + 1);
133 //      }
134 //Out.pr(className + "[" + os.size() + " bytes]");
135       Gate.getClassLoader().defineGateClass(className,
136                                             bytes, 0, os.size());
137     }
138 
139     Map sources;
140     MemoryLog memLog;
141   }
142 
143   /**
144    * Compiles a set of java sources and loads the compiled classes in the gate
145    * class loader.
146    * @param sources a map from fully qualified classname to java source
147    * @throws GateException in case of a compilation error or warning.
148    * In the case of warnings the compiled classes are loaded before the error is
149    * raised.
150    */
151   public static void loadClasses(Map sources)
152     throws GateException{
153     //build the compiler
154     Hashtable options = Hashtable.make();
155     MemoryLog log = new MemoryLog(sources);
156 
157     options.put("-classpath", System.getProperty("java.class.path"));
158 
159 
160     JavaCompiler compiler = new GJC(log,
161                                     new Symtab(new ClassReader(options),
162                                                new ClassWriter(options)),
163                                     options,
164                                     sources);
165 
166     //we have the compiler, let's put it to work
167     ArrayList classNames = new ArrayList(sources.keySet());
168     for(int i = 0; i < classNames.size(); i++){
169       String className = (String)classNames.get(i);
170       String filename = className.replace('.',
171                                           Strings.getFileSep().charAt(0));
172       classNames.set(i, filename + ".java" );
173     }
174 
175     try{
176       compiler.compile(List.make(classNames.toArray()));
177     }catch(Throwable t){
178       throw new GateException(t);
179     }
180 
181     //check for errors and warnings
182     if(log.errorsString != null && log.errorsString.length() > 0){
183       throw new GateException(log.errorsString.toString());
184     }
185   }
186 
187 }