KickJava   Java API By Example, From Geeks To Geeks.

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


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 import javax.swing.text.*;
39 import java.util.Arrays JavaDoc;
40 import java.util.List JavaDoc;
41 import java.util.LinkedList JavaDoc;
42 import java.util.HashMap JavaDoc;
43
44 import edu.rice.cs.util.UnexpectedException;
45 import edu.rice.cs.drjava.model.DummyGlobalModel;
46 import edu.rice.cs.drjava.model.FileGroupingState;
47 import edu.rice.cs.drjava.model.GlobalModel;
48 import edu.rice.cs.util.OperationCanceledException;
49 import edu.rice.cs.drjava.model.OpenDefinitionsDocument;
50 import edu.rice.cs.drjava.model.FileMovedException;
51
52 /**
53  * Contains the CompilerErrors for a particular file after
54  * a compile has ended.
55  * @version $Id: CompilerErrorModel.java 4043 2006-11-22 23:04:59Z rcartwright $
56  */

57 public class CompilerErrorModel {
58   private static final String JavaDoc newLine = System.getProperty("line.separator");
59   /** An array of errors to be displayed in the CompilerErrorPanel associated with this model. After model
60    * construction, this array should be sorted in this order:
61    * (i) Errors with no file.
62    * (ii) Errors for each file in path-alphabetical order.
63    * Within each file:
64    * errors with no line number
65    * errors with line numbers, in order
66    * In all cases, where all else is equal, warnings are sorted below errors.
67    */

68   private final CompilerError[] _errors;
69
70   /** An array of file offsets, parallel to the _errors array. NOTE: If there is no position associated with an error,
71    * its entry here should be set to null.
72    */

73   private final Position[] _positions;
74
75   /** The size of _errors and _positions. This should never change after model construction*/
76   private final int _numErrors;
77   
78   /** The number of compile errors. Used for display purposes only.*/
79   private int _numCompilerErrors;
80   
81   /** The number of compile errors. Used for display purposes only.*/
82   private int _numWarnings;
83   
84
85   /** Cached result of hasOnlyWarnings.
86    * Three-state enum:
87    * -1 => result has not been computed
88    * 0 => false
89    * 1 => true
90    */

91   private int _onlyWarnings = -1;
92
93   /** Used internally in building _positions. The file used as the index *must* be a canonical file, or else
94    * errors won't always be associated with the right documents.
95    */

96   private final HashMap JavaDoc<File JavaDoc, StartAndEndIndex> _filesToIndexes = new HashMap JavaDoc<File JavaDoc, StartAndEndIndex>();
97
98   /** The global model which created/controls this object. */
99   private final GlobalModel _model;
100
101   /** Constructs an empty CompilerErrorModel with no errors and a dummy global model. */
102   public CompilerErrorModel() {
103     _model = new DummyGlobalModel() {
104       public OpenDefinitionsDocument getDocumentForFile(File JavaDoc file) {
105         throw new IllegalStateException JavaDoc("No documents to get!");
106       }
107       public boolean isAlreadyOpen(File JavaDoc file) { return false; }
108       public List JavaDoc<OpenDefinitionsDocument> getOpenDefinitionsDocuments() {
109         return new LinkedList JavaDoc<OpenDefinitionsDocument>();
110       }
111       public boolean hasModifiedDocuments() { return false; }
112       public boolean hasUntitledDocuments() { return false; }
113     };
114     _errors = new CompilerError[0];
115     _numErrors = 0;
116     _numWarnings = 0;
117     _numCompilerErrors = 0;
118     _positions = new Position[0];
119   }
120
121   /** Constructs a new CompilerErrorModel with specified global model. Performed in DefaultGlobalModel construction
122     * and after compilation has been performed.
123     * @param errors the list of CompilerError's (or a subclass).
124     * @param model is the model to find documents from
125     */

126   public CompilerErrorModel(CompilerError[] errors, GlobalModel model) {
127     _model = model;
128
129     // TODO: If we move to NextGen-style generics, ensure _errors is non-null.
130
_errors = errors;
131     
132     // Next two lines are order-dependent!
133
_numErrors = errors.length;
134     _positions = new Position[_numErrors];
135     
136     _numWarnings =0;
137     _numCompilerErrors = 0;
138     for (int i =0; i<errors.length; i++){
139       if (errors[i].isWarning()) _numWarnings++;
140       else _numCompilerErrors++;
141     }
142     
143     // Sort the errors by file and position
144
Arrays.sort(_errors);
145     
146     // Populates _positions.
147
_calculatePositions();
148   }
149
150   /**
151    * Accessor for errors maintained here.
152    * @param idx the index of the error to retrieve
153    * @return the error at index idx
154    * @throws NullPointerException if this object was improperly initialized
155    * @throws ArrayIndexOutOfBoundsException if !(0 <= idx < this.getNumErrors())
156    */

157   public CompilerError getError(int idx) { return _errors[idx]; }
158
159   /** Returns the position of the given error in the document representing its file. */
160   public Position getPosition(CompilerError error) {
161     int spot = Arrays.binarySearch(_errors, error);
162     return _positions[spot];
163   }
164
165   /** Returns the number of CompilerErrors. */
166   public int getNumErrors() { return _numErrors; }
167   
168   /** Returns the number of CompilerErrors that are compiler errors */
169   public int getNumCompErrors() { return _numCompilerErrors; }
170   
171   /** Returns the number of CompilerErrors that are warnings */
172   public int getNumWarnings() { return _numWarnings; }
173
174   /** Prints out this model's errors. */
175   public String JavaDoc toString() {
176     final StringBuilder JavaDoc buf = new StringBuilder JavaDoc();
177     buf.append(this.getClass().toString() + ":\n ");
178     for (int i=0; i < _numErrors; i++) {
179       buf.append(_errors[i].toString());
180       buf.append("\n ");
181     }
182     return buf.toString();
183   }
184
185   /** This method finds and returns the error that is at the given offset
186    * @param odd the OpenDefinitionsDocument where you want to find the error at the caret
187    * @param offset the offset into the document
188    * @return the CompilerError at the given offset, null if no error corresponds to this location
189    */

190   public CompilerError getErrorAtOffset(OpenDefinitionsDocument odd, int offset) {
191     File JavaDoc file;
192     try {
193       file = odd.getFile();
194       if (file == null) return null;
195     }
196     catch (FileMovedException e) { file = e.getFile(); }
197
198     // Use the canonical file if possible
199
try { file = file.getCanonicalFile(); }
200     catch (IOException JavaDoc ioe) {
201       // Oh well, we'll look for it as is.
202
}
203
204     StartAndEndIndex saei = _filesToIndexes.get(file);
205     if (saei == null) return null;
206     int start = saei.getStartPos();
207     int end = saei.getEndPos();
208     if (start == end) return null;
209
210     // check if the dot is on a line with an error.
211
// Find the first error that is on or after the dot. If this comes
212
// before the newline after the dot, it's on the same line.
213
int errorAfter; // index of the first error after the dot
214
for (errorAfter = start; errorAfter < end; errorAfter++) {
215       if (_positions[errorAfter] == null) {
216         //This indicates something wrong, but it was happening before so...
217
return null;
218       }
219       if (_positions[errorAfter].getOffset() >=offset) break;
220     }
221
222     // index of the first error before the dot
223
int errorBefore = errorAfter - 1;
224
225     // this will be set to what we want to select, or -1 if nothing
226
int shouldSelect = -1;
227
228     if (errorBefore >= start) { // there's an error before the dot
229
int errPos = _positions[errorBefore].getOffset();
230       try {
231         String JavaDoc betweenDotAndErr = odd.getText(errPos, offset - errPos);
232         if (betweenDotAndErr.indexOf('\n') == -1) shouldSelect = errorBefore;
233       }
234       catch (BadLocationException e) { /* source document has been edited; fail silently */ }
235     }
236
237     if ((shouldSelect == -1) && (errorAfter < end)) {// (errorAfter != _positions.length)) {
238
// we found an error on/after the dot
239
// if there's a newline between dot and error,
240
// then it's not on this line
241
int errPos = _positions[errorAfter].getOffset();
242       try {
243         String JavaDoc betweenDotAndErr = odd.getText(offset, errPos - offset);
244         if (betweenDotAndErr.indexOf('\n') == -1) shouldSelect = errorAfter;
245       }
246       catch (BadLocationException e) { /* source document has been edited; fail silently */ }
247     }
248
249     if (shouldSelect == -1) return null;
250     return _errors[shouldSelect];
251   }
252
253   /** This function tells if there are errors with source locations associated with the given file. */
254   public boolean hasErrorsWithPositions(OpenDefinitionsDocument odd) {
255     File JavaDoc file = null;
256     try {
257       file = odd.getFile();
258       if (file == null) return false;
259     }
260     catch (FileMovedException fme) { file = fme.getFile(); }
261     
262     // Try to use the canonical file
263
try { file = file.getCanonicalFile(); }
264     catch (IOException JavaDoc ioe) { /* Oh well, look for the file as is.*/ }
265
266     StartAndEndIndex saei = _filesToIndexes.get(file);
267     if (saei == null) return false;
268     if (saei.getStartPos() == saei.getEndPos()) return false;
269     return true;
270   }
271
272   /** Checks whether all CompilerErrors contained here are actually warnings. This would indicate that there were no
273    * "real" errors, so output is valid.
274    * @return false if any error contained here is not a warning, true otherwise
275    */

276   public boolean hasOnlyWarnings() {
277     // Check for a cached value.
278
if (_onlyWarnings == 0) return false;
279     if (_onlyWarnings == 1) return true;
280     else {
281       // If there was no cached value, compute it.
282
boolean clean = true;
283       for (int i = 0; clean && (i < _numErrors); i++) {
284         clean = _errors[i].isWarning();
285       }
286       // Cache the value.
287
_onlyWarnings = clean? 1: 0;
288       return clean;
289     }
290   }
291
292   /** Create array of positions where each error occurred. Positions are related their corresponding documents. */
293   private void _calculatePositions() {
294     try {
295       int curError = 0;
296
297       // for (; numProcessed < _numErrors; numProcessed++) {
298
while ((curError < _numErrors)) {
299         // find the next error with a line number (skipping others)
300
curError = nextErrorWithLine(curError);
301         if (curError >= _numErrors) {break;}
302
303         //Now find the file and document we are working on
304
File JavaDoc file = _errors[curError].file();
305         OpenDefinitionsDocument document;
306         try { document = _model.getDocumentForFile(file); }
307         catch (Exception JavaDoc e) {
308           // This is intended to catch IOException or OperationCanceledException
309
if ((e instanceof IOException JavaDoc) || (e instanceof OperationCanceledException)) {
310             // skip positions for these errors if the document couldn't be loaded
311
do { curError++;}
312             while ((curError < _numErrors) && (_errors[curError].file().equals(file)));
313             
314             //If the document couldn't be loaded, start the loop over at the top
315
continue;
316           }
317           else throw new UnexpectedException(e);
318         }
319         if (curError >= _numErrors) break;
320
321         // curError is the first error in a file, and its document is open.
322
final int fileStartIndex = curError;
323         final int defsLength = document.getLength();
324         final String JavaDoc defsText = document.getText(0, defsLength);
325         int curLine = 0;
326         int offset = 0; // offset is number of chars from beginning of file
327

328         // offset is always pointing to the first character in the line
329
// containing an error (or the last line of the previous file) at the top of this loop
330
while ((curError < _numErrors) && // we still have errors to find
331
file.equals(_errors[curError].file()) && // the next error is in this file
332
(offset <= defsLength)) { // we haven't gone past the end of the file
333

334           // create new positions for all errors on this line
335
while ((curError < _numErrors) && file.equals(_errors[curError].file()) && // we are still in this file
336
(_errors[curError].lineNumber() == curLine)) {
337             _positions[curError] = document.createPosition(offset + _errors[curError].startColumn());
338             curError++;
339           }
340
341           // At this point, offset is the starting index of the previous error's line.
342
// Update offset to be appropriate for the current error.
343
// ... but don't bother looking if it isn't in this file.
344
// ... or if we're done with all errors already.
345
if (curError < _numErrors) {
346             int curErrorLine = _errors[curError].lineNumber();
347             int nextNewline = 0;
348             while ((curLine != curErrorLine)
349                      && (nextNewline != -1)
350                      && (file.equals(_errors[curError].file()))) {
351               nextNewline = defsText.indexOf(newLine, offset);
352               if (nextNewline == -1) nextNewline = defsText.indexOf("\n", offset);
353               if (nextNewline != -1) {
354                 curLine++;
355                 offset = nextNewline + 1;
356               }
357             }
358           }
359         }
360
361         //Remember the indexes in the _errors and _positions arrays that
362
// are for the errors in this file
363
int fileEndIndex = curError;
364         if (fileEndIndex != fileStartIndex) {
365           // Try to use the canonical file if possible
366
try { file = file.getCanonicalFile(); }
367           catch (IOException JavaDoc ioe) { /* Oh well, store it as is */ }
368           _filesToIndexes.put(file, new StartAndEndIndex(fileStartIndex, fileEndIndex));
369         }
370       }
371     }
372     catch (BadLocationException ble) { throw new UnexpectedException(ble); }
373   }
374
375   /** Finds the first error after numProcessed which has a file and line number.
376    * @param idx the starting index of the search
377    * @return the index of the found error
378    */

379   private int nextErrorWithLine(int idx) {
380     while (idx < _numErrors && (_errors[idx].hasNoLocation() || _errors[idx].file() == null)) idx++;
381     return idx;
382   }
383
384   /** This class is used only to track where the errors with positions for a file begin and end. The beginning index
385    * is inclusive, the ending index is exclusive.
386    */

387   private static class StartAndEndIndex {
388     private int startPos;
389     private int endPos;
390
391     public StartAndEndIndex(int startPos, int endPos) {
392       this.startPos = startPos;
393       this.endPos = endPos;
394     }
395     public int getStartPos() { return startPos; }
396     public int getEndPos() { return endPos; }
397   }
398 }
399
Popular Tags