001 /* 002 $Id: CSTNode.java 4032 2006-08-30 07:18:49Z mguillem $ 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 antlr.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 4032 2006-08-30 07:18:49Z mguillem $ 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 }