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