001 /*
002 $Id: CSTNode.java,v 1.2 2005/04/12 15:04:59 jstrachan 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.syntax;
048
049 import org.codehaus.groovy.GroovyBugError;
050 import org.codehaus.groovy.syntax.Token;
051 import org.codehaus.groovy.syntax.Types;
052 import org.codehaus.groovy.syntax.Reduction;
053
054 import java.io.StringWriter;
055 import java.io.PrintWriter;
056
057
058 /**
059 * An abstract base class for nodes in the concrete syntax tree that is
060 * the result of parsing. Note that the CSTNode is inextricably linked
061 * with the Token in that every CSTNode has a Token as it's root.
062 *
063 * @see org.codehaus.groovy.syntax.parser.Parser
064 * @see Token
065 * @see org.codehaus.groovy.syntax.Reduction
066 * @see org.codehaus.groovy.syntax.Types
067 *
068 * @author <a href="mailto:bob@werken.com">bob mcwhirter</a>
069 * @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
070 *
071 * @version $Id: CSTNode.java,v 1.2 2005/04/12 15:04:59 jstrachan Exp $
072 */
073
074 public abstract class CSTNode
075 {
076
077 //---------------------------------------------------------------------------
078 // NODE IDENTIFICATION AND MEANING
079
080
081 /**
082 * Returns the meaning of this node. If the node isEmpty(), returns
083 * the type of Token.NULL.
084 */
085
086 public int getMeaning()
087 {
088 return getRoot( true ).getMeaning();
089 }
090
091
092
093 /**
094 * Sets the meaning for this node (and it's root Token). Not
095 * valid if the node isEmpty(). Returns the node, for convenience.
096 */
097
098 public CSTNode setMeaning( int meaning )
099 {
100 getRoot().setMeaning( meaning );
101 return this;
102 }
103
104
105
106 /**
107 * Returns the actual type of the node. If the node isEmpty(), returns
108 * the type of Token.NULL.
109 */
110
111 public int getType()
112 {
113 return getRoot( true ).getType();
114 }
115
116
117
118 /**
119 * Returns true if the node can be coerced to the specified type.
120 */
121
122 public boolean canMean( int type )
123 {
124 return Types.canMean( getMeaning(), type );
125 }
126
127
128
129 /**
130 * Returns true if the node's meaning matches the specified type.
131 */
132
133 public boolean isA( int type )
134 {
135 return Types.ofType( getMeaning(), type );
136 }
137
138
139
140 /**
141 * Returns true if the node's meaning matches any of the specified types.
142 */
143
144 public boolean isOneOf( int[] types )
145 {
146 int meaning = getMeaning();
147 for( int i = 0; i < types.length; i++ )
148 {
149 if( Types.ofType(meaning, types[i]) )
150 {
151 return true;
152 }
153 }
154
155 return false;
156 }
157
158
159
160 /**
161 * Returns true if the node's meaning matches all of the specified types.
162 */
163
164 public boolean isAllOf( int[] types )
165 {
166 int meaning = getMeaning();
167 for( int i = 0; i < types.length; i++ )
168 {
169 if( !Types.ofType(meaning, types[i]) )
170 {
171 return false;
172 }
173 }
174
175 return true;
176 }
177
178
179
180 /**
181 * Returns the first matching meaning of the specified types.
182 * Returns Types.UNKNOWN if there are no matches.
183 */
184
185 public int getMeaningAs( int[] types )
186 {
187
188 for( int i = 0; i < types.length; i++ )
189 {
190 if( isA(types[i]) )
191 {
192 return types[i];
193 }
194 }
195
196 return Types.UNKNOWN;
197 }
198
199
200
201
202 //---------------------------------------------------------------------------
203 // TYPE SUGAR
204
205
206 /**
207 * Returns true if the node matches the specified type. Effectively
208 * a synonym for <code>isA()</code>. Missing nodes are Token.NULL.
209 */
210
211 boolean matches( int type )
212 {
213 return isA(type);
214 }
215
216
217
218 /**
219 * Returns true if the node and it's first child match the specified
220 * types. Missing nodes are Token.NULL.
221 */
222
223 boolean matches( int type, int child1 )
224 {
225 return isA(type) && get(1, true).isA(child1);
226 }
227
228
229
230 /**
231 * Returns true if the node and it's first and second child match the
232 * specified types. Missing nodes are Token.NULL.
233 */
234
235 boolean matches( int type, int child1, int child2 )
236 {
237 return matches( type, child1 ) && get(2, true).isA(child2);
238 }
239
240
241
242 /**
243 * Returns true if the node and it's first three children match the
244 * specified types. Missing nodes are Token.NULL.
245 */
246
247 boolean matches( int type, int child1, int child2, int child3 )
248 {
249 return matches( type, child1, child2 ) && get(3, true).isA(child3);
250 }
251
252
253
254 /**
255 * Returns true if the node an it's first four children match the
256 * specified types. Missing nodes have type Types.NULL.
257 */
258
259 boolean matches( int type, int child1, int child2, int child3, int child4 )
260 {
261 return matches( type, child1, child2, child3 ) && get(4, true).isA(child4);
262 }
263
264
265
266
267
268 //---------------------------------------------------------------------------
269 // MEMBER ACCESS
270
271
272 /**
273 * Returns true if the node is completely empty (no root, even).
274 */
275
276 public boolean isEmpty()
277 {
278 return false;
279 }
280
281
282
283 /**
284 * Returns the number of elements in the node (including root).
285 */
286
287 public abstract int size();
288
289
290
291 /**
292 * Returns true if the node has any non-root elements.
293 */
294
295 public boolean hasChildren()
296 {
297 return children() > 0;
298 }
299
300
301
302 /**
303 * Returns the number of non-root elements in the node.
304 */
305
306 public int children()
307 {
308 int size = size();
309 if( size > 1 )
310 {
311 return size - 1;
312 }
313 return 0;
314 }
315
316
317
318 /**
319 * Returns the specified element, or null.
320 */
321
322 public abstract CSTNode get( int index );
323
324
325
326 /**
327 * Returns the specified element, or Token.NULL if
328 * safe is set and the specified element is null (or doesn't
329 * exist).
330 */
331
332 public CSTNode get( int index, boolean safe )
333 {
334 CSTNode element = get( index );
335
336 if( element == null && safe )
337 {
338 element = Token.NULL;
339 }
340
341 return element;
342 }
343
344
345
346 /**
347 * Returns the root of the node. By convention, all nodes have
348 * a Token as the first element (or root), which indicates the type
349 * of the node. May return null if the node <code>isEmpty()</code>.
350 */
351
352 public abstract Token getRoot();
353
354
355
356 /**
357 * Returns the root of the node, the Token that indicates it's
358 * type. Returns a Token.NULL if safe and the actual root is null.
359 */
360
361 public Token getRoot( boolean safe )
362 {
363 Token root = getRoot();
364
365 if( root == null && safe )
366 {
367 root = Token.NULL;
368 }
369
370 return root;
371 }
372
373
374
375 /**
376 * Returns the text of the root. Uses <code>getRoot(true)</code>
377 * to get the root, so you will only receive null in return if the
378 * root token returns it.
379 */
380
381 public String getRootText()
382 {
383 Token root = getRoot( true );
384 return root.getText();
385 }
386
387
388
389 /**
390 * Returns a description of the node.
391 */
392
393 public String getDescription()
394 {
395 return Types.getDescription( getMeaning() );
396 }
397
398
399
400 /**
401 * Returns the starting line of the node. Returns -1
402 * if not known.
403 */
404
405 public int getStartLine()
406 {
407 return getRoot(true).getStartLine();
408 }
409
410
411
412 /**
413 * Returns the starting column of the node. Returns -1
414 * if not known.
415 */
416
417 public int getStartColumn()
418 {
419 return getRoot(true).getStartColumn();
420 }
421
422
423
424 /**
425 * Marks the node a complete expression. Not all nodes support
426 * this operation!
427 */
428
429 public void markAsExpression()
430 {
431 throw new GroovyBugError( "markAsExpression() not supported for this CSTNode type" );
432 }
433
434
435
436 /**
437 * Returns true if the node is a complete expression.
438 */
439
440 public boolean isAnExpression()
441 {
442 return isA(Types.SIMPLE_EXPRESSION);
443 }
444
445
446
447
448
449 //---------------------------------------------------------------------------
450 // OPERATIONS
451
452
453 /**
454 * Adds an element to the node. Returns the element for convenience.
455 * Not all nodes support this operation!
456 */
457
458 public CSTNode add( CSTNode element )
459 {
460 throw new GroovyBugError( "add() not supported for this CSTNode type" );
461 }
462
463
464
465 /**
466 * Adds all children of the specified node to this one. Not all
467 * nodes support this operation!
468 */
469
470 public void addChildrenOf( CSTNode of )
471 {
472 for( int i = 1; i < of.size(); i++ )
473 {
474 add( of.get(i) );
475 }
476 }
477
478
479
480 /**
481 * Sets an element node in at the specified index. Returns the element
482 * for convenience. Not all nodes support this operation!
483 */
484
485 public CSTNode set( int index, CSTNode element )
486 {
487 throw new GroovyBugError( "set() not supported for this CSTNode type" );
488 }
489
490
491
492 /**
493 * Creates a <code>Reduction</code> from this node. Returns self if the
494 * node is already a <code>Reduction</code>.
495 */
496
497 public abstract Reduction asReduction();
498
499
500
501
502 //---------------------------------------------------------------------------
503 // STRING CONVERSION
504
505
506 /**
507 * Formats the node as a <code>String</code> and returns it.
508 */
509
510 public String toString()
511 {
512 StringWriter string = new StringWriter();
513 write( new PrintWriter(string) );
514
515 string.flush();
516 return string.toString();
517 }
518
519
520 /**
521 * Formats the node and writes it to the specified <code>Writer</code>.
522 */
523
524 public void write( PrintWriter writer )
525 {
526 write( writer, "" );
527 }
528
529
530 /**
531 * Formats the node and writes it to the specified <code>Writer</code>.
532 * The indent is prepended to each output line, and is increased for each
533 * recursion.
534 */
535
536 protected void write( PrintWriter writer, String indent )
537 {
538 writer.print( "(" );
539
540 if( !isEmpty() )
541 {
542 Token root = getRoot( true );
543 int type = root.getType();
544 int meaning = root.getMeaning();
545
546
547 //
548 // Display our type, text, and (optional) meaning
549
550 writer.print( Types.getDescription(type) );
551
552 if( meaning != type )
553 {
554 writer.print( " as " );
555 writer.print( Types.getDescription(meaning) );
556 }
557
558 if( getStartLine() > -1 )
559 {
560 writer.print( " at " + getStartLine() + ":" + getStartColumn() );
561 }
562
563 String text = root.getText();
564 int length = text.length();
565 if( length > 0 )
566 {
567 writer.print( ": " );
568 if( length > 40 )
569 {
570 text = text.substring( 0, 17 ) + "..." + text.substring( length - 17, length );
571 }
572
573 writer.print( " \"" );
574 writer.print( text );
575 writer.print( "\" " );
576 }
577 else if( children() > 0 )
578 {
579 writer.print( ": " );
580 }
581
582
583
584 //
585 // Recurse to display the children.
586
587 int count = size();
588 if( count > 1 )
589 {
590 writer.println( "" );
591
592 String indent1 = indent + " ";
593 String indent2 = indent + " ";
594 for( int i = 1; i < count; i++ )
595 {
596 writer.print( indent1 );
597 writer.print( i );
598 writer.print( ": " );
599
600 get( i, true ).write( writer, indent2 );
601 }
602
603 writer.print( indent );
604 }
605 }
606
607 if( indent.length() > 0 )
608 {
609 writer.println( ")" );
610 }
611 else
612 {
613 writer.print( ")" );
614 }
615 }
616 }