001    /*
002    $Id: ErrorCollector.java,v 1.5 2005/06/13 09:31:01 blackdrag Exp $
003    
004    Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
005    
006    Redistribution and use of this software and associated documentation
007    ("Software"), with or without modification, are permitted provided
008    that the following conditions are met:
009    
010    1. Redistributions of source code must retain copyright
011       statements and notices.  Redistributions must also contain a
012       copy of this document.
013    
014    2. Redistributions in binary form must reproduce the
015       above copyright notice, this list of conditions and the
016       following disclaimer in the documentation and/or other
017       materials provided with the distribution.
018    
019    3. The name "groovy" must not be used to endorse or promote
020       products derived from this Software without prior written
021       permission of The Codehaus.  For written permission,
022       please contact info@codehaus.org.
023    
024    4. Products derived from this Software may not be called "groovy"
025       nor may "groovy" appear in their names without prior written
026       permission of The Codehaus. "groovy" is a registered
027       trademark of The Codehaus.
028    
029    5. Due credit should be given to The Codehaus -
030       http://groovy.codehaus.org/
031    
032    THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
033    ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
034    NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
035    FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
036    THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
037    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
038    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
039    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
040    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
041    STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
042    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
043    OF THE POSSIBILITY OF SUCH DAMAGE.
044    
045    */
046    
047    package org.codehaus.groovy.control;
048    
049    import java.io.PrintWriter;
050    import java.util.Iterator;
051    import java.util.LinkedList;
052    import java.util.List;
053    
054    import org.codehaus.groovy.control.messages.ExceptionMessage;
055    import org.codehaus.groovy.control.messages.LocatedMessage;
056    import org.codehaus.groovy.control.messages.Message;
057    import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
058    import org.codehaus.groovy.control.messages.WarningMessage;
059    import org.codehaus.groovy.syntax.CSTNode;
060    import org.codehaus.groovy.syntax.SyntaxException;
061    
062    /**
063     * A base class for collecting messages and errors during processing.
064     * Each CompilationUnit should have one and SourceUnits should share
065     * their ErrorCollector with the CompilationUnit.
066     *
067     * @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
068     * @author <a href="mailto:blackdrag@gmx.org">Jochen Theodorou</a>
069     * @version $Id: ErrorCollector.java,v 1.5 2005/06/13 09:31:01 blackdrag Exp $
070     */
071    public class ErrorCollector {
072        
073        /**
074         * WarningMessages collected during processing
075         */
076        protected LinkedList warnings;
077        /**
078         * ErrorMessages collected during processing
079         */
080        protected LinkedList errors;
081        /**
082         * Configuration and other settings that control processing
083         */
084        protected CompilerConfiguration configuration;
085    
086        /**
087         * Initialize the ErrorReporter.
088         */
089        public ErrorCollector(CompilerConfiguration configuration) {
090            this.warnings = null;
091            this.errors = null;
092            
093            this.configuration = configuration;
094        }
095        
096        public void addCollectorContents(ErrorCollector er) {
097            if (er.errors!=null) {
098                if (errors==null) {
099                    errors = er.errors;
100                } else {
101                    errors.addAll(errors);
102                }
103            }
104            if (er.warnings!=null) {
105                if (warnings==null) {
106                    warnings = er.warnings;
107                } else {
108                    warnings.addAll(warnings);
109                }            
110            }
111        }
112        
113        
114        
115        /**
116         * Adds an error to the message set, but don't fail.
117         */
118        public void addErrorAndContinue(Message message) {
119            if (this.errors == null) {
120                this.errors = new LinkedList();
121            }
122    
123            this.errors.add(message);
124        }
125        
126        /**
127         * Adds a non-fatal error to the message set.
128         */
129        public void addError(Message message) throws CompilationFailedException {
130            addErrorAndContinue(message);
131    
132            if (errors!=null && this.errors.size() >= configuration.getTolerance()) {
133                failIfErrors();
134            }
135        }
136        
137        /**
138         * Adds an optionally-fatal error to the message set.  Throws
139         * the unit as a PhaseFailedException, if the error is fatal.
140         */
141        public void addError(Message message, boolean fatal) throws CompilationFailedException {
142            if (fatal) {
143                addFatalError(message);
144            }
145            else {
146                addError(message);
147            }
148        }
149    
150        
151        /**
152         * Convenience wrapper for addError().
153         */
154        public void addError(SyntaxException error, SourceUnit source) throws CompilationFailedException {
155            addError(Message.create(error, source), error.isFatal());
156        }
157    
158    
159        /**
160         * Convenience wrapper for addError().
161         */
162        public void addError(String text, CSTNode context, SourceUnit source) throws CompilationFailedException {
163            addError(new LocatedMessage(text, context, source));
164        }
165        
166        
167        /**
168         * Adds a fatal exception to the message set and throws
169         * the unit as a PhaseFailedException.
170         */
171        public void addFatalError(Message message) throws CompilationFailedException {
172            addError(message);
173            failIfErrors();
174        }
175    
176    
177        public void addException(Exception cause, SourceUnit source) throws CompilationFailedException {
178            addError(new ExceptionMessage(cause,configuration.getDebug(),source));
179            failIfErrors();
180        }
181    
182        /**
183         * Returns true if there are any errors pending.
184         */
185        public boolean hasErrors() {
186            return this.errors != null;
187        }
188        
189        /**
190         * Returns true if there are any warnings pending.
191         */
192        public boolean hasWarnings() {
193            return this.warnings != null;
194        }
195        
196        /**
197         * Returns the list of warnings, or null if there are none.
198         */
199        public List getWarnings() {
200            return this.warnings;
201        }
202    
203        /**
204         * Returns the list of errors, or null if there are none.
205         */
206        public List getErrors() {
207            return this.errors;
208        }
209    
210        /**
211         * Returns the number of warnings.
212         */
213        public int getWarningCount() {
214            return ((this.warnings == null) ? 0 : this.warnings.size());
215        }
216    
217        /**
218         * Returns the number of errors.
219         */
220        public int getErrorCount() {
221            return ((this.errors == null) ? 0 : this.errors.size());
222        }
223    
224        /**
225         * Returns the specified warning message, or null.
226         */
227        public WarningMessage getWarning(int index) {
228            if (index < getWarningCount()) {
229                return (WarningMessage) this.warnings.get(index);
230            }
231            return null;
232        }
233    
234        /**
235         * Returns the specified error message, or null.
236         */
237        public Message getError(int index) {
238            if (index < getErrorCount()) {
239                return (Message) this.errors.get(index);
240            }
241            return null;
242        }
243    
244        /**
245         * Returns the last error reported
246         */
247        public Message getLastError() {
248            return (Message) this.errors.getLast();
249        }
250        
251        /**
252         * Convenience routine to return the specified error's
253         * underlying SyntaxException, or null if it isn't one.
254         */
255        public SyntaxException getSyntaxError(int index) {
256            SyntaxException exception = null;
257    
258            Message message = getError(index);
259            if (message != null && message instanceof SyntaxErrorMessage) {
260                exception = ((SyntaxErrorMessage) message).getCause();
261            }
262            return exception;
263        }
264    
265        /**
266         * Convenience routine to return the specified error's
267         * underlying Exception, or null if it isn't one.
268         */
269        public Exception getException(int index) {
270            Exception exception = null;
271    
272            Message message = getError(index);
273            if (message != null) {
274                if (message instanceof ExceptionMessage) {
275                    exception = ((ExceptionMessage) message).getCause();
276                }
277                else if (message instanceof SyntaxErrorMessage) {
278                    exception = ((SyntaxErrorMessage) message).getCause();
279                }
280            }
281            return exception;
282        }
283    
284        /**
285         * Adds a WarningMessage to the message set.
286         */
287        public void addWarning(WarningMessage message) {
288            if (message.isRelevant(configuration.getWarningLevel())) {
289                if (this.warnings == null) {
290                    this.warnings = new LinkedList();
291                }
292    
293                this.warnings.add(message);
294            }
295        }
296    
297    
298        /**
299         * Convenience wrapper for addWarning() that won't create an object
300         * unless it is relevant.
301         */
302        public void addWarning(int importance, String text, CSTNode context, SourceUnit source) {
303            if (WarningMessage.isRelevant(importance, configuration.getWarningLevel())) {
304                addWarning(new WarningMessage(importance, text, context, source));
305            }
306        }
307        
308        
309        /**
310         * Convenience wrapper for addWarning() that won't create an object
311         * unless it is relevant.
312         */
313        public void addWarning(int importance, String text, Object data, CSTNode context, SourceUnit source) {
314            if (WarningMessage.isRelevant(importance, configuration.getWarningLevel())) {
315                addWarning(new WarningMessage(importance, text, data, context, source));
316            }
317        }
318       
319    
320        /**
321         * Causes the current phase to fail by throwing a
322         * CompilationFailedException.
323         */
324        protected void failIfErrors() throws CompilationFailedException {
325            if (hasErrors()) throw new MultipleCompilationErrorsException(this);
326        }
327        
328        //---------------------------------------------------------------------------
329        // OUTPUT
330    
331    
332        /**
333         * Writes error messages to the specified PrintWriter.
334         */
335        public void write(PrintWriter writer, Janitor janitor) {
336            if (this.warnings != null) {
337                Iterator iterator = this.warnings.iterator();
338                while (iterator.hasNext()) {
339                    WarningMessage warning = (WarningMessage) iterator.next();
340                    warning.write(writer, janitor);
341                }
342    
343                writer.println();
344                writer.print(warnings.size());
345                writer.print(" Warning");
346                if (warnings.size()>1) writer.print("s");
347                writer.println();
348                
349                this.warnings = null;
350            }
351    
352            if (this.errors != null) {
353                Iterator iterator = this.errors.iterator();
354                while (iterator.hasNext()) {
355                    Message message = (Message) iterator.next();
356                    message.write(writer, janitor);
357                    
358                    if (configuration.getDebug() && (message instanceof SyntaxErrorMessage)) {
359                        SyntaxErrorMessage sem = (SyntaxErrorMessage) message;
360                        SyntaxException se = sem.getCause();
361                        se.printStackTrace(writer);
362                    }
363                }
364    
365                writer.println();
366                writer.print(errors.size());
367                writer.print(" Error");
368                if (errors.size()>1) writer.print("s");
369                writer.println();
370            }
371        }
372    
373    }