Clover coverage report - groovy - 1.0-beta-7
Coverage timestamp: Wed Sep 29 2004 16:55:52 BST
file stats: LOC: 614   Methods: 30
NCLOC: 234   Classes: 1
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
CSTNode.java 0% 0% 0% 0%
coverage
 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;
 52   
 import java.io.PrintWriter;
 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  0
     public int getMeaning()
 84   
     {
 85  0
         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  0
     public CSTNode setMeaning( int meaning )
 96   
     {
 97  0
         getRoot().setMeaning( meaning );
 98  0
         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  0
     public int getType()
 109   
     {
 110  0
         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  0
     public boolean canMean( int type )
 120   
     {
 121  0
         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  0
     public boolean isA( int type )
 131   
     {
 132  0
         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  0
     public boolean isOneOf( int[] types )
 142   
     {
 143  0
         int meaning = getMeaning();
 144  0
         for( int i = 0; i < types.length; i++ )
 145   
         {
 146  0
             if( Types.ofType(meaning, types[i]) )
 147   
             {
 148  0
                 return true;
 149   
             }
 150   
         }
 151   
 
 152  0
         return false;
 153   
     }
 154   
 
 155   
 
 156   
 
 157   
    /**
 158   
     *  Returns true if the node's meaning matches all of the specified types.
 159   
     */
 160   
 
 161  0
     public boolean isAllOf( int[] types )
 162   
     {
 163  0
         int meaning = getMeaning();
 164  0
         for( int i = 0; i < types.length; i++ )
 165   
         {
 166  0
             if( !Types.ofType(meaning, types[i]) )
 167   
             {
 168  0
                 return false;
 169   
             }
 170   
         }
 171   
 
 172  0
         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  0
     public int getMeaningAs( int[] types )
 183   
     {
 184   
 
 185  0
         for( int i = 0; i < types.length; i++ )
 186   
         {
 187  0
             if( isA(types[i]) )
 188   
             {
 189  0
                 return types[i];
 190   
             }
 191   
         }
 192   
 
 193  0
         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  0
     boolean matches( int type )
 209   
     {
 210  0
         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  0
     boolean matches( int type, int child1 )
 221   
     {
 222  0
         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  0
     boolean matches( int type, int child1, int child2 )
 233   
     {
 234  0
         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  0
     boolean matches( int type, int child1, int child2, int child3 )
 245   
     {
 246  0
         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  0
     boolean matches( int type, int child1, int child2, int child3, int child4 )
 257   
     {
 258  0
         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  0
     public boolean isEmpty()
 274   
     {
 275  0
         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  0
     public boolean hasChildren()
 293   
     {
 294  0
         return children() > 0;
 295   
     }
 296   
 
 297   
 
 298   
 
 299   
    /**
 300   
     *  Returns the number of non-root elements in the node.
 301   
     */
 302   
 
 303  0
     public int children()
 304   
     {
 305  0
         int size = size();
 306  0
         if( size > 1 )
 307   
         {
 308  0
             return size - 1;
 309   
         }
 310  0
         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  0
     public CSTNode get( int index, boolean safe )
 330   
     {
 331  0
         CSTNode element = get( index );
 332   
 
 333  0
         if( element == null && safe )
 334   
         {
 335  0
             element = Token.NULL;
 336   
         }
 337   
 
 338  0
         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  0
     public Token getRoot( boolean safe )
 359   
     {
 360  0
         Token root = getRoot();
 361   
 
 362  0
         if( root == null && safe )
 363   
         {
 364  0
             root = Token.NULL;
 365   
         }
 366   
 
 367  0
         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  0
     public String getRootText()
 379   
     {
 380  0
         Token root = getRoot( true );
 381  0
         return root.getText();
 382   
     }
 383   
 
 384   
 
 385   
 
 386   
    /**
 387   
     *  Returns a description of the node.
 388   
     */
 389   
 
 390  0
     public String getDescription()
 391   
     {
 392  0
         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  0
     public int getStartLine()
 403   
     {
 404  0
         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  0
     public int getStartColumn()
 415   
     {
 416  0
         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  0
     public void markAsExpression()
 427   
     {
 428  0
         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  0
     public boolean isAnExpression()
 438   
     {
 439  0
         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  0
     public CSTNode add( CSTNode element )
 456   
     {
 457  0
         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  0
     public void addChildrenOf( CSTNode of )
 468   
     {
 469  0
         for( int i = 1; i < of.size(); i++ )
 470   
         {
 471  0
             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  0
     public CSTNode set( int index, CSTNode element )
 483   
     {
 484  0
         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  0
     public String toString()
 508   
     {
 509  0
         StringWriter string = new StringWriter();
 510  0
         write( new PrintWriter(string) );
 511   
 
 512  0
         string.flush();
 513  0
         return string.toString();
 514   
     }
 515   
 
 516   
 
 517   
    /**
 518   
     *  Formats the node and writes it to the specified <code>Writer</code>.
 519   
     */
 520   
 
 521  0
     public void write( PrintWriter writer )
 522   
     {
 523  0
         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  0
     protected void write( PrintWriter writer, String indent )
 534   
     {
 535  0
         writer.print( "(" );
 536   
 
 537  0
         if( !isEmpty() )
 538   
         {
 539  0
             Token  root = getRoot( true );
 540  0
             int    type = root.getType();
 541  0
             int meaning = root.getMeaning();
 542   
 
 543   
 
 544   
             //
 545   
             // Display our type, text, and (optional) meaning
 546   
 
 547  0
             writer.print( Types.getDescription(type) );
 548   
 
 549  0
             if( meaning != type )
 550   
             {
 551  0
                 writer.print( " as " );
 552  0
                 writer.print( Types.getDescription(meaning) );
 553   
             }
 554   
 
 555  0
             if( getStartLine() > -1 )
 556   
             {
 557  0
                 writer.print( " at " + getStartLine() + ":" + getStartColumn() );
 558   
             }
 559   
 
 560  0
             String text = root.getText();
 561  0
             int  length = text.length();
 562  0
             if( length > 0 )
 563   
             {
 564  0
                 writer.print( ": " );
 565  0
                 if( length > 40 )
 566   
                 {
 567  0
                    text = text.substring( 0, 17 ) + "..." + text.substring( length - 17, length );
 568   
                 }
 569   
 
 570  0
                 writer.print( " \"" );
 571  0
                 writer.print( text );
 572  0
                 writer.print( "\" " );
 573   
             }
 574  0
             else if( children() > 0 )
 575   
             {
 576  0
                 writer.print( ": " );
 577   
             }
 578   
 
 579   
 
 580   
 
 581   
             //
 582   
             // Recurse to display the children.
 583   
 
 584  0
             int count = size();
 585  0
             if( count > 1 )
 586   
             {
 587  0
                 writer.println( "" );
 588   
 
 589  0
                 String indent1 = indent + "  ";
 590  0
                 String indent2 = indent + "   ";
 591  0
                 for( int i = 1; i < count; i++ )
 592   
                 {
 593  0
                     writer.print( indent1 );
 594  0
                     writer.print( i );
 595  0
                     writer.print( ": " );
 596   
 
 597  0
                     get( i, true ).write( writer, indent2 );
 598   
                 }
 599   
 
 600  0
                 writer.print( indent );
 601   
             }
 602   
         }
 603   
 
 604  0
         if( indent.length() > 0 )
 605   
         {
 606  0
             writer.println( ")" );
 607   
         }
 608   
         else
 609   
         {
 610  0
             writer.print( ")" );
 611   
         }
 612   
     }
 613   
 }
 614