KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > codehaus > groovy > syntax > CSTNode


1 /*
2  $Id: CSTNode.java,v 1.1 2004/04/01 06:21:53 cpoirier 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 java.io.StringWriter JavaDoc;
52 import java.io.PrintWriter JavaDoc;
53
54
55 /**
56  * An abstract base class for nodes in the concrete syntax tree that is
57  * the result of parsing. Note that the CSTNode is inextricably linked
58  * with the Token in that every CSTNode has a Token as it's root.
59  *
60  * @see Parser
61  * @see Token
62  * @see Reduction
63  * @see Types
64  *
65  * @author <a HREF="mailto:bob@werken.com">bob mcwhirter</a>
66  * @author <a HREF="mailto:cpoirier@dreaming.org">Chris Poirier</a>
67  *
68  * @version $Id: CSTNode.java,v 1.1 2004/04/01 06:21:53 cpoirier Exp $
69  */

70
71 public abstract class CSTNode
72 {
73
74   //---------------------------------------------------------------------------
75
// NODE IDENTIFICATION AND MEANING
76

77
78    /**
79     * Returns the meaning of this node. If the node isEmpty(), returns
80     * the type of Token.NULL.
81     */

82
83     public int getMeaning()
84     {
85         return getRoot( true ).getMeaning();
86     }
87
88
89
90    /**
91     * Sets the meaning for this node (and it's root Token). Not
92     * valid if the node isEmpty(). Returns the node, for convenience.
93     */

94
95     public CSTNode setMeaning( int meaning )
96     {
97         getRoot().setMeaning( meaning );
98         return this;
99     }
100
101
102
103    /**
104     * Returns the actual type of the node. If the node isEmpty(), returns
105     * the type of Token.NULL.
106     */

107
108     public int getType()
109     {
110         return getRoot( true ).getType();
111     }
112
113
114
115    /**
116     * Returns true if the node can be coerced to the specified type.
117     */

118
119     public boolean canMean( int type )
120     {
121         return Types.canMean( getMeaning(), type );
122     }
123
124
125
126    /**
127     * Returns true if the node's meaning matches the specified type.
128     */

129
130     public boolean isA( int type )
131     {
132         return Types.ofType( getMeaning(), type );
133     }
134
135
136
137    /**
138     * Returns true if the node's meaning matches any of the specified types.
139     */

140
141     public boolean isOneOf( int[] types )
142     {
143         int meaning = getMeaning();
144         for( int i = 0; i < types.length; i++ )
145         {
146             if( Types.ofType(meaning, types[i]) )
147             {
148                 return true;
149             }
150         }
151
152         return false;
153     }
154
155
156
157    /**
158     * Returns true if the node's meaning matches all of the specified types.
159     */

160
161     public boolean isAllOf( int[] types )
162     {
163         int meaning = getMeaning();
164         for( int i = 0; i < types.length; i++ )
165         {
166             if( !Types.ofType(meaning, types[i]) )
167             {
168                 return false;
169             }
170         }
171
172         return true;
173     }
174
175
176
177    /**
178     * Returns the first matching meaning of the specified types.
179     * Returns Types.UNKNOWN if there are no matches.
180     */

181
182     public int getMeaningAs( int[] types )
183     {
184
185         for( int i = 0; i < types.length; i++ )
186         {
187             if( isA(types[i]) )
188             {
189                 return types[i];
190             }
191         }
192
193         return Types.UNKNOWN;
194     }
195
196
197
198
199   //---------------------------------------------------------------------------
200
// TYPE SUGAR
201

202
203    /**
204     * Returns true if the node matches the specified type. Effectively
205     * a synonym for <code>isA()</code>. Missing nodes are Token.NULL.
206     */

207
208     boolean matches( int type )
209     {
210         return isA(type);
211     }
212
213
214
215    /**
216     * Returns true if the node and it's first child match the specified
217     * types. Missing nodes are Token.NULL.
218     */

219
220     boolean matches( int type, int child1 )
221     {
222         return isA(type) && get(1, true).isA(child1);
223     }
224
225
226
227    /**
228     * Returns true if the node and it's first and second child match the
229     * specified types. Missing nodes are Token.NULL.
230     */

231
232     boolean matches( int type, int child1, int child2 )
233     {
234         return matches( type, child1 ) && get(2, true).isA(child2);
235     }
236
237
238
239    /**
240     * Returns true if the node and it's first three children match the
241     * specified types. Missing nodes are Token.NULL.
242     */

243
244     boolean matches( int type, int child1, int child2, int child3 )
245     {
246         return matches( type, child1, child2 ) && get(3, true).isA(child3);
247     }
248
249
250
251    /**
252     * Returns true if the node an it's first four children match the
253     * specified types. Missing nodes have type Types.NULL.
254     */

255
256     boolean matches( int type, int child1, int child2, int child3, int child4 )
257     {
258         return matches( type, child1, child2, child3 ) && get(4, true).isA(child4);
259     }
260
261
262
263
264
265   //---------------------------------------------------------------------------
266
// MEMBER ACCESS
267

268
269    /**
270     * Returns true if the node is completely empty (no root, even).
271     */

272
273     public boolean isEmpty()
274     {
275         return false;
276     }
277
278
279
280    /**
281     * Returns the number of elements in the node (including root).
282     */

283
284     public abstract int size();
285
286
287
288    /**
289     * Returns true if the node has any non-root elements.
290     */

291
292     public boolean hasChildren()
293     {
294         return children() > 0;
295     }
296
297
298
299    /**
300     * Returns the number of non-root elements in the node.
301     */

302
303     public int children()
304     {
305         int size = size();
306         if( size > 1 )
307         {
308             return size - 1;
309         }
310         return 0;
311     }
312
313
314
315    /**
316     * Returns the specified element, or null.
317     */

318
319     public abstract CSTNode get( int index );
320
321
322
323    /**
324     * Returns the specified element, or Token.NULL if
325     * safe is set and the specified element is null (or doesn't
326     * exist).
327     */

328
329     public CSTNode get( int index, boolean safe )
330     {
331         CSTNode element = get( index );
332
333         if( element == null && safe )
334         {
335             element = Token.NULL;
336         }
337
338         return element;
339     }
340
341
342
343    /**
344     * Returns the root of the node. By convention, all nodes have
345     * a Token as the first element (or root), which indicates the type
346     * of the node. May return null if the node <code>isEmpty()</code>.
347     */

348
349     public abstract Token getRoot();
350
351
352
353    /**
354     * Returns the root of the node, the Token that indicates it's
355     * type. Returns a Token.NULL if safe and the actual root is null.
356     */

357
358     public Token getRoot( boolean safe )
359     {
360         Token root = getRoot();
361
362         if( root == null && safe )
363         {
364             root = Token.NULL;
365         }
366
367         return root;
368     }
369
370
371
372    /**
373     * Returns the text of the root. Uses <code>getRoot(true)</code>
374     * to get the root, so you will only receive null in return if the
375     * root token returns it.
376     */

377
378     public String JavaDoc getRootText()
379     {
380         Token root = getRoot( true );
381         return root.getText();
382     }
383
384
385
386    /**
387     * Returns a description of the node.
388     */

389
390     public String JavaDoc getDescription()
391     {
392         return Types.getDescription( getMeaning() );
393     }
394
395
396
397    /**
398     * Returns the starting line of the node. Returns -1
399     * if not known.
400     */

401
402     public int getStartLine()
403     {
404         return getRoot(true).getStartLine();
405     }
406
407
408
409    /**
410     * Returns the starting column of the node. Returns -1
411     * if not known.
412     */

413
414     public int getStartColumn()
415     {
416         return getRoot(true).getStartColumn();
417     }
418
419
420
421    /**
422     * Marks the node a complete expression. Not all nodes support
423     * this operation!
424     */

425
426     public void markAsExpression()
427     {
428         throw new GroovyBugError( "markAsExpression() not supported for this CSTNode type" );
429     }
430
431
432
433    /**
434     * Returns true if the node is a complete expression.
435     */

436
437     public boolean isAnExpression()
438     {
439         return isA(Types.SIMPLE_EXPRESSION);
440     }
441
442
443
444
445
446   //---------------------------------------------------------------------------
447
// OPERATIONS
448

449
450    /**
451     * Adds an element to the node. Returns the element for convenience.
452     * Not all nodes support this operation!
453     */

454
455     public CSTNode add( CSTNode element )
456     {
457         throw new GroovyBugError( "add() not supported for this CSTNode type" );
458     }
459
460
461
462    /**
463     * Adds all children of the specified node to this one. Not all
464     * nodes support this operation!
465     */

466
467     public void addChildrenOf( CSTNode of )
468     {
469         for( int i = 1; i < of.size(); i++ )
470         {
471             add( of.get(i) );
472         }
473     }
474
475
476
477    /**
478     * Sets an element node in at the specified index. Returns the element
479     * for convenience. Not all nodes support this operation!
480     */

481
482     public CSTNode set( int index, CSTNode element )
483     {
484         throw new GroovyBugError( "set() not supported for this CSTNode type" );
485     }
486
487
488
489    /**
490     * Creates a <code>Reduction</code> from this node. Returns self if the
491     * node is already a <code>Reduction</code>.
492     */

493
494     public abstract Reduction asReduction();
495
496
497
498
499   //---------------------------------------------------------------------------
500
// STRING CONVERSION
501

502
503    /**
504     * Formats the node as a <code>String</code> and returns it.
505     */

506
507     public String JavaDoc toString()
508     {
509         StringWriter JavaDoc string = new StringWriter JavaDoc();
510         write( new PrintWriter JavaDoc(string) );
511
512         string.flush();
513         return string.toString();
514     }
515
516
517    /**
518     * Formats the node and writes it to the specified <code>Writer</code>.
519     */

520
521     public void write( PrintWriter JavaDoc writer )
522     {
523         write( writer, "" );
524     }
525
526
527    /**
528     * Formats the node and writes it to the specified <code>Writer</code>.
529     * The indent is prepended to each output line, and is increased for each
530     * recursion.
531     */

532
533     protected void write( PrintWriter JavaDoc writer, String JavaDoc indent )
534     {
535         writer.print( "(" );
536
537         if( !isEmpty() )
538         {
539             Token root = getRoot( true );
540             int type = root.getType();
541             int meaning = root.getMeaning();
542
543
544             //
545
// Display our type, text, and (optional) meaning
546

547             writer.print( Types.getDescription(type) );
548
549             if( meaning != type )
550             {
551                 writer.print( " as " );
552                 writer.print( Types.getDescription(meaning) );
553             }
554
555             if( getStartLine() > -1 )
556             {
557                 writer.print( " at " + getStartLine() + ":" + getStartColumn() );
558             }
559
560             String JavaDoc text = root.getText();
561             int length = text.length();
562             if( length > 0 )
563             {
564                 writer.print( ": " );
565                 if( length > 40 )
566                 {
567                    text = text.substring( 0, 17 ) + "..." + text.substring( length - 17, length );
568                 }
569
570                 writer.print( " \"" );
571                 writer.print( text );
572                 writer.print( "\" " );
573             }
574             else if( children() > 0 )
575             {
576                 writer.print( ": " );
577             }
578
579
580
581             //
582
// Recurse to display the children.
583

584             int count = size();
585             if( count > 1 )
586             {
587                 writer.println( "" );
588
589                 String JavaDoc indent1 = indent + " ";
590                 String JavaDoc indent2 = indent + " ";
591                 for( int i = 1; i < count; i++ )
592                 {
593                     writer.print( indent1 );
594                     writer.print( i );
595                     writer.print( ": " );
596
597                     get( i, true ).write( writer, indent2 );
598                 }
599
600                 writer.print( indent );
601             }
602         }
603
604         if( indent.length() > 0 )
605         {
606             writer.println( ")" );
607         }
608         else
609         {
610             writer.print( ")" );
611         }
612     }
613 }
614
Popular Tags