KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > java > source > builder > Builder


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.java.source.builder;
21
22 import com.sun.source.tree.CompilationUnitTree;
23 import com.sun.tools.javac.api.JavacTaskImpl;
24 import com.sun.tools.javac.parser.EndPosParser;
25 import org.netbeans.api.java.source.query.CommentHandler;
26 import org.netbeans.modules.java.source.engine.RootTree;
27 import org.netbeans.modules.java.source.engine.BuildProgress;
28 import org.netbeans.modules.java.source.engine.ElapsedTimer;
29 import org.netbeans.modules.java.source.builder.DefaultSourceFileFilter;
30 import org.netbeans.modules.java.source.engine.ReattributionException;
31 import org.netbeans.modules.java.source.save.SourceBuffer;
32 import org.netbeans.modules.java.source.builder.SourceFileList;
33 import org.netbeans.modules.java.source.builder.SourceFileFilter;
34 import com.sun.tools.javac.comp.*;
35 import com.sun.tools.javac.code.Symbol.*;
36 import com.sun.tools.javac.jvm.ClassReader;
37 import com.sun.tools.javac.parser.Parser;
38 import com.sun.tools.javac.tree.*;
39 import com.sun.tools.javac.tree.JCTree.*;
40 import com.sun.tools.javac.util.*;
41 import java.io.*;
42 import java.util.ArrayList JavaDoc;
43 import java.util.Collections JavaDoc;
44 import java.util.logging.*;
45 import javax.tools.JavaCompiler;
46 import javax.tools.JavaFileManager;
47 import javax.tools.JavaFileObject;
48 import javax.tools.StandardJavaFileManager;
49 import javax.tools.ToolProvider;
50 import org.netbeans.api.java.source.ClasspathInfo;
51
52 /**
53  * This JavaCompiler replacement turns off code generation, and can
54  * read simplified Jar URLs as supplied by SourceFileList.
55  *
56  */

