View Javadoc

1   /*
2    $Id: CSTNode.java,v 1.2 2005/04/12 15:04:59 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.syntax;
48  
49  import org.codehaus.groovy.GroovyBugError;
50  import org.codehaus.groovy.syntax.Token;
51  import org.codehaus.groovy.syntax.Types;
52  import org.codehaus.groovy.syntax.Reduction;
53  
54  import java.io.StringWriter;
55  import java.io.PrintWriter;
56  
57  
58  /***
59   *  An abstract base class for nodes in the concrete syntax tree that is
60   *  the result of parsing.  Note that the CSTNode is inextricably linked
61   *  with the Token in that every CSTNode has a Token as it's root.
62   *
63   *  @see org.codehaus.groovy.syntax.parser.Parser
64   *  @see Token
65   *  @see org.codehaus.groovy.syntax.Reduction
66   *  @see org.codehaus.groovy.syntax.Types
67   *
68   *  @author <a href="mailto:bob@werken.com">bob mcwhirter</a>
69   *  @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
70   *
71   *  @version $Id: CSTNode.java,v 1.2 2005/04/12 15:04:59 jstrachan Exp $
72   */
73  
74  public abstract class CSTNode
75  {
76  
77    //---------------------------------------------------------------------------
78    // NODE IDENTIFICATION AND MEANING
79  
80  
81     /***
82      *  Returns the meaning of this node.  If the node isEmpty(), returns
83      *  the type of Token.NULL.
84      */
85  
86      public int getMeaning()
87      {
88          return getRoot( true ).getMeaning();
89      }
90  
91  
92  
93     /***
94      *  Sets the meaning for this node (and it's root Token).  Not
95      *  valid if the node isEmpty().  Returns the node, for convenience.
96      */
97  
98      public CSTNode setMeaning( int meaning )
99      {
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 }