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
26package jdk.nashorn.internal.runtime;
27
28import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError;
29
30import java.io.PrintWriter;
31import jdk.nashorn.internal.parser.Token;
32
33/**
34 * Handles JavaScript error reporting.
35 */
36public 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}
237