57 public final class Builder implements ClassReader.SourceCompleter {
58     private Log log;
59     private Context context;
60     private Enter enter;
61     private TreeMaker make;
62     private CommentHandlerService comments;
63     private Parser.Factory parserFactory;
64     private Scanner.Factory scannerFactory;
65     private ASTService trees;
66     private String JavaDoc encoding;
67     private String JavaDoc[] sourcePaths;
68     private BuildProgress progressMonitor;
69     private int nfiles;
70     private JavaFileManager fileManager;
71
72     static final Logger logger = Logger.getLogger("org.netbeans.modules.java.source");
73     static boolean dumpAST = Boolean.getBoolean("jackpot.dump.ast");
74
75     /**
76      * Build the abstract syntax tree structure for a list of filenames.
77      *
78      * Note: this method can only be called once per Context, due to
79      * unresolved reattribution issues.
80      *
81      * @return the number of compilation errors that occurred during the build.
82      */

83     public int build() throws Exception JavaDoc {
84         ElapsedTimer timer = new ElapsedTimer();
85     try {
86             progressMonitor.buildStarted();
87             progressMonitor.sourceExpansionStarting();
88             String JavaDoc[] filenames = SourceFileList.findFiles(sourcePaths, sourceFileFilter);
89             nfiles = filenames.length;
90             progressMonitor.sourceExpansionFinished();
91             if (nfiles == 0)
92                 return 0; // if no files, then no errors!
93
compile(List.from(filenames));
94     } finally {
95         close();
96             timer.stop();
97             progressMonitor.buildFinished(errorCount(), warningCount(), timer.getElapsedMilliseconds());
98             progressMonitor = null;
99     }
100     return errorCount();
101     }
102
103     /**
104      * Create a new Builder for a specified context and sourcepath(s).
105      *
106      * @param ctx this build's context.
107      * @param sourcePaths the list of paths to search for source files.
108      * @param expandPaths if true, recursively search any directories for
109      * source files.
110      * @param progress the BuildProgress instance to which notifications
111      * of build progress is sent.
112      */

113     Builder(JavacTaskImpl task, String JavaDoc[] sourcePaths, BuildProgress progress) {
114     context = task.getContext();
115         this.sourcePaths = sourcePaths;
116         this.progressMonitor = progress;
117     log = Log.instance(context);
118     enter = (Enter)Enter.instance(context);
119         enter.setBuildProgress(progress);
120         make = TreeMaker.instance(context);
121         comments = CommentHandlerService.instance(context);
122     parserFactory = ParserFactory.instance(context);
123     scannerFactory = (Scanner.Factory)Scanner.Factory.instance(context);
124         trees = ASTService.instance(context);
125         fileManager = context.get(JavaFileManager.class);
126         assert this.fileManager instanceof StandardJavaFileManager;
127     }
128
129     static JavacTaskImpl createJavacTask(String JavaDoc sourcepath, String JavaDoc classpath, String JavaDoc bootclasspath, String JavaDoc sourceLevel, String JavaDoc encoding) {
130         ArrayList JavaDoc<String JavaDoc> options = new ArrayList JavaDoc<String JavaDoc>();
131         options.add("-XDbackgroundCompilation"); //NOI18N
132
options.add("-XDcompilePolicy=attr"); //NOI18N
133
options.add("-source"); // NOI18N
134
if (sourceLevel == null)
135             sourceLevel = "1.5";
136         options.add(sourceLevel);
137         options.add("-classpath");
138         options.add(classpath);
139         if (bootclasspath != null) {
140             options.add("-bootclasspath");
141             options.add(bootclasspath);
142         }
143         if (encoding != null && encoding.length() > 0) {
144             options.add("-encoding");
145             options.add(encoding);
146         }
147     String JavaDoc maxInt = Integer.toString(Integer.MAX_VALUE);
148     options.add("-Xmaxerrs");
149         options.add(maxInt);
150
151         ClassLoader JavaDoc orig = Thread.currentThread().getContextClassLoader();
152         try {
153             //The ToolProvider.defaultJavaCompiler will use the context classloader to load the javac implementation
154
//it should be load by the current module's classloader (should delegate to other module's classloaders as necessary)
155
Thread.currentThread().setContextClassLoader(ClasspathInfo.class.getClassLoader());
156             JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
157             JavacTaskImpl task = (JavacTaskImpl)tool.getTask(null, null, null, options, null, Collections.<JavaFileObject>emptySet());
158             Enter.register(task.getContext());
159             return task;
160         } finally {
161             Thread.currentThread().setContextClassLoader(orig);
162         }
163     }
164     
165     public int errorCount() {
166     return log.nerrors;
167     }
168     
169     public int warningCount() {
170         return log.nwarnings;
171     }
172     
173     public int sourcefileCount() {
174         return nfiles;
175     }
176
177     private boolean hasBeenUsed = false;
178     
179     protected List<ClassSymbol> compile(List<String JavaDoc> filenames) throws Exception JavaDoc {
180     assert !hasBeenUsed : "attempt to reuse Builder";
181     hasBeenUsed = true;
182
183     ListBuffer<ClassSymbol> classes = new ListBuffer<ClassSymbol>();
184     try {
185         //parse all files
186
progressMonitor.setTotalSourceFiles(filenames.size());
187         ListBuffer<CompilationUnitTree> trees = new ListBuffer<CompilationUnitTree>();
188             for (JavaFileObject jfo : ((StandardJavaFileManager)fileManager).getJavaFileObjectsFromStrings(filenames)) {
189         trees.append(parse(jfo));
190                 progressMonitor.fileParsed();
191             }
192             progressMonitor.parsingFinished();
193             
194             List<CompilationUnitTree> treeList = trees.toList();
195             if (dumpAST)
196                 dumpTrees(treeList, comments, "parse");
197
198             //enter symbols for all files
199
if (errorCount() == 0)
200                 attributeTrees(context, treeList, progressMonitor);
201             
202             if (dumpAST)
203                 dumpTrees(treeList, comments, "attr");
204     } catch (Abort ex) {
205     }
206
207     return classes.toList();
208     }
209
210     /**
211      * Enter JCCompilationUnit roots into symbol table and attribute. This
212      * method is static so it can be also be called during reattribution
213      * without keeping a Builder instance around.
214      */

215     static void attributeTrees(Context context, java.util.List JavaDoc<CompilationUnitTree> roots,
216                                BuildProgress progressMonitor) throws ReattributionException {
217         
218     Log log = Log.instance(context);
219     Enter enter = (Enter)Enter.instance(context);
220     Todo todo = Todo.instance(context);
221         ASTService model = ASTService.instance(context);
222         model.setRoot(new RootTree(roots));
223
224         //enter symbols for all files
225
progressMonitor.setTotalTrees(roots.size());
226         ListBuffer<JCCompilationUnit> buf = new ListBuffer<JCCompilationUnit>();
227         for (CompilationUnitTree t : roots)
228             buf.append((JCCompilationUnit)t);
229         try {
230             enter.main(buf.toList());
231         } catch (IllegalArgumentException JavaDoc e) {
232             log.error(e.getLocalizedMessage());
233         }
234         progressMonitor.enterFinished();
235
236         if (log.nerrors == 0) {
237             progressMonitor.setTotalClasses(todo.length());
238             Attr attr = Attr.instance(context);
239             while (todo.nonEmpty()) {
240                 Env<AttrContext> env = todo.next();
241                 JavaFileObject classname = env.enclClass.sym.sourcefile;
242                 log.useSource(classname);
243                 try {
244                     attr.attribClass(env.tree.pos(), env.enclClass.sym);
245                 } catch (AssertionError JavaDoc e) {
246                     //FIXME: javac bug? log and continue for now...
247
System.err.println("Problem attributing " + env.toplevel.sourcefile);
248                 }
249                 progressMonitor.classAttributed();
250             }
251             progressMonitor.attributionFinished();
252         }
253     }
254
255     protected void close() {
256     }
257
258     /** Parse contents of file.
259      * @param fileobject The file to be parsed.
260      */

261     protected JCCompilationUnit parse(JavaFileObject fileobject) {
262         try {
263             return parse(fileobject, fileobject.openInputStream());
264         } catch (IOException e) {
265             log.error(Position.NOPOS,
266                       "error.reading.file", fileobject, e);
267         }
268         return make.TopLevel(List.<JCAnnotation>nil(), null, List.<JCTree>nil());
269     }
270
271     /** Parse contents of input stream.
272      * @param filename The name of the file from which input stream comes.
273      * @param input The input stream to be parsed.
274      */

275     protected JCCompilationUnit parse(JavaFileObject filename, InputStream input) {
276     JavaFileObject prev = log.useSource(filename);
277     JCCompilationUnit tree = make.TopLevel(List.<JCAnnotation>nil(), null, List.<JCTree>nil());
278         tree.sourcefile = filename;
279         if (input != null) {
280         try {
281                 SourceBuffer sbuf = encoding != null ?
282                     new SourceBuffer(input, encoding) : new SourceBuffer(input);
283         input.close();
284                 BufferRunQueue runs = new BufferRunQueue();
285         Scanner scanner = (Scanner)scannerFactory.newScanner(sbuf, runs);
286         Parser parser = parserFactory.newParser(scanner, false, true);
287         tree = parser.compilationUnit();
288                 tree.sourcefile = filename;
289                 if (tree.endPositions != null)
290                     trees.setEndPosTable(filename, tree.endPositions);
291                 tree.lineMap = scanner.getLineMap();
292                 comments.mapComments(tree, sbuf, runs);
293         } catch (IOException e) {
294         log.error(Position.NOPOS,
295               "error.reading.file", filename, e);
296         }
297     }
298     log.useSource(prev);
299     return tree;
300     }
301
302     /** Complete compiling a source file that has been accessed
303      * by the class file reader.
304      *
305      * NOTE: this method should only be called by the classreader; it is public
306      * only because it is an interface method.
307      *
308      * @param c The class the source file of which needs to be compiled.
309      */

310     public void complete(ClassSymbol c) throws CompletionFailure {
311     JCCompilationUnit tree;
312     JavaFileObject filename = c.classfile;
313     JavaFileObject prev = log.useSource(filename);
314     try {
315             tree = parse(filename);
316     } finally {
317         log.useSource(prev);
318     }
319         enter.complete(List.of(tree), c);
320         if (enter.getEnv(c) == null) {
321             String JavaDoc msg = log.getLocalizedString("file.doesnt.contain.class",
322                                                 c.fullname);
323         throw new ClassReader.BadClassFile(c, filename, msg);
324         }
325     }
326
327     private void reportErrors() {
328     int errCount = errorCount();
329     String JavaDoc errCountResource =
330         "count." + ((errCount == 1) ? "error" : "error.plural");
331     Log.printLines(log.errWriter,
332                log.getLocalizedString(errCountResource,
333                           Integer.toString(errCount)));
334     String JavaDoc warnCountResource =
335         "count." + ((errCount == 1) ? "warn" : "warn.plural");
336     Log.printLines(log.errWriter,
337                log.getLocalizedString(warnCountResource,
338                           Integer.toString(errCount)));
339     log.errWriter.flush();
340     }
341
342     static void dumpTrees(java.util.List JavaDoc<CompilationUnitTree> trees, CommentHandler comments, String JavaDoc directory) {
343         logger.fine("dumping trees");
344         File dumpDir = makeDumpDirectory(directory);
345         for (CompilationUnitTree tl : trees) {
346             File src = new File(tl.getSourceFile().toUri().getPath());
347             File f = new File(dumpDir, src.getName());
348             try {
349                 FileOutputStream fout = new FileOutputStream(f);
350                 PrintStream out = new PrintStream(new BufferedOutputStream(fout));
351                 DumpAST.dump(tl, comments, out);
352                 out.flush();
353                 fout.close();
354             } catch (IOException e) {
355                 System.err.println("failed writing " + f.getPath() + ": " + e);
356             }
357         }
358     }
359     
360     private static File makeDumpDirectory(String JavaDoc directory) {
361     File dir;
362     File tmp = null;
363     try {
364         tmp = File.createTempFile("HOHO", null);
365         dir = new File(tmp.getParentFile(), "jackpot-" + directory);
366     } catch(IOException ioe) {
367         dir = new File("/tmp/jackpot-" + directory);
368     } finally {
369         if (tmp != null)
370         tmp.delete();
371     }
372     dir.mkdirs();
373         return dir;
374     }
375
376     /**
377      * Match on Java source files.
378      */

379     public static final SourceFileFilter sourceFileFilter =
380     new DefaultSourceFileFilter() {
381         public boolean acceptFile(String JavaDoc name) {
382         return super.acceptFile(name) && (name.endsWith(".java"));
383         }
384         public boolean acceptDirectory(String JavaDoc name) {
385         return super.acceptDirectory(name) && !name.equals("junk");
386         }
387     };
388
389     /**
390      * Parser subclass factory that doesn't fold string constants.
391      */

392     public static class ParserFactory extends Parser.Factory {
393         public static Parser.Factory instance(Context context) {
394             Parser.Factory instance = context.get(parserFactoryKey);
395             if (instance == null)
396                 instance = new ParserFactory(context);
397             return instance;
398         }
399
400         protected ParserFactory(Context context) {
401             super(context);
402         }
403
404         public Parser newParser(Scanner S,
405                                 boolean keepDocComments, boolean genEndPos) {
406             return new EndPosParser(this, S, keepDocComments) {
407                     protected StringBuffer JavaDoc foldStrings(JCTree tree) {
408                         // don't fold
409
return null;
410                     }
411                 };
412         }
413     }
414 }
415
Popular Tags