View Javadoc

1   /*
2    $Id: ProcessingUnit.java,v 1.8 2005/02/17 10:56:46 jstrachan Exp $
3   
4    Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
5   
6    Redistribution and use of this software and associated documentation
7    ("Software"), with or without modification, are permitted provided
8    that the following conditions are met:
9   
10   1. Redistributions of source code must retain copyright
11      statements and notices.  Redistributions must also contain a
12      copy of this document.
13  
14   2. Redistributions in binary form must reproduce the
15      above copyright notice, this list of conditions and the
16      following disclaimer in the documentation and/or other
17      materials provided with the distribution.
18  
19   3. The name "groovy" must not be used to endorse or promote
20      products derived from this Software without prior written
21      permission of The Codehaus.  For written permission,
22      please contact info@codehaus.org.
23  
24   4. Products derived from this Software may not be called "groovy"
25      nor may "groovy" appear in their names without prior written
26      permission of The Codehaus. "groovy" is a registered
27      trademark of The Codehaus.
28  
29   5. Due credit should be given to The Codehaus -
30      http://groovy.codehaus.org/
31  
32   THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
33   ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
34   NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
35   FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
36   THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
37   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
38   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
39   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
41   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
42   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
43   OF THE POSSIBILITY OF SUCH DAMAGE.
44  
45   */
46  
47  package org.codehaus.groovy.control;
48  
49  import org.codehaus.groovy.control.messages.ExceptionMessage;
50  import org.codehaus.groovy.control.messages.Message;
51  import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
52  import org.codehaus.groovy.control.messages.WarningMessage;
53  import org.codehaus.groovy.syntax.SyntaxException;
54  
55  import java.io.PrintWriter;
56  import java.util.Iterator;
57  import java.util.LinkedList;
58  import java.util.List;
59  
60  
61  /***
62   * A base class for data structures that can collect messages and errors
63   * during processing.
64   *
65   * @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
66   * @version $Id: ProcessingUnit.java,v 1.8 2005/02/17 10:56:46 jstrachan Exp $
67   */
68  
69  public abstract class ProcessingUnit {
70  
71      /***
72       * WarningMessages collected during processing
73       */
74      protected LinkedList warnings;
75      /***
76       * ErrorMessages collected during processing
77       */
78      protected LinkedList errors;
79      /***
80       * Set on the first fatal error
81       */
82      protected boolean fatal;
83  
84      /***
85       * The current phase
86       */
87      protected int phase;
88      /***
89       * Set true if phase is finished
90       */
91      protected boolean phaseComplete;
92  
93      /***
94       * Configuration and other settings that control processing
95       */
96      protected CompilerConfiguration configuration;
97      /***
98       * Warnings will be filtered on this level
99       */
100     protected int warningLevel;
101     /***
102      * A place to send warning output
103      */
104     protected PrintWriter output;
105     /***
106      * The number of non-fatal errors to allow before fail()
107      */
108     protected int tolerance;
109 
110     /***
111      * The ClassLoader to use during processing
112      */
113     protected ClassLoader classLoader;
114 
115 
116     /***
117      * Initialize the ProcessingUnit to the empty state.
118      */
119 
120     public ProcessingUnit(CompilerConfiguration configuration, ClassLoader classLoader) {
121         this.warnings = null;
122         this.errors = null;
123         this.fatal = false;
124 
125         this.phase = Phases.INITIALIZATION;
126         this.classLoader = (classLoader == null ? new CompilerClassLoader() : classLoader);
127 
128         configure((configuration == null ? new CompilerConfiguration() : configuration));
129     }
130 
131 
132     /***
133      * Reconfigures the ProcessingUnit.
134      */
135 
136     public void configure(CompilerConfiguration configuration) {
137         this.configuration = configuration;
138         this.warningLevel = configuration.getWarningLevel();
139         this.output = configuration.getOutput();
140         this.tolerance = configuration.getTolerance();
141     }
142 
143 
144     public CompilerConfiguration getConfiguration() {
145         return configuration;
146     }
147 
148     public void setConfiguration(CompilerConfiguration configuration) {
149         this.configuration = configuration;
150     }
151 
152     /***
153      * Returns the class loader in use by this ProcessingUnit.
154      */
155 
156     public ClassLoader getClassLoader() {
157         return classLoader;
158     }
159 
160 
161     /***
162      * Sets the class loader for use by this ProcessingUnit.
163      */
164 
165     public void setClassLoader(ClassLoader loader) {
166         this.classLoader = loader;
167     }
168 
169 
170     /***
171      * Returns the current phase.
172      */
173 
174     public int getPhase() {
175         return this.phase;
176     }
177 
178 
179     /***
180      * Returns the description for the current phase.
181      */
182 
183     public String getPhaseDescription() {
184         return Phases.getDescription(this.phase);
185     }
186 
187 
188     /***
189      * Returns the list of warnings, or null if there are none.
190      */
191 
192     public List getWarnings() {
193         return this.warnings;
194     }
195 
196 
197     /***
198      * Returns the list of errors, or null if there are none.
199      */
200 
201     public List getErrors() {
202         return this.errors;
203     }
204 
205 
206     /***
207      * Returns the number of warnings.
208      */
209 
210     public int getWarningCount() {
211         return ((this.warnings == null) ? 0 : this.warnings.size());
212     }
213 
214 
215     /***
216      * Returns the number of errors.
217      */
218 
219     public int getErrorCount() {
220         return ((this.errors == null) ? 0 : this.errors.size());
221     }
222 
223 
224     /***
225      * Returns the specified warning message, or null.
226      */
227 
228     public WarningMessage getWarning(int index) {
229         if (index < getWarningCount()) {
230             return (WarningMessage) this.warnings.get(index);
231         }
232 
233         return null;
234     }
235 
236 
237     /***
238      * Returns the specified error message, or null.
239      */
240 
241     public Message getError(int index) {
242         if (index < getErrorCount()) {
243             return (Message) this.errors.get(index);
244         }
245 
246         return null;
247     }
248 
249 
250     /***
251      * Convenience routine to return the specified error's
252      * underlying SyntaxException, or null if it isn't one.
253      */
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 
263         return exception;
264     }
265 
266 
267     /***
268      * Convenience routine to return the specified error's
269      * underlying Exception, or null if it isn't one.
270      */
271 
272     public Exception getException(int index) {
273         Exception exception = null;
274 
275         Message message = getError(index);
276         if (message != null) {
277             if (message instanceof ExceptionMessage) {
278                 exception = ((ExceptionMessage) message).getCause();
279             }
280             else if (message instanceof SyntaxErrorMessage) {
281                 exception = ((SyntaxErrorMessage) message).getCause();
282             }
283         }
284 
285         return exception;
286     }
287 
288 
289 
290 
291     //---------------------------------------------------------------------------
292     // MESSAGES
293 
294 
295     /***
296      * Adds a WarningMessage to the message set.
297      */
298 
299     public void addWarning(WarningMessage message) {
300         if (message.isRelevant(this.warningLevel)) {
301             if (this.warnings == null) {
302                 this.warnings = new LinkedList();
303             }
304 
305             this.warnings.add(message);
306         }
307     }
308 
309 
310     /***
311      * Adds a non-fatal error to the message set.
312      */
313 
314     public void addError(Message message) throws CompilationFailedException {
315         if (this.errors == null) {
316             this.errors = new LinkedList();
317         }
318 
319         this.errors.add(message);
320 
321         if (this.errors.size() >= this.tolerance) {
322             fail();
323         }
324     }
325 
326 
327     /***
328      * Adds an optionally-fatal error to the message set.  Throws
329      * the unit as a PhaseFailedException, if the error is fatal.
330      */
331 
332     public void addError(Message message, boolean fatal) throws CompilationFailedException {
333         if (fatal) {
334             addFatalError(message);
335         }
336         else {
337             addError(message);
338         }
339     }
340 
341 
342     /***
343      * Adds a fatal exception to the message set and throws
344      * the unit as a PhaseFailedException.
345      */
346 
347     public void addFatalError(Message message) throws CompilationFailedException {
348         addError(message);
349         fail();
350     }
351 
352 
353     public void addException(Exception cause) throws CompilationFailedException {
354         addError(new ExceptionMessage(cause));
355         fail();
356     }
357 
358 
359     //---------------------------------------------------------------------------
360     // PROCESSING
361 
362 
363     /***
364      * Returns true if there are any errors pending.
365      */
366 
367     public boolean hasErrors() {
368         return this.errors != null;
369     }
370 
371 
372     /***
373      * Marks the current phase complete and processes any
374      * errors.
375      */
376 
377     public void completePhase() throws CompilationFailedException {
378         //
379         // First up, display and clear any pending warnings.
380 
381         if (this.warnings != null) {
382             Janitor janitor = new Janitor();
383 
384             try {
385                 Iterator iterator = this.warnings.iterator();
386                 while (iterator.hasNext()) {
387                     WarningMessage warning = (WarningMessage) iterator.next();
388                     warning.write(output, this, janitor);
389                 }
390 
391                 this.warnings = null;
392             }
393             finally {
394                 janitor.cleanup();
395             }
396         }
397 
398         //
399         // Then either fail() or update the phase and return
400 
401         if (this.hasErrors()) {
402             fail();
403         }
404         else {
405             phaseComplete = true;
406         }
407     }
408 
409 
410     /***
411      * A synonym for <code>gotoPhase( phase + 1 )</code>.
412      */
413 
414     public void nextPhase() throws CompilationFailedException {
415         gotoPhase(this.phase + 1);
416     }
417 
418 
419     /***
420      * Wraps up any pending operations for the current phase
421      * and switches to the next phase.
422      */
423 
424     public void gotoPhase(int phase) throws CompilationFailedException {
425         if (!this.phaseComplete) {
426             completePhase();
427         }
428 
429         this.phase = phase;
430         this.phaseComplete = false;
431     }
432 
433 
434     /***
435      * Causes the current phase to fail by throwing a
436      * CompilationFailedException.
437      */
438 
439     protected void fail() throws CompilationFailedException {
440         // lets find the first error exception
441         Throwable firstException = null;
442         for (Iterator iter = errors.iterator(); iter.hasNext();) {
443             Message message = (Message) iter.next();
444             if (message instanceof ExceptionMessage) {
445                 ExceptionMessage exceptionMessage = (ExceptionMessage) message;
446                 firstException = exceptionMessage.getCause();
447                 if (firstException != null) {
448                     break;
449                 }
450             }
451             if (message instanceof SyntaxErrorMessage) {
452                 SyntaxErrorMessage exceptionMessage = (SyntaxErrorMessage) message;
453                 firstException = exceptionMessage.getCause();
454                 if (firstException != null) {
455                     break;
456                 }
457             }
458         }
459 
460         if (firstException != null) {
461             throw new CompilationFailedException(phase, this, firstException);
462         }
463         else {
464             throw new CompilationFailedException(phase, this);
465         }
466     }
467 
468 
469 
470 
471     //---------------------------------------------------------------------------
472     // OUTPUT
473 
474 
475     /***
476      * Writes error messages to the specified PrintWriter.
477      */
478 
479     public void write(PrintWriter writer, Janitor janitor) {
480         if (this.warnings != null) {
481             Iterator iterator = this.warnings.iterator();
482             while (iterator.hasNext()) {
483                 WarningMessage warning = (WarningMessage) iterator.next();
484                 warning.write(writer, this, janitor);
485             }
486 
487             this.warnings = null;
488         }
489 
490         if (this.errors != null) {
491             Iterator iterator = this.errors.iterator();
492             while (iterator.hasNext()) {
493                 Message message = (Message) iterator.next();
494                 message.write(writer, this, janitor);
495             }
496 
497             //why? this nukes the errors once a getString call is made
498             //this.errors = null;
499         }
500     }
501 
502 
503 }
504 
505 
506 
507