KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > rice > cs > drjava > model > compiler > DefaultCompilerModel


1 /* BEGIN_COPYRIGHT_BLOCK
2  *
3  * This file is part of DrJava. Download the current version of this project from http://www.drjava.org/
4  * or http://sourceforge.net/projects/drjava/
5  *
6  * DrJava Open Source License
7  *
8  * Copyright (C) 2001-2005 JavaPLT group at Rice University (javaplt@rice.edu). All rights reserved.
9  *
10  * Developed by: Java Programming Languages Team, Rice University, http://www.cs.rice.edu/~javaplt/
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
13  * documentation files (the "Software"), to deal with the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
15  * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
16  *
17  * - Redistributions of source code must retain the above copyright notice, this list of conditions and the
18  * following disclaimers.
19  * - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
20  * following disclaimers in the documentation and/or other materials provided with the distribution.
21  * - Neither the names of DrJava, the JavaPLT, Rice University, nor the names of its contributors may be used to
22  * endorse or promote products derived from this Software without specific prior written permission.
23  * - Products derived from this software may not be called "DrJava" nor use the term "DrJava" as part of their
24  * names without prior written permission from the JavaPLT group. For permission, write to javaplt@rice.edu.
25  *
26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
27  * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28  * CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
29  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
30  * WITH THE SOFTWARE.
31  *
32  *END_COPYRIGHT_BLOCK */

33
34 package edu.rice.cs.drjava.model.compiler;
35
36 import java.io.File JavaDoc;
37 import java.io.IOException JavaDoc;
38
39 import java.util.List JavaDoc;
40 import java.util.ArrayList JavaDoc;
41 import java.util.Arrays JavaDoc;
42 import java.util.Collections JavaDoc;
43 import java.util.HashSet JavaDoc;
44 import java.util.LinkedList JavaDoc;
45 import java.util.Iterator JavaDoc;
46 import java.util.StringTokenizer JavaDoc;
47
48 import edu.rice.cs.drjava.DrJava;
49 import edu.rice.cs.drjava.config.OptionConstants;
50
51 import edu.rice.cs.drjava.model.GlobalModel;
52 import edu.rice.cs.drjava.model.OpenDefinitionsDocument;
53 import edu.rice.cs.drjava.model.definitions.InvalidPackageException;
54
55 import edu.rice.cs.plt.io.IOUtil;
56 import edu.rice.cs.util.ClassPathVector;
57 import edu.rice.cs.util.swing.Utilities;
58 import edu.rice.cs.util.UnexpectedException;
59
60
61 import edu.rice.cs.javalanglevels.*;
62 import edu.rice.cs.javalanglevels.parser.*;
63 import edu.rice.cs.javalanglevels.tree.*;
64
65 /** Default implementation of the CompilerModel interface. This implementation is used for normal DrJava execution
66  * (as opposed to testing DrJava).
67  * @version $Id: DefaultCompilerModel.java 4075 2007-01-19 21:35:50Z dlsmith $
68  */

