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    }