View Javadoc

1   /*
2    $Id: ProcessingUnit.java,v 1.3 2004/12/15 00:19:52 zohar 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 java.io.PrintWriter;
50  import java.util.Iterator;
51  import java.util.LinkedList;
52  import java.util.List;
53  
54  import org.codehaus.groovy.control.messages.ExceptionMessage;
55  import org.codehaus.groovy.control.messages.Message;
56  import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
57  import org.codehaus.groovy.control.messages.WarningMessage;
58  import org.codehaus.groovy.syntax.SyntaxException;
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   *
67   *  @version $Id: ProcessingUnit.java,v 1.3 2004/12/15 00:19:52 zohar Exp $
68   */
69  
70  public abstract class ProcessingUnit
71  {
72  
73    //---------------------------------------------------------------------------
74    // CONSTRUCTION AND SUCH
75  
76      protected LinkedList    warnings;       // WarningMessages collected during processing
77      protected LinkedList    errors;         // ErrorMessages collected during processing
78      protected boolean       fatal;          // Set on the first fatal error
79  
80      protected int           phase;          // The current phase
81      protected boolean       phaseComplete;  // Set true if phase is finished
82  
83      protected CompilerConfiguration configuration;  // Configuration and other settings that control processing
84      protected int           warningLevel;   // Warnings will be filtered on this level
85      protected PrintWriter   output;         // A place to send warning output
86      protected int           tolerance;      // The number of non-fatal errors to allow before fail()
87  
88      protected ClassLoader   classLoader;    // The ClassLoader to use during processing
89  
90  
91  
92     /***
93      *  Initialize the ProcessingUnit to the empty state.
94      */
95  
96      public ProcessingUnit( CompilerConfiguration configuration, ClassLoader classLoader )
97      {
98          this.warnings      = null;
99          this.errors        = null;
100         this.fatal         = false;
101 
102         this.phase         = Phases.INITIALIZATION;
103         this.classLoader   = (classLoader == null ? new CompilerClassLoader() : classLoader);
104 
105         configure( (configuration == null ? new CompilerConfiguration() : configuration) );
106     }
107 
108 
109 
110    /***
111     *  Reconfigures the ProcessingUnit.
112     */
113 
114     public void configure( CompilerConfiguration configuration )
115     {
116         this.configuration = configuration;
117         this.warningLevel  = configuration.getWarningLevel();
118         this.output        = configuration.getOutput();
119         this.tolerance     = configuration.getTolerance();
120     }
121 
122 
123 
124    /***
125     *  Returns the class loader in use by this ProcessingUnit.
126     */
127 
128     public ClassLoader getClassLoader()
129     {
130         return classLoader;
131     }
132 
133 
134 
135    /***
136     *  Sets the class loader for use by this ProcessingUnit.
137     */
138 
139     public void setClassLoader( ClassLoader loader )
140     {
141         this.classLoader = loader;
142     }
143 
144 
145 
146    /***
147     *  Returns the current phase.
148     */
149 
150     public int getPhase()
151     {
152         return this.phase;
153     }
154 
155 
156 
157    /***
158     *  Returns the description for the current phase.
159     */
160 
161     public String getPhaseDescription()
162     {
163         return Phases.getDescription( this.phase );
164     }
165 
166 
167 
168    /***
169     *  Returns the list of warnings, or null if there are none.
170     */
171 
172     public List getWarnings()
173     {
174         return this.warnings;
175     }
176 
177 
178 
179    /***
180     *  Returns the list of errors, or null if there are none.
181     */
182 
183     public List getErrors()
184     {
185         return this.errors;
186     }
187 
188 
189 
190    /***
191     *  Returns the number of warnings.
192     */
193 
194     public int getWarningCount()
195     {
196         return ((this.warnings == null) ? 0 : this.warnings.size());
197     }
198 
199 
200 
201    /***
202     *  Returns the number of errors.
203     */
204 
205     public int getErrorCount()
206     {
207         return ((this.errors == null) ? 0 : this.errors.size());
208     }
209 
210 
211 
212    /***
213     *  Returns the specified warning message, or null.
214     */
215 
216     public WarningMessage getWarning( int index )
217     {
218         if( index < getWarningCount() )
219         {
220             return (WarningMessage)this.warnings.get(index);
221         }
222 
223         return null;
224     }
225 
226 
227 
228    /***
229     *  Returns the specified error message, or null.
230     */
231 
232     public Message getError( int index )
233     {
234         if( index < getErrorCount() )
235         {
236             return (Message)this.errors.get(index);
237         }
238 
239         return null;
240     }
241 
242 
243 
244    /***
245     *  Convenience routine to return the specified error's
246     *  underlying SyntaxException, or null if it isn't one.
247     */
248 
249     public SyntaxException getSyntaxError( int index )
250     {
251         SyntaxException exception = null;
252 
253         Message message = getError( index );
254         if( message != null && message instanceof SyntaxErrorMessage )
255         {
256             exception = ((SyntaxErrorMessage)message).getCause();
257         }
258 
259         return exception;
260     }
261 
262 
263 
264    /***
265     *  Convenience routine to return the specified error's
266     *  underlying Exception, or null if it isn't one.
267     */
268 
269     public Exception getException( int index )
270     {
271         Exception exception = null;
272 
273         Message message = getError( index );
274         if( message != null )
275         {
276             if( message instanceof ExceptionMessage )
277             {
278                 exception = ((ExceptionMessage)message).getCause();
279             }
280             else if( message instanceof SyntaxErrorMessage )
281             {
282                 exception = ((SyntaxErrorMessage)message).getCause();
283             }
284         }
285 
286         return exception;
287     }
288 
289 
290 
291 
292   //---------------------------------------------------------------------------
293   // MESSAGES
294 
295 
296    /***
297     *  Adds a WarningMessage to the message set.
298     */
299 
300     public void addWarning( WarningMessage message )
301     {
302         if( message.isRelevant(this.warningLevel) )
303         {
304             if( this.warnings == null )
305             {
306                 this.warnings = new LinkedList();
307             }
308 
309             this.warnings.add( message );
310         }
311     }
312 
313 
314 
315    /***
316     *  Adds a non-fatal error to the message set.
317     */
318 
319     public void addError( Message message ) throws CompilationFailedException
320     {
321         if( this.errors == null )
322         {
323             this.errors = new LinkedList();
324         }
325 
326         this.errors.add( message );
327 
328         if( this.errors.size() >= this.tolerance )
329         {
330             fail();
331         }
332     }
333 
334 
335 
336    /***
337     *  Adds an optionally-fatal error to the message set.  Throws
338     *  the unit as a PhaseFailedException, if the error is fatal.
339     */
340 
341     public void addError( Message message, boolean fatal ) throws CompilationFailedException
342     {
343         if( fatal )
344         {
345             addFatalError( message );
346         }
347         else
348         {
349             addError( message );
350         }
351     }
352 
353 
354 
355    /***
356     *  Adds a fatal exception to the message set and throws
357     *  the unit as a PhaseFailedException.
358     */
359 
360     public void addFatalError( Message message ) throws CompilationFailedException
361     {
362         addError( message );
363         fail();
364     }
365 
366 
367 
368 
369   //---------------------------------------------------------------------------
370   // PROCESSING
371 
372 
373    /***
374     *  Returns true if there are any errors pending.
375     */
376 
377     public boolean hasErrors()
378     {
379         return this.errors != null;
380     }
381 
382 
383 
384    /***
385     *  Marks the current phase complete and processes any
386     *  errors.
387     */
388 
389     public void completePhase() throws CompilationFailedException
390     {
391         //
392         // First up, display and clear any pending warnings.
393 
394         if( this.warnings != null )
395         {
396             Janitor janitor = new Janitor();
397 
398             try
399             {
400                 Iterator iterator = this.warnings.iterator();
401                 while( iterator.hasNext() )
402                 {
403                     WarningMessage warning = (WarningMessage)iterator.next();
404                     warning.write( output, this, janitor );
405                 }
406 
407                 this.warnings = null;
408             }
409             finally
410             {
411                 janitor.cleanup();
412             }
413         }
414 
415         //
416         // Then either fail() or update the phase and return
417 
418         if( this.hasErrors() )
419         {
420             fail();
421         }
422         else
423         {
424             phaseComplete = true;
425         }
426     }
427 
428 
429 
430    /***
431     *  A synonym for <code>gotoPhase( phase + 1 )</code>.
432     */
433 
434     public void nextPhase() throws CompilationFailedException
435     {
436         gotoPhase( this.phase + 1 );
437     }
438 
439 
440 
441    /***
442     *  Wraps up any pending operations for the current phase
443     *  and switches to the next phase.
444     */
445 
446     public void gotoPhase( int phase ) throws CompilationFailedException
447     {
448         if( !this.phaseComplete )
449         {
450             completePhase();
451         }
452 
453         this.phase = phase;
454         this.phaseComplete = false;
455     }
456 
457 
458 
459    /***
460     *  Causes the current phase to fail by throwing a
461     *  CompilationFailedException.
462     */
463 
464     protected void fail() throws CompilationFailedException
465     {
466         throw new CompilationFailedException( phase, this );
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     {
481         if( this.warnings != null )
482         {
483             Iterator iterator = this.warnings.iterator();
484             while( iterator.hasNext() )
485             {
486                 WarningMessage warning = (WarningMessage)iterator.next();
487                 warning.write( writer, this, janitor );
488             }
489 
490             this.warnings = null;
491         }
492 
493         if( this.errors != null )
494         {
495             Iterator iterator = this.errors.iterator();
496             while( iterator.hasNext() )
497             {
498                 Message message = (Message)iterator.next();
499                 message.write( writer, this, janitor );
500             }
501 
502             //why? this nukes the errors once a getString call is made
503             //this.errors = null;
504         }
505     }
506 
507 
508 }
509 
510 
511 
512