69 public class DefaultCompilerModel implements CompilerModel {
70   
71   /** Manages listeners to this model. */
72   private final CompilerEventNotifier _notifier = new CompilerEventNotifier();
73
74   /** The global model to which this compiler model belongs. */
75   private final GlobalModel _model;
76
77   /** The error model containing all current compiler errors. */
78   private CompilerErrorModel _compilerErrorModel;
79   
80   /** The working directory corresponding to the last compilation */
81   private File JavaDoc _workDir;
82   
83   /** The lock providing mutual exclustion between compilation and unit testing */
84   private Object JavaDoc _compilerLock = new Object JavaDoc();
85
86   /** Main constructor.
87     * @param m the GlobalModel that is the source of documents for this CompilerModel
88     */

89   public DefaultCompilerModel(GlobalModel m) {
90     _model = m;
91     _compilerErrorModel = new CompilerErrorModel(new CompilerError[0], _model);
92     _workDir = _model.getWorkingDirectory();
93   }
94   
95   //--------------------------------- Locking -------------------------------//
96

97   /** Returns the lock used to prevent simultaneous compilation and JUnit testing */
98   public Object JavaDoc getCompilerLock() { return _compilerLock; }
99
100   //-------------------------- Listener Management --------------------------//
101

102   /** Add a CompilerListener to the model.
103    * @param listener a listener that reacts to compiler events
104    *
105    * This operation is synchronized by the readers/writers protocol in EventNotifier<T>.
106    */

107   public void addListener(CompilerListener listener) { _notifier.addListener(listener); }
108
109   /** Remove a CompilerListener from the model. If the listener is not currently
110    * listening to this model, this method has no effect.
111    * @param listener a listener that reacts to compiler events
112    *
113    * This operation is synchronized by the readers/writers protocol in EventNotifier<T>.
114    */

115   public void removeListener(CompilerListener listener) { _notifier.removeListener(listener); }
116
117   /** Removes all CompilerListeners from this model. */
118   public void removeAllListeners() { _notifier.removeAllListeners(); }
119
120   //-------------------------------- Triggers --------------------------------//
121

122
123   /** Compile all open documents.
124    *
125    * <p>Before compiling, all unsaved and untitled documents are saved, and compilation ends if the user cancels this
126    * step. The compilation classpath and sourcepath includes the build directory (if it exists), the source roots,
127    * the project "extra classpath" (if it exists), the global "extra classpath", and the current JVM's classpath
128    * (which includes drjava.jar, containing JUnit classes).</p>
129    *
130    * This method formerly only compiled documents which were out of sync with their class file, as a performance
131    * optimization. However, bug #634386 pointed out that unmodified files could depend on modified files, in which
132    * case this command would not recompile a file in some situations when it should. Since we value correctness over
133    * performance, we now always compile all open documents.</p>
134    *
135    * @throws IOException if a filesystem-related problem prevents compilation
136    */

137   public void compileAll() throws IOException JavaDoc {
138     if (_prepareForCompile()) {
139       _doCompile(_model.getOpenDefinitionsDocuments());
140     }
141   }
142   
143    /** Compiles all documents in the project source tree. Assumes DrJava currently contains an active project.
144     *
145     * <p>Before compiling, all unsaved and untitled documents are saved, and compilation ends if the user cancels this
146     * step. The compilation classpath and sourcepath includes the build directory (if it exists), the source roots,
147     * the project "extra classpath" (if it exists), the global "extra classpath", and the current JVM's classpath
148     * (which includes drjava.jar, containing JUnit classes).</p>
149     *
150     * This method formerly only compiled documents which were out of sync with their class file, as a performance
151     * optimization. However, bug #634386 pointed out that unmodified files could depend on modified files, in which
152     * case this command would not recompile a file in some situations when it should. Since we value correctness over
153     * performance, we now always compile all open documents.</p>
154     *
155     * @throws IOException if a filesystem-related problem prevents compilation
156     */

157   public void compileProject() throws IOException JavaDoc {
158     if (! _model.isProjectActive())
159       throw new UnexpectedException("compileProject invoked when DrJava is not in project mode");
160     
161     if (_prepareForCompile()) {
162       _doCompile(_model.getProjectDocuments());
163     }
164   }
165   
166   /** Compiles all of the given files.
167    *
168    * <p>Before compiling, all unsaved and untitled documents are saved, and compilation ends if the user cancels this
169    * step. The compilation classpath and sourcepath includes the build directory (if it exists), the source roots,
170    * the project "extra classpath" (if it exists), the global "extra classpath", and the current JVM's classpath
171    * (which includes drjava.jar, containing JUnit classes).</p>
172    *
173    * This method formerly only compiled documents which were out of sync with their class file, as a performance
174    * optimization. However, bug #634386 pointed out that unmodified files could depend on modified files, in which
175    * case this command would not recompile a file in some situations when it should. Since we value correctness over
176    * performance, we now always compile all open documents.</p>
177    *
178    * @throws IOException if a filesystem-related problem prevents compilation
179    */

180   public void compile(List JavaDoc<OpenDefinitionsDocument> defDocs) throws IOException JavaDoc {
181     if (_prepareForCompile()) {
182       _doCompile(defDocs);
183     }
184   }
185   
186   /** Compiles the given file.
187     *
188     * <p>Before compiling, all unsaved and untitled documents are saved, and compilation ends if the user cancels this
189     * step. The compilation classpath and sourcepath includes the build directory (if it exists), the source roots,
190     * the project "extra classpath" (if it exists), the global "extra classpath", and the current JVM's classpath
191     * (which includes drjava.jar, containing JUnit classes).</p>
192     *
193     * This method formerly only compiled documents which were out of sync with their class file, as a performance
194     * optimization. However, bug #634386 pointed out that unmodified files could depend on modified files, in which
195     * case this command would not recompile a file in some situations when it should. Since we value correctness over
196     * performance, we now always compile all open documents.</p>
197     *
198     * @throws IOException if a filesystem-related problem prevents compilation
199     */

200   public void compile(OpenDefinitionsDocument doc) throws IOException JavaDoc {
201     if (_prepareForCompile()) {
202       _doCompile(Arrays.asList(doc));
203     }
204   }
205   
206   /** Check that there are no unsaved or untitled files currently open.
207     * @return @code{true} iff compilation should continue
208     */

209   private boolean _prepareForCompile() {
210     if (_model.hasModifiedDocuments()) _notifier.saveBeforeCompile();
211     // If user cancelled save, abort compilation
212
return !_model.hasModifiedDocuments();
213   }
214   
215   /** Compile the given documents. */
216   private void _doCompile(List JavaDoc<OpenDefinitionsDocument> docs) throws IOException JavaDoc {
217     ArrayList JavaDoc<File JavaDoc> filesToCompile = new ArrayList JavaDoc<File JavaDoc>();
218     ArrayList JavaDoc<File JavaDoc> excludedFiles = new ArrayList JavaDoc<File JavaDoc>();
219     for (OpenDefinitionsDocument doc : docs) {
220       if (doc.isSourceFile()) {
221         File JavaDoc f = doc.getFile();
222         // Check for null in case the file is untitled (not sure this is the correct check)
223
if (f != null) { filesToCompile.add(f); }
224         doc.setCachedClassFile(null); // clear cached class file
225
}
226       else excludedFiles.add(doc.getFile());
227     }
228     
229     File JavaDoc buildDir = _model.getBuildDirectory();
230     if ((buildDir!=null) && !buildDir.exists() && !buildDir.mkdirs()) {
231       throw new IOException JavaDoc("Could not create build directory: "+buildDir);
232     }
233
234     File JavaDoc workDir = _model.getWorkingDirectory();
235     if ((workDir != null) && ! workDir.exists() && ! workDir.mkdirs()) {
236       throw new IOException JavaDoc("Could not create working directory: "+workDir);
237     }
238      
239     _notifier.compileStarted();
240     try { _compileFiles(filesToCompile, buildDir); }
241     catch (Throwable JavaDoc t) {
242       CompilerError err = new CompilerError(t.toString(), false);
243       _distributeErrors(Arrays.asList(err));
244     }
245     finally { _notifier.compileEnded(workDir, excludedFiles); }
246   }
247   
248
249   //-------------------------------- Helpers --------------------------------//
250

251   /** Converts JExprParseExceptions thrown by the JExprParser in language levels to CompilerErrors. */
252   private LinkedList JavaDoc<CompilerError> _parseExceptions2CompilerErrors(LinkedList JavaDoc<JExprParseException> pes) {
253     LinkedList JavaDoc<CompilerError> errors = new LinkedList JavaDoc<CompilerError>();
254     Iterator JavaDoc<JExprParseException> iter = pes.iterator();
255     while (iter.hasNext()) {
256       JExprParseException pe = iter.next();
257       errors.addLast(new CompilerError(pe.getFile(), pe.currentToken.beginLine-1, pe.currentToken.beginColumn-1, pe.getMessage(), false));
258     }
259     return errors;
260   }
261   
262   /** Converts errors thrown by the language level visitors to CompilerErrors. */
263   private LinkedList JavaDoc<CompilerError> _visitorErrors2CompilerErrors(LinkedList JavaDoc<Pair<String JavaDoc, JExpressionIF>> visitorErrors) {
264     LinkedList JavaDoc<CompilerError> errors = new LinkedList JavaDoc<CompilerError>();
265     Iterator JavaDoc<Pair<String JavaDoc, JExpressionIF>> iter = visitorErrors.iterator();
266     while (iter.hasNext()) {
267       Pair<String JavaDoc, JExpressionIF> pair = iter.next();
268       String JavaDoc message = pair.getFirst();
269 // System.out.println("Got error message: " + message);
270
JExpressionIF jexpr = pair.getSecond();
271       
272       SourceInfo si;
273       if (jexpr == null) si = JExprParser.NO_SOURCE_INFO;
274       else si = pair.getSecond().getSourceInfo();
275       
276       errors.addLast(new CompilerError(si.getFile(), si.getStartLine()-1, si.getStartColumn()-1, message, false));
277     }
278     return errors;
279   }
280   
281   /** Compile the given files and update the model with any errors that result. Does not notify listeners.
282    * All public compile methods delegate to this one so this method is the only one that uses synchronization to
283    * prevent compiling and unit testing at the same time.
284    *
285    * @param files The files to be compiled
286    * @param buildDir The output directory for all the .class files; @code{null} means output to the same
287    * directory as the source file
288    *
289    */

290   private void _compileFiles(List JavaDoc<? extends File JavaDoc> files, File JavaDoc buildDir) throws IOException JavaDoc {
291     if (!files.isEmpty()) {
292       /* Canonicalize buildDir */
293       if (buildDir != null) buildDir = IOUtil.attemptCanonicalFile(buildDir);
294       
295       List JavaDoc<File JavaDoc> classPath = _model.getClassPath().asFileVector();
296       
297       // Temporary hack to allow a boot class path to be specified
298
List JavaDoc<File JavaDoc> bootClassPath = null;
299       if (System.getProperty("drjava.bootclasspath") != null) {
300         bootClassPath = new LinkedList JavaDoc<File JavaDoc>();
301         StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(System.getProperty("drjava.bootclasspath"), File.pathSeparator);
302         while (st.hasMoreTokens()) {
303           bootClassPath.add(new File JavaDoc(st.nextToken()));
304         }
305       }
306       
307       List JavaDoc<CompilerError> errors = new LinkedList JavaDoc<CompilerError>();
308       
309       List JavaDoc<? extends File JavaDoc> preprocessedFiles = _compileLanguageLevelsFiles(files, errors);
310       
311       if (errors.isEmpty()) {
312         CompilerInterface compiler = CompilerRegistry.ONLY.getActiveCompiler();
313         
314         synchronized(_compilerLock) {
315           if (preprocessedFiles == null) {
316             errors.addAll(compiler.compile(files, classPath, null, buildDir, bootClassPath, null, true));
317           }
318           else {
319             /** If compiling a language level file, do not show warnings, as these are not caught by the language level parser */
320             errors.addAll(compiler.compile(preprocessedFiles, classPath, null, buildDir, bootClassPath, null, false));
321           }
322         }
323       }
324       _distributeErrors(errors);
325     }
326     else {
327       // TODO: Is this necessary?
328
_distributeErrors(Collections.<CompilerError>emptyList());
329     }
330   }
331   
332   
333   /** Compiles the language levels files in the list. Adds any errors to the given error list.
334     * @return An updated list for compilation containing no Language Levels files, or @code{null}
335     * if there were no Language Levels files to process.
336     */

337   private List JavaDoc<? extends File JavaDoc> _compileLanguageLevelsFiles(List JavaDoc<? extends File JavaDoc> files, List JavaDoc<? super CompilerError> errors) {
338     // TODO: The classpath (and sourcepath, bootclasspath) should be an argument passed to Language Levels.
339
LanguageLevelConverter llc = new LanguageLevelConverter(getActiveCompiler().getName());
340     Pair<LinkedList JavaDoc<JExprParseException>, LinkedList JavaDoc<Pair<String JavaDoc, JExpressionIF>>> llErrors =
341       llc.convert(files.toArray(new File JavaDoc[0]));
342     
343     /* Rename any .dj0 files in files to be .java files, so the correct thing is compiled. The hashset is used to
344      * make sure we never send in duplicate files. This can happen if the java file was sent in along with the
345      * corresponding .dj* file. The dj* file is renamed to a .java file and thus we have two of the same file in
346      * the list. By adding the renamed file to the hashset, the hashset efficiently removes duplicates.
347      */

348     HashSet JavaDoc<File JavaDoc> javaFileSet = new HashSet JavaDoc<File JavaDoc>();
349     boolean containsLanguageLevels = false;
350     for (File JavaDoc f : files) {
351       File JavaDoc canonicalFile = IOUtil.attemptCanonicalFile(f);
352       String JavaDoc fileName = canonicalFile.getPath();
353       int lastIndex = fileName.lastIndexOf(".dj");
354       if (lastIndex != -1) {
355         containsLanguageLevels = true;
356         javaFileSet.add(new File JavaDoc(fileName.substring(0, lastIndex) + ".java"));
357       }
358       else { javaFileSet.add(canonicalFile); }
359     }
360     files = new LinkedList JavaDoc<File JavaDoc>(javaFileSet);
361     
362     errors.addAll(_parseExceptions2CompilerErrors(llErrors.getFirst()));
363     errors.addAll(_visitorErrors2CompilerErrors(llErrors.getSecond()));
364     if (containsLanguageLevels) { return files; }
365     else { return null; }
366   }
367   
368   /** Sorts the given array of CompilerErrors and divides it into groups based on the file, giving each group to the
369     * appropriate OpenDefinitionsDocument, opening files if necessary. Called immediately after compilations finishes.
370     */

371   private void _distributeErrors(List JavaDoc<? extends CompilerError> errors) throws IOException JavaDoc {
372 // resetCompilerErrors(); // Why is this done?
373
_compilerErrorModel = new CompilerErrorModel(errors.toArray(new CompilerError[0]), _model);
374     _model.setNumCompErrors(_compilerErrorModel.getNumCompErrors()); // cache number of compiler errors in global model
375
}
376
377   //----------------------------- Error Results -----------------------------//
378

379   /** Gets the CompilerErrorModel representing the last compile. */
380   public CompilerErrorModel getCompilerErrorModel() { return _compilerErrorModel; }
381
382   /** Gets the total number of errors in this compiler model. */
383   public int getNumErrors() { return getCompilerErrorModel().getNumErrors(); }
384   
385   /** Gets the total number of current compiler errors. */
386   public int getNumCompErrors() { return getCompilerErrorModel().getNumCompErrors(); }
387   
388   /** Gets the total number of current warnings. */
389   public int getNumWarnings() { return getCompilerErrorModel().getNumWarnings(); }
390
391   /** Resets the compiler error state to have no errors. */
392   public void resetCompilerErrors() {
393     // TODO: see if we can get by without this function
394
_compilerErrorModel = new CompilerErrorModel(new CompilerError[0], _model);
395   }
396
397   //-------------------------- Compiler Management --------------------------//
398

399   /**
400    * Returns all registered compilers that are actually available.
401    * That is, for all elements in the returned array, .isAvailable()
402    * is true.
403    * This method will never return null or a zero-length array.
404    * Instead, if no compiler is registered and available, this will return
405    * a one-element array containing an instance of
406    * {@link NoCompilerAvailable}.
407    *
408    * @see CompilerRegistry#getAvailableCompilers
409    */

410   public CompilerInterface[] getAvailableCompilers() {
411     return CompilerRegistry.ONLY.getAvailableCompilers();
412   }
413
414   /**
415    * Gets the compiler that is the "active" compiler.
416    *
417    * @see #setActiveCompiler
418    * @see CompilerRegistry#getActiveCompiler
419    */

420   public CompilerInterface getActiveCompiler() {
421     return CompilerRegistry.ONLY.getActiveCompiler();
422   }
423
424   /**
425    * Sets which compiler is the "active" compiler.
426    *
427    * @param compiler Compiler to set active.
428    *
429    * @see #getActiveCompiler
430    * @see CompilerRegistry#setActiveCompiler
431    */

432   public void setActiveCompiler(CompilerInterface compiler) {
433     CompilerRegistry.ONLY.setActiveCompiler(compiler);
434   }
435 }
436
Popular Tags