1 // Copyright (c) 1999, 2005  Per M.A. Bothner.
2 // This is free software;  for terms and warranty disclaimer see ./COPYING.
3 
4 package gnu.text;
5 
6 /** A collection of (zero or more) SourceErrors.
7  * Has a "current line number" which clients can use as the default line
8  * number, or clients can explicitly provide a line number.
9  * Does not handle localization of messages.
10  *
11  * Similar functionality as JAVA6's javax.tools.DiagnosticCollector.
12  */
13 
14 public class SourceMessages extends SourceLocator.Simple {
15     // Number of errors (not counting warnings).  A value of 1000 is "fatal".
16     private int errorCount = 0;
17 
18     /** The first error or warning in a linked list. */
19     SourceError firstError;
20     /** The last error or warning in a linked list. */
21     SourceError lastError;
22 
getErrors()23     public SourceError getErrors() { return firstError; }
24 
25     SourceLocator locator;
26 
27     public static boolean stripDirectoriesDefault = false;
28     public boolean stripDirectories = stripDirectoriesDefault;
29 
30     /** If true, print out stack trace with any warning. */
31     public static boolean debugStackTraceOnWarning = false;
32 
33     /** If true, print out stack trace with any error. */
34     public static boolean debugStackTraceOnError = false;
35 
36 
37     /** Return true iff errors (not warnings) have been seen. */
seenErrors()38     public boolean seenErrors() { return errorCount > 0; }
39 
seenErrorsOrWarnings()40     public boolean seenErrorsOrWarnings() { return firstError != null; }
41 
42     /** Get the number of errors (not counting warnings). */
getErrorCount()43     public int getErrorCount() { return errorCount; }
44 
45     /** Get number of diagnostics whose severity is one of the characters in the argument. */
getCount(String severities)46     public int getCount (String severities) {
47         int count = 0;
48         for (SourceError err = firstError; err != null;  err = err.next)
49         {
50             if (severities.indexOf(err.severity) >= 0)
51                 count++;
52         }
53         return count;
54     }
55 
56     /** Clear the error count (only). */
clearErrors()57     public void clearErrors() { errorCount = 0; }
58 
59     /** Clear the contained errors and warnings. */
clear()60     public void clear() {
61         firstError = lastError = null;
62         errorCount = 0;
63     }
64 
65     // The last SourceError with a *differnt* filename than prev has.
66     SourceError lastPrevFilename = null;
67 
68     /** True if we should sort messages by line number. */
69     public boolean sortMessages;
70 
71     /** Link in an error. */
error(SourceError error)72     public void error(SourceError error) {
73         if (error.severity == 'f')
74             errorCount = 1000;
75         else if (error.severity != 'w' && error.severity != 'i')
76             errorCount++;
77         if (error.fakeException == null
78             && ((SourceMessages.debugStackTraceOnError
79                  && (error.severity == 'e' || error.severity == 'f'))
80                 || (SourceMessages.debugStackTraceOnWarning
81                     && error.severity == 'w'))) {
82             error.fakeException = new Throwable();
83         }
84 
85         // Insert the next error so that line numbers are increasing.
86         if (lastError != null && lastError.filename != null
87             && ! lastError.filename.equals(error.filename))
88             lastPrevFilename = lastError;
89         SourceError prev = lastPrevFilename;
90         if (! sortMessages || error.severity == 'f')
91             prev = lastError;
92         else {
93             for (;;) {
94                 SourceError next;
95                 if (prev == null)
96                     next = firstError;
97                 else
98                     next = prev.next;
99                 if (next == null)
100                     break;
101                 int errline = error.getStartLine();
102                 int nextline = next.getStartLine();
103                 if (errline != 0 && nextline != 0) {
104                     if (errline < nextline)
105                         break;
106                     if (errline == nextline) {
107                         int errcol = error.getStartColumn();
108                         int nextcol = next.getStartColumn();
109                         if (errcol > 0 && nextcol > 0
110                             && errcol < nextcol)
111                             break;
112                     }
113                 }
114                 prev = next;
115             }
116         }
117         if (prev == null) {
118             error.next = firstError;
119             firstError = error;
120         } else {
121             error.next = prev.next;
122             prev.next = error;
123         }
124         if (prev == lastError)
125             lastError = error;
126     }
127 
128     /** Record a new error.
129      * @param severity is the seriousness of the error
130      *  - one of 'w' (for warning), 'e' (for error), or 'f' (for fatal error)
131      * @param filename the name or URL of the file containing the error
132      * @param line the (1-origin) line number or 0 if unknown
133      * @param column the (1-origin) column number or 0 if unknown
134      * @param message the error message
135      */
error(char severity, String filename, int line, int column, String message)136     public void error(char severity, String filename, int line, int column,
137                       String message) {
138         error(new SourceError(severity, filename, line, column, message));
139     }
140 
error(char severity, SourceLocator location, String message)141     public void error(char severity, SourceLocator location, String message) {
142         error(new SourceError(severity, location, message));
143     }
144 
error(char severity, String filename, int line, int column, String message, String code)145     public void error(char severity, String filename, int line, int column,
146                       String message, String code) {
147         SourceError err = new SourceError(severity, filename, line, column,
148                                           message);
149         err.code = code;
150         error(err);
151     }
152 
error(char severity, SourceLocator location, String message, String code)153     public void error(char severity, SourceLocator location,
154                       String message, String code) {
155         SourceError err = new SourceError(severity, location,  message);
156         err.code = code;
157         error(err);
158     }
159 
160     /** Record a new error at the current default source file location.
161      * @param severity is the seriousness of the error
162      *  - one of 'w' (for warning), 'e' (for error), or 'f' (for fatal error)
163      * @param message the error message
164      */
error(char severity, String message)165     public void error(char severity, String message) {
166         error(new SourceError(severity, this, message));
167     }
168 
error(char severity, String message, Throwable exception)169     public void error(char severity, String message, Throwable exception) {
170         SourceError err = new SourceError(severity, this, message);
171         err.fakeException = exception;
172         error(err);
173     }
174 
error(char severity, String message, String code)175     public void error(char severity, String message, String code) {
176         SourceError err = new SourceError(severity, this, message);
177         err.code = code;
178         error(err);
179     }
180 
181     /** Print all the error messages to an Appendable. */
printAll(Appendable out, int max)182     public void printAll(Appendable out, int max) {
183         int errCount = getCount("ef");
184         int wrnCount = getCount("iw");
185         int errLimit = max >= 0 && errCount > max ? max : errCount;
186         int wrnLimit = max >= 0 && errCount + wrnCount > max ? max - errLimit
187             : wrnCount;
188         int skippedErrors = 0;
189         int skippedWarnings = 0;
190         int skippedInfo = 0;
191         for (SourceError err = firstError;  err != null;  err = err.next) {
192             if (err.severity == 'e' && --errLimit < 0)
193                 skippedErrors++;
194             else if (err.severity == 'w' && --wrnLimit < 0)
195                 skippedWarnings++;
196             else if (err.severity == 'i' && --wrnLimit < 0)
197                 skippedInfo++;
198             else
199                 err.println(out, stripDirectories);
200         }
201         if (skippedErrors + skippedWarnings + skippedInfo > 0) {
202             SourceError err =
203                 new SourceError('i', firstError.getFileName(), 0, 0,
204                                 "skipped "+skippedErrors+" errors, "
205                                 +skippedWarnings+" warnings, "
206                                 +skippedInfo+" notes");
207             err.println(out, stripDirectories);
208         }
209     }
210 
211     /** Convert this to a String containing the recorded errors.
212      * @param max the maximum number of error error to list
213      * @return a String with one '\n'-terminated line per recorded error
214      */
toString(int max)215     public String toString(int max) {
216         if (firstError == null)
217             return null;
218         StringBuilder buffer = new StringBuilder();
219         for (SourceError err = firstError;
220              err != null && --max >= 0;  err = err.next) {
221             err.appendTo(buffer, stripDirectories, "\n");
222         }
223         return buffer.toString();
224     }
225 
226     /** Checks if an error was seen; if so, prints and clears the messages.
227      * @param out where to write the error message to
228      * @param max maximum number of messages to print (can be 0)
229      */
checkErrors(Appendable out, int max)230     public boolean checkErrors(Appendable out, int max) {
231         if (firstError != null) {
232             printAll(out, max);
233             firstError = lastError = null;
234             int saveCount = errorCount;
235             errorCount = 0;
236             return saveCount > 0;
237         }
238         return false;
239     }
240 
241     /** Links our location to the one give. */
setSourceLocator(SourceLocator locator)242     public final void setSourceLocator(SourceLocator locator) {
243         this.locator = locator == this ? null : locator;
244     }
245 
swapSourceLocator(SourceLocator locator)246     public final SourceLocator swapSourceLocator(SourceLocator locator) {
247         SourceLocator save = this.locator;
248         this.locator = locator;
249         return save;
250     }
251 
252     /** Copies the current position of locator. */
setLocation(SourceLocator locator)253     public final void setLocation(SourceLocator locator) {
254         this.locator = null;
255         super.setLocation(locator);
256     }
257 
getPublicId()258     public String getPublicId() {
259         return locator == null ? super.getPublicId() : locator.getPublicId();
260     }
getSystemId()261     public String getSystemId() {
262         return locator == null ? super.getSystemId() : locator.getSystemId();
263     }
264 
isStableSourceLocation()265     public boolean isStableSourceLocation() { return false; }
266 
267     /** The default filename to use for a new error. */
getFileName()268     public final String getFileName() {
269         return locator == null ? super.getFileName() : locator.getFileName();
270     }
271 
272     /** The default line number to use for a new error. */
getLineNumber()273     public final int getLineNumber() {
274         return locator == null ? super.getLineNumber() : locator.getLineNumber();
275     }
276 
277     /** The default column number to use for a new error. */
getColumnNumber()278     public final int getColumnNumber() {
279         return locator == null ? super.getColumnNumber() : locator.getColumnNumber();
280     }
getStartLine()281     public int getStartLine() {
282         return locator == null ? super.getStartLine() : locator.getStartLine();
283     }
getStartColumn()284     public int getStartColumn() {
285         return locator == null ? super.getStartColumn() : locator.getStartColumn();
286     }
getEndLine()287     public int getEndLine() {
288         return locator == null ? super.getEndLine() : locator.getEndLine();
289     }
getEndColumn()290     public int getEndColumn() {
291         return locator == null ? super.getEndColumn() : locator.getEndColumn();
292     }
293 
294     /** Set the default column number to use for a new error. */
setColumn(int column)295     public void setColumn(int column) { setLine(getLineNumber(), column); }
296 }
297