1 /*
   2  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.nashorn.internal.runtime;
  27 
  28 import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError;
  29 
  30 import java.io.PrintWriter;
  31 import jdk.nashorn.internal.parser.Token;
  32 
  33 /**
  34  * Handles JavaScript error reporting.
  35  */
  36 public class ErrorManager {
  37     // TODO - collect and sort/collapse error messages.
  38     // TODO - property based error messages.
  39     /** Reporting writer. */
  40     private final PrintWriter writer;
  41 
  42     /** Error count. */
  43     private int errors;
  44 
  45     /** Warning count */
  46     private int warnings;
  47 
  48     /** Limit of the number of messages. */
  49     private int limit;
  50 
  51     /** Treat warnings as errors. */
  52     private boolean warningsAsErrors;
  53 
  54     /**
  55      * Constructor
  56      */
  57     public ErrorManager() {
  58         this(new PrintWriter(System.err, true)); //bootstrapping, context may not be initialized
  59     }
  60 
  61     /**
  62      * Constructor.
  63      * @param writer I/O writer to report on.
  64      */
  65     public ErrorManager(final PrintWriter writer) {
  66         this.writer           = writer;
  67         this.limit            = 100;
  68         this.warningsAsErrors = false;
  69     }
  70 
  71     /**
  72      * Check to see if number of errors exceed limit.
  73      */
  74     private void checkLimit() {
  75         int count = errors;
  76 
  77         if (warningsAsErrors) {
  78             count += warnings;
  79         }
  80 
  81         if (limit != 0 && count > limit) {
  82             throw rangeError("too.many.errors", Integer.toString(limit));
  83         }
  84     }
  85 
  86     /**
  87      * Format an error message to include source and line information.
  88      * @param message Error message string.
  89      * @param source  Source file information.
  90      * @param line    Source line number.
  91      * @param column  Source column number.
  92      * @param token   Offending token descriptor.
  93      * @return formatted string
  94      */
  95     public static String format(final String message, final Source source, final int line, final int column, final long token) {
  96         final String        eoln     = System.lineSeparator();
  97         final int           position = Token.descPosition(token);
  98         final StringBuilder sb       = new StringBuilder();
  99 
 100         // Source description and message.
 101         sb.append(source.getName()).
 102             append(':').
 103             append(line).
 104             append(':').
 105             append(column).
 106             append(' ').
 107             append(message).
 108             append(eoln);
 109 
 110         // Source content.
 111         final String sourceLine = source.getSourceLine(position);
 112         sb.append(sourceLine).append(eoln);
 113 
 114         // Pointer to column.
 115         for (int i = 0; i < column; i++) {
 116             if (i < sourceLine.length() && sourceLine.charAt(i) == '\t') {
 117                 sb.append('\t');
 118             } else {
 119                 sb.append(' ');
 120             }
 121         }
 122 
 123         sb.append('^');
 124         // Use will append eoln.
 125         // buffer.append(eoln);
 126 
 127         return sb.toString();
 128     }
 129 
 130     /**
 131      * Report an error using information provided by the ParserException
 132      *
 133      * @param e ParserException object
 134      */
 135 
 136     public void error(final ParserException e) {
 137         error(e.getMessage());
 138     }
 139 
 140     /**
 141      * Report an error message provided
 142      *
 143      * @param message Error message string.
 144      */
 145     public void error(final String message) {
 146         writer.println(message);
 147         writer.flush();
 148         errors++;
 149         checkLimit();
 150     }
 151 
 152     /**
 153      * Report a warning using information provided by the ParserException
 154      *
 155      * @param e ParserException object
 156      */
 157     public void warning(final ParserException e) {
 158         warning(e.getMessage());
 159     }
 160 
 161     /**
 162      * Report a warning message provided
 163      *
 164      * @param message Error message string.
 165      */
 166     public void warning(final String message) {
 167         writer.println(message);
 168         writer.flush();
 169         warnings++;
 170         checkLimit();
 171     }
 172 
 173     /**
 174      * Test to see if errors have occurred.
 175      * @return True if errors.
 176      */
 177     public boolean hasErrors() {
 178         return errors != 0;
 179     }
 180 
 181     /**
 182      * Get the message limit
 183      * @return max number of messages
 184      */
 185     public int getLimit() {
 186         return limit;
 187     }
 188 
 189     /**
 190      * Set the message limit
 191      * @param limit max number of messages
 192      */
 193     public void setLimit(final int limit) {
 194         this.limit = limit;
 195     }
 196 
 197     /**
 198      * Check whether warnings should be treated like errors
 199      * @return true if warnings should be treated like errors
 200      */
 201     public boolean isWarningsAsErrors() {
 202         return warningsAsErrors;
 203     }
 204 
 205     /**
 206      * Set warnings to be treated as errors
 207      * @param warningsAsErrors true if warnings should be treated as errors, false otherwise
 208      */
 209     public void setWarningsAsErrors(final boolean warningsAsErrors) {
 210         this.warningsAsErrors = warningsAsErrors;
 211     }
 212 
 213     /**
 214      * Get the number of errors
 215      * @return number of errors
 216      */
 217     public int getNumberOfErrors() {
 218         return errors;
 219     }
 220 
 221     /**
 222      * Get number of warnings
 223      * @return number of warnings
 224      */
 225     public int getNumberOfWarnings() {
 226         return warnings;
 227     }
 228 
 229     /**
 230      * Clear warnings and error count.
 231      */
 232     void reset() {
 233         warnings = 0;
 234         errors = 0;
 235     }
 236 }