|
|||||||||||||||||||
30 day Evaluation Version distributed via the Maven Jar Repository. Clover is not free. You have 30 days to evaluate it. Please visit http://www.thecortex.net/clover to obtain a licensed version of Clover | |||||||||||||||||||
Source file | Conditionals | Statements | Methods | TOTAL | |||||||||||||||
SourceUnit.java | 0% | 0% | 0% | 0% |
|
1 |
/*
|
|
2 |
$Id: SourceUnit.java,v 1.2 2004/07/10 03:31:41 bran 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.File;
|
|
50 |
import java.io.IOException;
|
|
51 |
import java.io.Reader;
|
|
52 |
import java.lang.reflect.Constructor;
|
|
53 |
import java.lang.reflect.InvocationTargetException;
|
|
54 |
import java.net.URL;
|
|
55 |
import java.util.List;
|
|
56 |
|
|
57 |
import org.codehaus.groovy.GroovyBugError;
|
|
58 |
import org.codehaus.groovy.ast.ClassNode;
|
|
59 |
import org.codehaus.groovy.ast.FieldNode;
|
|
60 |
import org.codehaus.groovy.ast.MethodNode;
|
|
61 |
import org.codehaus.groovy.ast.ModuleNode;
|
|
62 |
import org.codehaus.groovy.ast.stmt.BlockStatement;
|
|
63 |
import org.codehaus.groovy.ast.stmt.Statement;
|
|
64 |
import org.codehaus.groovy.control.io.FileReaderSource;
|
|
65 |
import org.codehaus.groovy.control.io.ReaderSource;
|
|
66 |
import org.codehaus.groovy.control.io.StringReaderSource;
|
|
67 |
import org.codehaus.groovy.control.io.URLReaderSource;
|
|
68 |
import org.codehaus.groovy.control.messages.LocatedMessage;
|
|
69 |
import org.codehaus.groovy.control.messages.Message;
|
|
70 |
import org.codehaus.groovy.control.messages.SimpleMessage;
|
|
71 |
import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
|
|
72 |
import org.codehaus.groovy.control.messages.WarningMessage;
|
|
73 |
import org.codehaus.groovy.syntax.CSTNode;
|
|
74 |
import org.codehaus.groovy.syntax.Reduction;
|
|
75 |
import org.codehaus.groovy.syntax.SyntaxException;
|
|
76 |
import org.codehaus.groovy.syntax.Token;
|
|
77 |
import org.codehaus.groovy.syntax.TokenStream;
|
|
78 |
import org.codehaus.groovy.syntax.Types;
|
|
79 |
import org.codehaus.groovy.syntax.lexer.GroovyLexer;
|
|
80 |
import org.codehaus.groovy.syntax.lexer.LexerTokenStream;
|
|
81 |
import org.codehaus.groovy.syntax.lexer.ReaderCharStream;
|
|
82 |
import org.codehaus.groovy.syntax.parser.ASTBuilder;
|
|
83 |
import org.codehaus.groovy.syntax.parser.Parser;
|
|
84 |
import org.codehaus.groovy.syntax.parser.UnexpectedTokenException;
|
|
85 |
import org.codehaus.groovy.tools.Utilities;
|
|
86 |
|
|
87 |
|
|
88 |
|
|
89 |
/**
|
|
90 |
* Provides an anchor for a single source unit (usually a script file)
|
|
91 |
* as it passes through the compiler system.
|
|
92 |
*
|
|
93 |
* @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
|
|
94 |
* @author <a href="mailto:b55r@sina.com">Bing Ran</a>
|
|
95 |
*
|
|
96 |
* @version $Id: SourceUnit.java,v 1.2 2004/07/10 03:31:41 bran Exp $
|
|
97 |
*/
|
|
98 |
|
|
99 |
public class SourceUnit extends ProcessingUnit |
|
100 |
{ |
|
101 |
|
|
102 |
//---------------------------------------------------------------------------
|
|
103 |
// CONSTRUCTION AND SUCH
|
|
104 |
|
|
105 |
protected ReaderSource source; // Where we can get Readers for our source unit |
|
106 |
protected String name; // A descriptive name of the source unit |
|
107 |
protected Reduction cst; // A Concrete Syntax Tree of the source |
|
108 |
protected ModuleNode ast; // The root of the Abstract Syntax Tree for the source |
|
109 |
|
|
110 |
|
|
111 |
|
|
112 |
/**
|
|
113 |
* Initializes the SourceUnit from existing machinery.
|
|
114 |
*/
|
|
115 |
|
|
116 | 0 |
public SourceUnit( String name, ReaderSource source, CompilerConfiguration flags, ClassLoader loader )
|
117 |
{ |
|
118 | 0 |
super( flags, loader );
|
119 |
|
|
120 | 0 |
this.name = name;
|
121 | 0 |
this.source = source;
|
122 |
} |
|
123 |
|
|
124 |
|
|
125 |
|
|
126 |
/**
|
|
127 |
* Initializes the SourceUnit from the specified file.
|
|
128 |
*/
|
|
129 |
|
|
130 | 0 |
public SourceUnit( File source, CompilerConfiguration configuration, ClassLoader loader )
|
131 |
{ |
|
132 | 0 |
this( source.getPath(), new FileReaderSource(source, configuration), configuration, loader ); |
133 |
} |
|
134 |
|
|
135 |
|
|
136 |
/**
|
|
137 |
* Initializes the SourceUnit from the specified URL.
|
|
138 |
*/
|
|
139 |
|
|
140 | 0 |
public SourceUnit( URL source, CompilerConfiguration configuration, ClassLoader loader )
|
141 |
{ |
|
142 | 0 |
this( source.getPath(), new URLReaderSource(source, configuration), configuration, loader ); |
143 |
} |
|
144 |
|
|
145 |
|
|
146 |
|
|
147 |
/**
|
|
148 |
* Initializes the SourceUnit for a string of source.
|
|
149 |
*/
|
|
150 |
|
|
151 | 0 |
public SourceUnit( String name, String source, CompilerConfiguration configuration, ClassLoader loader )
|
152 |
{ |
|
153 | 0 |
this( name, new StringReaderSource(source, configuration), configuration, loader ); |
154 |
} |
|
155 |
|
|
156 |
|
|
157 |
/**
|
|
158 |
* Returns the name for the SourceUnit.
|
|
159 |
*/
|
|
160 |
|
|
161 | 0 |
public String getName()
|
162 |
{ |
|
163 | 0 |
return name;
|
164 |
} |
|
165 |
|
|
166 |
|
|
167 |
|
|
168 |
/**
|
|
169 |
* Returns the Concrete Syntax Tree produced during parse()ing.
|
|
170 |
*/
|
|
171 |
|
|
172 | 0 |
public Reduction getCST()
|
173 |
{ |
|
174 | 0 |
return this.cst; |
175 |
} |
|
176 |
|
|
177 |
|
|
178 |
|
|
179 |
/**
|
|
180 |
* Returns the Abstract Syntax Tree produced during parse()ing
|
|
181 |
* and expanded during later phases.
|
|
182 |
*/
|
|
183 |
|
|
184 | 0 |
public ModuleNode getAST()
|
185 |
{ |
|
186 | 0 |
return this.ast; |
187 |
} |
|
188 |
|
|
189 |
|
|
190 |
|
|
191 |
/**
|
|
192 |
* Convenience routine, primarily for use by the InteractiveShell,
|
|
193 |
* that returns true if parse() failed with an unexpected EOF.
|
|
194 |
*/
|
|
195 |
|
|
196 | 0 |
public boolean failedWithUnexpectedEOF() |
197 |
{ |
|
198 | 0 |
boolean result = false; |
199 |
|
|
200 | 0 |
if( this.errors != null ) |
201 |
{ |
|
202 | 0 |
Message last = (Message)errors.get(errors.size() - 1); |
203 | 0 |
if( last instanceof SyntaxErrorMessage ) |
204 |
{ |
|
205 | 0 |
SyntaxException cause = ((SyntaxErrorMessage)last).getCause(); |
206 | 0 |
if( cause instanceof UnexpectedTokenException ) |
207 |
{ |
|
208 | 0 |
Token unexpected = ((UnexpectedTokenException)cause).getUnexpectedToken(); |
209 | 0 |
if( unexpected.isA(Types.EOF) )
|
210 |
{ |
|
211 | 0 |
result = true;
|
212 |
} |
|
213 |
} |
|
214 |
} |
|
215 |
} |
|
216 |
|
|
217 | 0 |
return result;
|
218 |
} |
|
219 |
|
|
220 |
|
|
221 |
|
|
222 |
//---------------------------------------------------------------------------
|
|
223 |
// FACTORIES
|
|
224 |
|
|
225 |
|
|
226 |
/**
|
|
227 |
* A convenience routine to create a standalone SourceUnit on a String
|
|
228 |
* with defaults for almost everything that is configurable.
|
|
229 |
*/
|
|
230 |
|
|
231 | 0 |
public static SourceUnit create( String name, String source ) |
232 |
{ |
|
233 | 0 |
CompilerConfiguration configuration = new CompilerConfiguration();
|
234 | 0 |
configuration.setTolerance( 1 ); |
235 |
|
|
236 | 0 |
return new SourceUnit( name, source, configuration, null ); |
237 |
} |
|
238 |
|
|
239 |
|
|
240 |
|
|
241 |
/**
|
|
242 |
* A convenience routine to create a standalone SourceUnit on a String
|
|
243 |
* with defaults for almost everything that is configurable.
|
|
244 |
*/
|
|
245 |
|
|
246 | 0 |
public static SourceUnit create( String name, String source, int tolerance ) |
247 |
{ |
|
248 | 0 |
CompilerConfiguration configuration = new CompilerConfiguration();
|
249 | 0 |
configuration.setTolerance( tolerance ); |
250 |
|
|
251 | 0 |
return new SourceUnit( name, source, configuration, null ); |
252 |
} |
|
253 |
|
|
254 |
|
|
255 |
|
|
256 |
|
|
257 |
|
|
258 |
//---------------------------------------------------------------------------
|
|
259 |
// PROCESSING
|
|
260 |
|
|
261 |
|
|
262 |
/**
|
|
263 |
* Parses the source to a CST. You can retrieve it with getCST().
|
|
264 |
*/
|
|
265 |
|
|
266 | 0 |
public void parse() throws CompilationFailedException |
267 |
{ |
|
268 | 0 |
if( this.phase > Phases.PARSING ) |
269 |
{ |
|
270 | 0 |
throw new GroovyBugError( "parsing is already complete" ); |
271 |
} |
|
272 |
|
|
273 | 0 |
if( this.phase == Phases.INITIALIZATION ) |
274 |
{ |
|
275 | 0 |
nextPhase(); |
276 |
} |
|
277 |
|
|
278 |
|
|
279 |
//
|
|
280 |
// Create a reader on the source and run the parser.
|
|
281 |
|
|
282 | 0 |
Reader reader = null;
|
283 | 0 |
try
|
284 |
{ |
|
285 | 0 |
reader = source.getReader(); |
286 |
|
|
287 |
//
|
|
288 |
// Create a lexer and token stream
|
|
289 |
|
|
290 | 0 |
GroovyLexer lexer = new GroovyLexer( new ReaderCharStream(reader) ); |
291 | 0 |
TokenStream stream = new LexerTokenStream( lexer );
|
292 |
|
|
293 |
//
|
|
294 |
// Do the parsing
|
|
295 |
|
|
296 | 0 |
Parser parser = new Parser( this, stream ); |
297 | 0 |
this.cst = parser.parse();
|
298 |
|
|
299 | 0 |
completePhase(); |
300 |
} |
|
301 |
catch( IOException e )
|
|
302 |
{ |
|
303 | 0 |
addFatalError( new SimpleMessage(e.getMessage()) );
|
304 |
} |
|
305 |
finally
|
|
306 |
{ |
|
307 | 0 |
if( reader != null ) |
308 |
{ |
|
309 | 0 |
try { reader.close(); } catch( IOException e ) {} |
310 |
} |
|
311 |
} |
|
312 |
} |
|
313 |
|
|
314 |
|
|
315 |
|
|
316 |
/**
|
|
317 |
* Generates an AST from the CST. You can retrieve it with getAST().
|
|
318 |
*/
|
|
319 |
|
|
320 | 0 |
public void convert() throws CompilationFailedException |
321 |
{ |
|
322 | 0 |
if( this.phase == Phases.PARSING && this.phaseComplete ) |
323 |
{ |
|
324 | 0 |
gotoPhase( Phases.CONVERSION ); |
325 |
} |
|
326 |
|
|
327 | 0 |
if( this.phase != Phases.CONVERSION ) |
328 |
{ |
|
329 | 0 |
throw new GroovyBugError( "SourceUnit not ready for convert()" ); |
330 |
} |
|
331 |
|
|
332 |
|
|
333 |
//
|
|
334 |
// Build the AST
|
|
335 |
|
|
336 | 0 |
try
|
337 |
{ |
|
338 | 0 |
ASTBuilder builder = new ASTBuilder( this, this.classLoader ); |
339 | 0 |
this.ast = builder.build( this.cst ); |
340 | 0 |
this.ast.setDescription( this.name ); |
341 |
} |
|
342 |
catch( SyntaxException e )
|
|
343 |
{ |
|
344 | 0 |
addError( new SyntaxErrorMessage(e) );
|
345 |
} |
|
346 |
|
|
347 | 0 |
completePhase(); |
348 |
} |
|
349 |
|
|
350 |
|
|
351 |
|
|
352 |
|
|
353 |
//---------------------------------------------------------------------------
|
|
354 |
// ERROR REPORTING
|
|
355 |
|
|
356 |
|
|
357 |
/**
|
|
358 |
* Convenience wrapper for addWarning() that won't create an object
|
|
359 |
* unless it is relevant.
|
|
360 |
*/
|
|
361 |
|
|
362 | 0 |
public void addWarning( int importance, String text, CSTNode context ) |
363 |
{ |
|
364 | 0 |
if( WarningMessage.isRelevant(importance, this.warningLevel) ) |
365 |
{ |
|
366 | 0 |
addWarning( new WarningMessage(importance, text, context) );
|
367 |
} |
|
368 |
} |
|
369 |
|
|
370 |
|
|
371 |
|
|
372 |
/**
|
|
373 |
* Convenience wrapper for addWarning() that won't create an object
|
|
374 |
* unless it is relevant.
|
|
375 |
*/
|
|
376 |
|
|
377 | 0 |
public void addWarning( int importance, String text, Object data, CSTNode context ) |
378 |
{ |
|
379 | 0 |
if( WarningMessage.isRelevant(importance, this.warningLevel) ) |
380 |
{ |
|
381 | 0 |
addWarning( new WarningMessage(importance, text, data, context) );
|
382 |
} |
|
383 |
} |
|
384 |
|
|
385 |
|
|
386 |
|
|
387 |
/**
|
|
388 |
* Convenience wrapper for addError().
|
|
389 |
*/
|
|
390 |
|
|
391 | 0 |
public void addError( SyntaxException error ) throws CompilationFailedException |
392 |
{ |
|
393 | 0 |
addError( Message.create(error), error.isFatal() ); |
394 |
} |
|
395 |
|
|
396 |
|
|
397 |
|
|
398 |
/**
|
|
399 |
* Convenience wrapper for addError().
|
|
400 |
*/
|
|
401 |
|
|
402 | 0 |
public void addError( String text, CSTNode context ) throws CompilationFailedException |
403 |
{ |
|
404 | 0 |
addError( new LocatedMessage(text, context) );
|
405 |
} |
|
406 |
|
|
407 |
|
|
408 |
|
|
409 |
|
|
410 |
//---------------------------------------------------------------------------
|
|
411 |
// SOURCE SAMPLING
|
|
412 |
|
|
413 |
|
|
414 |
/**
|
|
415 |
* Returns a sampling of the source at the specified line and column,
|
|
416 |
* of null if it is unavailable.
|
|
417 |
*/
|
|
418 |
|
|
419 | 0 |
public String getSample( int line, int column, Janitor janitor ) |
420 |
{ |
|
421 | 0 |
String sample = null;
|
422 | 0 |
String text = source.getLine( line, janitor ); |
423 |
|
|
424 | 0 |
if( text != null ) |
425 |
{ |
|
426 | 0 |
if( column > 0 )
|
427 |
{ |
|
428 | 0 |
String marker = Utilities.repeatString(" ", column-1) + "^"; |
429 |
|
|
430 | 0 |
if( column > 40 )
|
431 |
{ |
|
432 | 0 |
int start = column - 30 - 1;
|
433 | 0 |
int end = (column + 10 > text.length() ? text.length() : column + 10 - 1);
|
434 | 0 |
sample = " " + text.substring( start, end ) + Utilities.eol() + " " + marker.substring( start, marker.length() ); |
435 |
} |
|
436 |
else
|
|
437 |
{ |
|
438 | 0 |
sample = " " + text + Utilities.eol() + " " + marker; |
439 |
} |
|
440 |
} |
|
441 |
else
|
|
442 |
{ |
|
443 | 0 |
sample = text; |
444 |
} |
|
445 |
} |
|
446 |
|
|
447 | 0 |
return sample;
|
448 |
} |
|
449 |
|
|
450 |
/**
|
|
451 |
* to quickly create a ModuleNode from a piece of Groovy code
|
|
452 |
* @param code
|
|
453 |
* @return
|
|
454 |
* @throws CompilationFailedException
|
|
455 |
*/
|
|
456 | 0 |
public static ModuleNode createModule(String code) throws CompilationFailedException { |
457 | 0 |
SourceUnit su = create("NodeGen", code);
|
458 | 0 |
su.parse(); |
459 | 0 |
su.convert(); |
460 | 0 |
return su.getAST();
|
461 |
} |
|
462 |
|
|
463 | 0 |
public static ClassNode createClassNode(String code) throws CompilationFailedException { |
464 | 0 |
ModuleNode module = createModule(code); |
465 | 0 |
List classes = module.getClasses(); |
466 | 0 |
if (classes.size() > 1) {
|
467 | 0 |
throw new RuntimeException("The code defines more than one class"); |
468 |
} |
|
469 | 0 |
return (ClassNode) classes.get(0);
|
470 |
} |
|
471 |
|
|
472 |
/**
|
|
473 |
* Takes a field definition statement and wrap it in class definition. The FieldNode object
|
|
474 |
* representing the field is extracted and returned, Types need to be fully qualified.
|
|
475 |
* @param code a naked statement to define a field, such as: String prop = "hello"
|
|
476 |
* @return a FieldNode object.
|
|
477 |
* @throws CompilationFailedException
|
|
478 |
*/
|
|
479 | 0 |
public static FieldNode createFieldNode(String code) throws CompilationFailedException { |
480 | 0 |
ClassNode classNode = createClassNode(wrapCode(code)); |
481 | 0 |
List flds = classNode.getFields(); |
482 | 0 |
if (flds.size() > 1)
|
483 | 0 |
throw new RuntimeException("The code defines more than one field"); |
484 | 0 |
return (FieldNode) flds.get(0);
|
485 |
} |
|
486 |
|
|
487 | 0 |
public Statement createStatement(String code) throws CompilationFailedException { |
488 | 0 |
ModuleNode module = createModule(code); |
489 | 0 |
BlockStatement block = module.getStatementBlock(); |
490 | 0 |
if (block == null) |
491 | 0 |
throw new RuntimeException("no proper statement block is created."); |
492 | 0 |
List stats = block.getStatements(); |
493 | 0 |
if (stats == null || stats.size() != 1) |
494 | 0 |
throw new RuntimeException("no proper statement node is created."); |
495 | 0 |
return (Statement)stats.get(0);
|
496 |
} |
|
497 |
|
|
498 | 0 |
public MethodNode createMethodNode(String code) throws CompilationFailedException { |
499 | 0 |
code = code.trim(); |
500 | 0 |
if (code.indexOf("def") != 0) { |
501 | 0 |
code = "def " + code;
|
502 |
} |
|
503 | 0 |
ModuleNode module = createModule(code); |
504 | 0 |
List ms = module.getMethods(); |
505 | 0 |
if (ms == null || ms.size() != 1) |
506 | 0 |
throw new RuntimeException("no proper method node is created."); |
507 | 0 |
return (MethodNode)ms.get(0);
|
508 |
} |
|
509 |
|
|
510 | 0 |
private static String wrapCode(String code) { |
511 | 0 |
String prefix = "class SynthedClass {\n";
|
512 | 0 |
String suffix = "\n }";
|
513 | 0 |
return prefix + code + suffix;
|
514 |
|
|
515 |
} |
|
516 |
} |
|
517 |
|
|
518 |
|
|
519 |
|
|
520 |
|
|
521 |
|
|