src/content/engine/1.6.2/xref/org/apache/velocity/runtime/parser/Parser.html [37:848]: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 27 */ 28 public class Parser/*@bgen(jjtree)*/implements ParserTreeConstants, ParserConstants {/*@bgen(jjtree)*/ 29 protected JJTParserState jjtree = new JJTParserState();/** 30 * This Hashtable contains a list of all of the dynamic directives. 31 */ 32 private Hashtable directives = new Hashtable(0); 33 34 /** 35 * Name of current template we are parsing. Passed to us in parse() 36 */ 37 public String currentTemplateName = ""; 38 39 VelocityCharStream velcharstream = null; 40 41 private RuntimeServices rsvc = null; 42 43 /** 44 * This constructor was added to allow the re-use of parsers. 45 * The normal constructor takes a single argument which 46 * an InputStream. This simply creates a re-usable parser 47 * object, we satisfy the requirement of an InputStream 48 * by using a newline character as an input stream. 49 */ 50 public Parser( RuntimeServices rs) 51 { 52 /* 53 * need to call the CTOR first thing. 54 */ 55 56 this( new VelocityCharStream( 57 new ByteArrayInputStream("\n".getBytes()), 1, 1 )); 58 59 /* 60 * now setup a VCS for later use 61 */ 62 velcharstream = new VelocityCharStream( 63 new ByteArrayInputStream("\n".getBytes()), 1, 1 ); 64 65 /* 66 * and save the RuntimeServices 67 */ 68 rsvc = rs; 69 } 70 71 /** 72 * This was also added to allow parsers to be 73 * re-usable. Normal JavaCC use entails passing an 74 * input stream to the constructor and the parsing 75 * process is carried out once. We want to be able 76 * to re-use parsers: we do this by adding this 77 * method and re-initializing the lexer with 78 * the new stream that we want parsed. 79 */ 80 public SimpleNode parse( Reader reader, String templateName ) 81 throws ParseException 82 { 83 SimpleNode sn = null; 84 85 currentTemplateName = templateName; 86 87 try 88 { 89 token_source.clearStateVars(); 90 91 /* 92 * reinitialize the VelocityCharStream 93 * with the new reader 94 */ 95 velcharstream.ReInit( reader, 1, 1 ); 96 97 /* 98 * now reinit the Parser with this CharStream 99 */ 100 ReInit( velcharstream ); 101 102 /* 103 * do that voodoo... 104 */ 105 sn = process(); 106 } 107 catch (MacroParseException mee) 108 { 109 /* 110 * thrown by the Macro class when something is amiss in the 111 * Macro specification 112 */ 113 rsvc.getLog().error("Parser Error: " + templateName, mee); 114 throw mee; 115 } 116 catch (ParseException pe) 117 { 118 rsvc.getLog().error("Parser Exception: " + templateName, pe); 119 throw new TemplateParseException (pe.currentToken, 120 pe.expectedTokenSequences, pe.tokenImage, currentTemplateName); 121 } 122 catch (TokenMgrError tme) 123 { 124 throw new ParseException("Lexical error: " + tme.toString()); 125 } 126 catch (Exception e) 127 { 128 String msg = "Parser Error: " + templateName; 129 rsvc.getLog().error(msg, e); 130 throw new VelocityException(msg, e); 131 } 132 133 currentTemplateName = ""; 134 135 return sn; 136 } 137 138 /** 139 * This method sets the directives Hashtable 140 */ 141 public void setDirectives(Hashtable directives) 142 { 143 this.directives = directives; 144 } 145 146 /** 147 * This method gets a Directive from the directives Hashtable 148 */ 149 public Directive getDirective(String directive) 150 { 151 return (Directive) directives.get(directive); 152 } 153 154 /** 155 * This method finds out of the directive exists in the directives 156 * Hashtable. 157 */ 158 public boolean isDirective(String directive) 159 { 160 return directives.containsKey(directive); 161 } 162 163 164 /** 165 * Produces a processed output for an escaped control or 166 * pluggable directive 167 */ 168 private String escapedDirective( String strImage ) 169 { 170 int iLast = strImage.lastIndexOf("\\"); 171 172 String strDirective = strImage.substring(iLast + 1); 173 174 boolean bRecognizedDirective = false; 175 176 // we don't have to call substring method all the time in this method 177 String dirTag = strDirective.substring(1); 178 if (dirTag.charAt(0) == '{') 179 { 180 dirTag = dirTag.substring(1, dirTag.length() - 1); 181 } 182 183 /* 184 * is this a PD or a control directive? 185 */ 186 187 if ( isDirective(dirTag) ) 188 { 189 bRecognizedDirective = true; 190 } 191 else if ( rsvc.isVelocimacro(dirTag, currentTemplateName)) 192 { 193 bRecognizedDirective = true; 194 } 195 else 196 { 197 /* order for speed? */ 198 199 if ( dirTag.equals("if") 200 || dirTag.equals("end") 201 || dirTag.equals("set") 202 || dirTag.equals("else") 203 || dirTag.equals("elseif") 204 || dirTag.equals("stop") 205 ) 206 { 207 bRecognizedDirective = true; 208 } 209 } 210 211 /* 212 * if so, make the proper prefix string (let the escapes do their thing..) 213 * otherwise, just return what it is.. 214 */ 215 216 if (bRecognizedDirective) 217 return ( strImage.substring(0,iLast/2) + strDirective); 218 else 219 return ( strImage ); 220 } 221 222 /** 223 * Check whether there is a left parenthesis with leading optional 224 * whitespaces. This method is used in the semantic look ahead of 225 * Directive method. This is done in code instead of as a production 226 * for simplicity and efficiency. 227 */ 228 private boolean isLeftParenthesis() 229 { 230 char c; 231 int no = 0; 232 try { 233 while(true) 234 { 235 /** 236 * Read a character 237 */ 238 c = velcharstream.readChar(); 239 no++; 240 if (c == '(') 241 { 242 return true; 243 } 244 /** 245 * if not a white space return 246 */ 247 else if (c != ' ' && c != '\n' && c != '\r' && c != '\t') 248 { 249 return false; 250 } 251 } 252 } 253 catch (IOException e) 254 { 255 } 256 finally 257 { 258 /** 259 * Backup the stream to the initial state 260 */ 261 velcharstream.backup(no); 262 } 263 return false; 264 } 265 266 /** 267 * This method is what starts the whole parsing 268 * process. After the parsing is complete and 269 * the template has been turned into an AST, 270 * this method returns the root of AST which 271 * can subsequently be traversed by a visitor 272 * which implements the ParserVisitor interface 273 * which is generated automatically by JavaCC 274 */ 275 final public SimpleNode process() throws ParseException { 276 /*@bgen(jjtree) process */ 277 ASTprocess jjtn000 = new ASTprocess(this, JJTPROCESS); 278 boolean jjtc000 = true; 279 jjtree.openNodeScope(jjtn000); 280 try { 281 label_1: 282 while (true) { 283 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { 284 case LPAREN: 285 case RPAREN: 286 case ESCAPE_DIRECTIVE: 287 case SET_DIRECTIVE: 288 case SINGLE_LINE_COMMENT_START: 289 case DOUBLE_ESCAPE: 290 case ESCAPE: 291 case TEXT: 292 case FORMAL_COMMENT: 293 case MULTI_LINE_COMMENT: 294 case STRING_LITERAL: 295 case IF_DIRECTIVE: 296 case STOP_DIRECTIVE: 297 case INTEGER_LITERAL: 298 case FLOATING_POINT_LITERAL: 299 case WORD: 300 case BRACKETED_WORD: 301 case IDENTIFIER: 302 case DOT: 303 case LCURLY: 304 case RCURLY: 305 ; 306 break; 307 default: 308 jj_la1[0] = jj_gen; 309 break label_1; 310 } 311 Statement(); 312 } 313 jj_consume_token(0); 314 jjtree.closeNodeScope(jjtn000, true); 315 jjtc000 = false; 316 {if (true) return jjtn000;} 317 } catch (Throwable jjte000) { 318 if (jjtc000) { 319 jjtree.clearNodeScope(jjtn000); 320 jjtc000 = false; 321 } else { 322 jjtree.popNode(); 323 } 324 if (jjte000 instanceof RuntimeException) { 325 {if (true) throw (RuntimeException)jjte000;} 326 } 327 if (jjte000 instanceof ParseException) { 328 {if (true) throw (ParseException)jjte000;} 329 } 330 {if (true) throw (Error)jjte000;} 331 } finally { 332 if (jjtc000) { 333 jjtree.closeNodeScope(jjtn000, true); 334 } 335 } 336 throw new Error("Missing return statement in function"); 337 } 338 339 /** 340 * These are the types of statements that 341 * are acceptable in Velocity templates. 342 */ 343 final public void Statement() throws ParseException { 344 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { 345 case IF_DIRECTIVE: 346 IfStatement(); 347 break; 348 case STOP_DIRECTIVE: 349 StopStatement(); 350 break; 351 default: 352 jj_la1[1] = jj_gen; 353 if (jj_2_1(2)) { 354 Reference(); 355 } else { 356 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { 357 case SINGLE_LINE_COMMENT_START: 358 case FORMAL_COMMENT: 359 case MULTI_LINE_COMMENT: 360 Comment(); 361 break; 362 case SET_DIRECTIVE: 363 SetDirective(); 364 break; 365 case ESCAPE_DIRECTIVE: 366 EscapedDirective(); 367 break; 368 case DOUBLE_ESCAPE: 369 Escape(); 370 break; 371 case WORD: 372 case BRACKETED_WORD: 373 Directive(); 374 break; 375 case LPAREN: 376 case RPAREN: 377 case ESCAPE: 378 case TEXT: 379 case STRING_LITERAL: 380 case INTEGER_LITERAL: 381 case FLOATING_POINT_LITERAL: 382 case DOT: 383 case LCURLY: 384 case RCURLY: 385 Text(); 386 break; 387 default: 388 jj_la1[2] = jj_gen; 389 jj_consume_token(-1); 390 throw new ParseException(); 391 } 392 } 393 } 394 } 395 396 /** 397 * used to separate the notion of a valid directive that has been 398 * escaped, versus something that looks like a directive and 399 * is just schmoo. This is important to do as a separate production 400 * that creates a node, because we want this, in either case, to stop 401 * the further parsing of the Directive() tree. 402 */ 403 final public void EscapedDirective() throws ParseException { 404 /*@bgen(jjtree) EscapedDirective */ 405 ASTEscapedDirective jjtn000 = new ASTEscapedDirective(this, JJTESCAPEDDIRECTIVE); 406 boolean jjtc000 = true; 407 jjtree.openNodeScope(jjtn000); 408 try { 409 Token t = null; 410 t = jj_consume_token(ESCAPE_DIRECTIVE); 411 jjtree.closeNodeScope(jjtn000, true); 412 jjtc000 = false; 413 /* 414 * churn and burn.. 415 */ 416 t.image = escapedDirective( t.image ); 417 } finally { 418 if (jjtc000) { 419 jjtree.closeNodeScope(jjtn000, true); 420 } 421 } 422 } 423 424 /** 425 * Used to catch and process escape sequences in grammatical constructs 426 * as escapes outside of VTL are just characters. Right now we have both 427 * this and the EscapeDirective() construction because in the EscapeDirective() 428 * case, we want to suck in the #<directive> and here we don't. We just want 429 * the escapes to render correctly 430 */ 431 final public void Escape() throws ParseException { 432 /*@bgen(jjtree) Escape */ 433 ASTEscape jjtn000 = new ASTEscape(this, JJTESCAPE); 434 boolean jjtc000 = true; 435 jjtree.openNodeScope(jjtn000); 436 try { 437 Token t = null; 438 int count = 0; 439 boolean control = false; 440 label_2: 441 while (true) { 442 t = jj_consume_token(DOUBLE_ESCAPE); 443 count++; 444 if (jj_2_2(2)) { 445 ; 446 } else { 447 break label_2; 448 } 449 } 450 jjtree.closeNodeScope(jjtn000, true); 451 jjtc000 = false; 452 /* 453 * first, check to see if we have a control directive 454 */ 455 switch(t.next.kind ) { 456 case IF_DIRECTIVE : 457 case ELSE_DIRECTIVE : 458 case ELSEIF_DIRECTIVE : 459 case END : 460 case STOP_DIRECTIVE : 461 control = true; 462 break; 463 } 464 465 /* 466 * if that failed, lets lookahead to see if we matched a PD or a VM 467 */ 468 String nTag = t.next.image.substring(1); 469 470 if ( isDirective(nTag) ) 471 control = true; 472 else if ( rsvc.isVelocimacro(nTag, currentTemplateName)) 473 control = true; 474 475 jjtn000.val = ""; 476 477 for( int i = 0; i < count; i++) 478 jjtn000.val += ( control ? "\\" : "\\\\"); 479 } finally { 480 if (jjtc000) { 481 jjtree.closeNodeScope(jjtn000, true); 482 } 483 } 484 } 485 486 final public void Comment() throws ParseException { 487 /*@bgen(jjtree) Comment */ 488 ASTComment jjtn000 = new ASTComment(this, JJTCOMMENT); 489 boolean jjtc000 = true; 490 jjtree.openNodeScope(jjtn000); 491 try { 492 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { 493 case SINGLE_LINE_COMMENT_START: 494 jj_consume_token(SINGLE_LINE_COMMENT_START); 495 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { 496 case SINGLE_LINE_COMMENT: 497 jj_consume_token(SINGLE_LINE_COMMENT); 498 break; 499 default: 500 jj_la1[3] = jj_gen; 501 ; 502 } 503 break; 504 case MULTI_LINE_COMMENT: 505 jj_consume_token(MULTI_LINE_COMMENT); 506 break; 507 case FORMAL_COMMENT: 508 jj_consume_token(FORMAL_COMMENT); 509 break; 510 default: 511 jj_la1[4] = jj_gen; 512 jj_consume_token(-1); 513 throw new ParseException(); 514 } 515 } finally { 516 if (jjtc000) { 517 jjtree.closeNodeScope(jjtn000, true); 518 } 519 } 520 } 521 522 final public void FloatingPointLiteral() throws ParseException { 523 /*@bgen(jjtree) FloatingPointLiteral */ 524 ASTFloatingPointLiteral jjtn000 = new ASTFloatingPointLiteral(this, JJTFLOATINGPOINTLITERAL); 525 boolean jjtc000 = true; 526 jjtree.openNodeScope(jjtn000); 527 try { 528 jj_consume_token(FLOATING_POINT_LITERAL); 529 } finally { 530 if (jjtc000) { 531 jjtree.closeNodeScope(jjtn000, true); 532 } 533 } 534 } 535 536 final public void IntegerLiteral() throws ParseException { 537 /*@bgen(jjtree) IntegerLiteral */ 538 ASTIntegerLiteral jjtn000 = new ASTIntegerLiteral(this, JJTINTEGERLITERAL); 539 boolean jjtc000 = true; 540 jjtree.openNodeScope(jjtn000); 541 try { 542 jj_consume_token(INTEGER_LITERAL); 543 } finally { 544 if (jjtc000) { 545 jjtree.closeNodeScope(jjtn000, true); 546 } 547 } 548 } 549 550 final public void StringLiteral() throws ParseException { 551 /*@bgen(jjtree) StringLiteral */ 552 ASTStringLiteral jjtn000 = new ASTStringLiteral(this, JJTSTRINGLITERAL); 553 boolean jjtc000 = true; 554 jjtree.openNodeScope(jjtn000); 555 try { 556 jj_consume_token(STRING_LITERAL); 557 } finally { 558 if (jjtc000) { 559 jjtree.closeNodeScope(jjtn000, true); 560 } 561 } 562 } 563 564 /** 565 * This method corresponds to variable 566 * references in Velocity templates. 567 * The following are examples of variable 568 * references that may be found in a 569 * template: 570 * 571 * $foo 572 * $bar 573 * 574 */ 575 final public void Identifier() throws ParseException { 576 /*@bgen(jjtree) Identifier */ 577 ASTIdentifier jjtn000 = new ASTIdentifier(this, JJTIDENTIFIER); 578 boolean jjtc000 = true; 579 jjtree.openNodeScope(jjtn000); 580 try { 581 jj_consume_token(IDENTIFIER); 582 } finally { 583 if (jjtc000) { 584 jjtree.closeNodeScope(jjtn000, true); 585 } 586 } 587 } 588 589 final public void Word() throws ParseException { 590 /*@bgen(jjtree) Word */ 591 ASTWord jjtn000 = new ASTWord(this, JJTWORD); 592 boolean jjtc000 = true; 593 jjtree.openNodeScope(jjtn000); 594 try { 595 jj_consume_token(WORD); 596 } finally { 597 if (jjtc000) { 598 jjtree.closeNodeScope(jjtn000, true); 599 } 600 } 601 } 602 603 /** 604 * Supports the arguments for the Pluggable Directives 605 */ 606 final public int DirectiveArg() throws ParseException { 607 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { 608 case IDENTIFIER: 609 case LCURLY: 610 Reference(); 611 {if (true) return ParserTreeConstants.JJTREFERENCE;} 612 break; 613 case WORD: 614 Word(); 615 {if (true) return ParserTreeConstants.JJTWORD;} 616 break; 617 case STRING_LITERAL: 618 StringLiteral(); 619 {if (true) return ParserTreeConstants.JJTSTRINGLITERAL;} 620 break; 621 case INTEGER_LITERAL: 622 IntegerLiteral(); 623 {if (true) return ParserTreeConstants.JJTINTEGERLITERAL;} 624 break; 625 default: 626 jj_la1[5] = jj_gen; 627 if (jj_2_3(2147483647)) { 628 IntegerRange(); 629 {if (true) return ParserTreeConstants.JJTINTEGERRANGE;} 630 } else { 631 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { 632 case FLOATING_POINT_LITERAL: 633 FloatingPointLiteral(); 634 {if (true) return ParserTreeConstants.JJTFLOATINGPOINTLITERAL;} 635 break; 636 case LEFT_CURLEY: 637 Map(); 638 {if (true) return ParserTreeConstants.JJTMAP;} 639 break; 640 case LBRACKET: 641 ObjectArray(); 642 {if (true) return ParserTreeConstants.JJTOBJECTARRAY;} 643 break; 644 case TRUE: 645 True(); 646 {if (true) return ParserTreeConstants.JJTTRUE;} 647 break; 648 case FALSE: 649 False(); 650 {if (true) return ParserTreeConstants.JJTFALSE;} 651 break; 652 default: 653 jj_la1[6] = jj_gen; 654 jj_consume_token(-1); 655 throw new ParseException(); 656 } 657 } 658 } 659 throw new Error("Missing return statement in function"); 660 } 661 662 /** 663 * Supports the Pluggable Directives 664 * #foo( arg+ ) 665 */ 666 final public SimpleNode Directive() throws ParseException { 667 /*@bgen(jjtree) Directive */ 668 ASTDirective jjtn000 = new ASTDirective(this, JJTDIRECTIVE); 669 boolean jjtc000 = true; 670 jjtree.openNodeScope(jjtn000);Token t = null; 671 int argType; 672 int argPos = 0; 673 Directive d; 674 int directiveType; 675 boolean isVM = false; 676 boolean doItNow = false; 677 try { 678 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { 679 case WORD: 680 t = jj_consume_token(WORD); 681 break; 682 case BRACKETED_WORD: 683 t = jj_consume_token(BRACKETED_WORD); 684 break; 685 default: 686 jj_la1[7] = jj_gen; 687 jj_consume_token(-1); 688 throw new ParseException(); 689 } 690 String directiveName; 691 if (t.kind == ParserConstants.BRACKETED_WORD) 692 { 693 directiveName = t.image.substring(2, t.image.length() - 1); 694 } 695 else 696 { 697 directiveName = t.image.substring(1); 698 } 699 700 d = (Directive) directives.get(directiveName); 701 702 /* 703 * Velocimacro support : if the directive is macro directive 704 * then set the flag so after the block parsing, we add the VM 705 * right then. (So available if used w/in the current template ) 706 */ 707 708 if (directiveName.equals("macro")) 709 { 710 doItNow = true; 711 } 712 713 /* 714 * set the directive name from here. No reason for the thing to know 715 * about parser tokens 716 */ 717 718 jjtn000.setDirectiveName(directiveName); 719 720 if ( d == null) 721 { 722 /* 723 * if null, then not a real directive, but maybe a Velocimacro 724 */ 725 726 isVM = rsvc.isVelocimacro(directiveName, currentTemplateName); 727 728 /* 729 * Currently, all VMs are LINE directives 730 */ 731 732 directiveType = Directive.LINE; 733 } 734 else 735 { 736 directiveType = d.getType(); 737 } 738 739 /* 740 * now, switch us out of PRE_DIRECTIVE 741 */ 742 743 token_source.SwitchTo(DIRECTIVE); 744 745 argPos = 0; 746 if (isLeftParenthesis()) { 747 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { 748 case WHITESPACE: 749 jj_consume_token(WHITESPACE); 750 break; 751 default: 752 jj_la1[8] = jj_gen; 753 ; 754 } 755 jj_consume_token(LPAREN); 756 label_3: 757 while (true) { 758 if (jj_2_4(2)) { 759 ; 760 } else { 761 break label_3; 762 } 763 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { 764 case WHITESPACE: 765 jj_consume_token(WHITESPACE); 766 break; 767 default: 768 jj_la1[9] = jj_gen; 769 ; 770 } 771 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { 772 case COMMA: 773 jj_consume_token(COMMA); 774 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { 775 case WHITESPACE: 776 jj_consume_token(WHITESPACE); 777 break; 778 default: 779 jj_la1[10] = jj_gen; 780 ; 781 } 782 break; 783 default: 784 jj_la1[11] = jj_gen; 785 ; 786 } 787 argType = DirectiveArg(); 788 if (argType == ParserTreeConstants.JJTWORD) 789 { 790 if (doItNow && argPos == 0) 791 { 792 /* if #macro and it's the 0th arg, ok */ 793 } 794 else if (isVM) 795 { 796 {if (true) throw new MacroParseException("Invalid arg #" 797 + argPos + " in VM " + t.image, currentTemplateName, t);} 798 } 799 /* if #foreach and it's the 2nd arg, ok */ 800 else if (d != null && (!directiveName.equals("foreach") || argPos != 1)) 801 { 802 {if (true) throw new MacroParseException("Invalid arg #" 803 + argPos + " in directive " + t.image, currentTemplateName, t);} 804 } 805 else 806 { 807 /* either schmoo or a late-defined macro, 808 * VelocimacroProxy will have to check for latter. */ 809 } 810 } 811 else 812 { 813 if (doItNow && argPos == 0) 814 { 815 /* if a VM and it's the 0th arg, not ok */ 816 817 {if (true) throw new MacroParseException("Invalid first arg" 818 + " in #macro() directive - must be a" 819 + " word token (no \' or \" surrounding)", currentTemplateName, t);} 820 } 821 } 822 823 argPos++; 824 } 825 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { 826 case WHITESPACE: 827 jj_consume_token(WHITESPACE); 828 break; 829 default: 830 jj_la1[12] = jj_gen; 831 ; 832 } 833 jj_consume_token(RPAREN); 834 if (directiveType == Directive.LINE) 835 { 836 {if (true) return jjtn000;} 837 } 838 } else { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - src/content/engine/1.6/xref/org/apache/velocity/runtime/parser/Parser.html [37:848]: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 27 */ 28 public class Parser/*@bgen(jjtree)*/implements ParserTreeConstants, ParserConstants {/*@bgen(jjtree)*/ 29 protected JJTParserState jjtree = new JJTParserState();/** 30 * This Hashtable contains a list of all of the dynamic directives. 31 */ 32 private Hashtable directives = new Hashtable(0); 33 34 /** 35 * Name of current template we are parsing. Passed to us in parse() 36 */ 37 public String currentTemplateName = ""; 38 39 VelocityCharStream velcharstream = null; 40 41 private RuntimeServices rsvc = null; 42 43 /** 44 * This constructor was added to allow the re-use of parsers. 45 * The normal constructor takes a single argument which 46 * an InputStream. This simply creates a re-usable parser 47 * object, we satisfy the requirement of an InputStream 48 * by using a newline character as an input stream. 49 */ 50 public Parser( RuntimeServices rs) 51 { 52 /* 53 * need to call the CTOR first thing. 54 */ 55 56 this( new VelocityCharStream( 57 new ByteArrayInputStream("\n".getBytes()), 1, 1 )); 58 59 /* 60 * now setup a VCS for later use 61 */ 62 velcharstream = new VelocityCharStream( 63 new ByteArrayInputStream("\n".getBytes()), 1, 1 ); 64 65 /* 66 * and save the RuntimeServices 67 */ 68 rsvc = rs; 69 } 70 71 /** 72 * This was also added to allow parsers to be 73 * re-usable. Normal JavaCC use entails passing an 74 * input stream to the constructor and the parsing 75 * process is carried out once. We want to be able 76 * to re-use parsers: we do this by adding this 77 * method and re-initializing the lexer with 78 * the new stream that we want parsed. 79 */ 80 public SimpleNode parse( Reader reader, String templateName ) 81 throws ParseException 82 { 83 SimpleNode sn = null; 84 85 currentTemplateName = templateName; 86 87 try 88 { 89 token_source.clearStateVars(); 90 91 /* 92 * reinitialize the VelocityCharStream 93 * with the new reader 94 */ 95 velcharstream.ReInit( reader, 1, 1 ); 96 97 /* 98 * now reinit the Parser with this CharStream 99 */ 100 ReInit( velcharstream ); 101 102 /* 103 * do that voodoo... 104 */ 105 sn = process(); 106 } 107 catch (MacroParseException mee) 108 { 109 /* 110 * thrown by the Macro class when something is amiss in the 111 * Macro specification 112 */ 113 rsvc.getLog().error("Parser Error: " + templateName, mee); 114 throw mee; 115 } 116 catch (ParseException pe) 117 { 118 rsvc.getLog().error("Parser Exception: " + templateName, pe); 119 throw new TemplateParseException (pe.currentToken, 120 pe.expectedTokenSequences, pe.tokenImage, currentTemplateName); 121 } 122 catch (TokenMgrError tme) 123 { 124 throw new ParseException("Lexical error: " + tme.toString()); 125 } 126 catch (Exception e) 127 { 128 String msg = "Parser Error: " + templateName; 129 rsvc.getLog().error(msg, e); 130 throw new VelocityException(msg, e); 131 } 132 133 currentTemplateName = ""; 134 135 return sn; 136 } 137 138 /** 139 * This method sets the directives Hashtable 140 */ 141 public void setDirectives(Hashtable directives) 142 { 143 this.directives = directives; 144 } 145 146 /** 147 * This method gets a Directive from the directives Hashtable 148 */ 149 public Directive getDirective(String directive) 150 { 151 return (Directive) directives.get(directive); 152 } 153 154 /** 155 * This method finds out of the directive exists in the directives 156 * Hashtable. 157 */ 158 public boolean isDirective(String directive) 159 { 160 return directives.containsKey(directive); 161 } 162 163 164 /** 165 * Produces a processed output for an escaped control or 166 * pluggable directive 167 */ 168 private String escapedDirective( String strImage ) 169 { 170 int iLast = strImage.lastIndexOf("\\"); 171 172 String strDirective = strImage.substring(iLast + 1); 173 174 boolean bRecognizedDirective = false; 175 176 // we don't have to call substring method all the time in this method 177 String dirTag = strDirective.substring(1); 178 if (dirTag.charAt(0) == '{') 179 { 180 dirTag = dirTag.substring(1, dirTag.length() - 1); 181 } 182 183 /* 184 * is this a PD or a control directive? 185 */ 186 187 if ( isDirective(dirTag) ) 188 { 189 bRecognizedDirective = true; 190 } 191 else if ( rsvc.isVelocimacro(dirTag, currentTemplateName)) 192 { 193 bRecognizedDirective = true; 194 } 195 else 196 { 197 /* order for speed? */ 198 199 if ( dirTag.equals("if") 200 || dirTag.equals("end") 201 || dirTag.equals("set") 202 || dirTag.equals("else") 203 || dirTag.equals("elseif") 204 || dirTag.equals("stop") 205 ) 206 { 207 bRecognizedDirective = true; 208 } 209 } 210 211 /* 212 * if so, make the proper prefix string (let the escapes do their thing..) 213 * otherwise, just return what it is.. 214 */ 215 216 if (bRecognizedDirective) 217 return ( strImage.substring(0,iLast/2) + strDirective); 218 else 219 return ( strImage ); 220 } 221 222 /** 223 * Check whether there is a left parenthesis with leading optional 224 * whitespaces. This method is used in the semantic look ahead of 225 * Directive method. This is done in code instead of as a production 226 * for simplicity and efficiency. 227 */ 228 private boolean isLeftParenthesis() 229 { 230 char c; 231 int no = 0; 232 try { 233 while(true) 234 { 235 /** 236 * Read a character 237 */ 238 c = velcharstream.readChar(); 239 no++; 240 if (c == '(') 241 { 242 return true; 243 } 244 /** 245 * if not a white space return 246 */ 247 else if (c != ' ' && c != '\n' && c != '\r' && c != '\t') 248 { 249 return false; 250 } 251 } 252 } 253 catch (IOException e) 254 { 255 } 256 finally 257 { 258 /** 259 * Backup the stream to the initial state 260 */ 261 velcharstream.backup(no); 262 } 263 return false; 264 } 265 266 /** 267 * This method is what starts the whole parsing 268 * process. After the parsing is complete and 269 * the template has been turned into an AST, 270 * this method returns the root of AST which 271 * can subsequently be traversed by a visitor 272 * which implements the ParserVisitor interface 273 * which is generated automatically by JavaCC 274 */ 275 final public SimpleNode process() throws ParseException { 276 /*@bgen(jjtree) process */ 277 ASTprocess jjtn000 = new ASTprocess(this, JJTPROCESS); 278 boolean jjtc000 = true; 279 jjtree.openNodeScope(jjtn000); 280 try { 281 label_1: 282 while (true) { 283 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { 284 case LPAREN: 285 case RPAREN: 286 case ESCAPE_DIRECTIVE: 287 case SET_DIRECTIVE: 288 case SINGLE_LINE_COMMENT_START: 289 case DOUBLE_ESCAPE: 290 case ESCAPE: 291 case TEXT: 292 case FORMAL_COMMENT: 293 case MULTI_LINE_COMMENT: 294 case STRING_LITERAL: 295 case IF_DIRECTIVE: 296 case STOP_DIRECTIVE: 297 case INTEGER_LITERAL: 298 case FLOATING_POINT_LITERAL: 299 case WORD: 300 case BRACKETED_WORD: 301 case IDENTIFIER: 302 case DOT: 303 case LCURLY: 304 case RCURLY: 305 ; 306 break; 307 default: 308 jj_la1[0] = jj_gen; 309 break label_1; 310 } 311 Statement(); 312 } 313 jj_consume_token(0); 314 jjtree.closeNodeScope(jjtn000, true); 315 jjtc000 = false; 316 {if (true) return jjtn000;} 317 } catch (Throwable jjte000) { 318 if (jjtc000) { 319 jjtree.clearNodeScope(jjtn000); 320 jjtc000 = false; 321 } else { 322 jjtree.popNode(); 323 } 324 if (jjte000 instanceof RuntimeException) { 325 {if (true) throw (RuntimeException)jjte000;} 326 } 327 if (jjte000 instanceof ParseException) { 328 {if (true) throw (ParseException)jjte000;} 329 } 330 {if (true) throw (Error)jjte000;} 331 } finally { 332 if (jjtc000) { 333 jjtree.closeNodeScope(jjtn000, true); 334 } 335 } 336 throw new Error("Missing return statement in function"); 337 } 338 339 /** 340 * These are the types of statements that 341 * are acceptable in Velocity templates. 342 */ 343 final public void Statement() throws ParseException { 344 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { 345 case IF_DIRECTIVE: 346 IfStatement(); 347 break; 348 case STOP_DIRECTIVE: 349 StopStatement(); 350 break; 351 default: 352 jj_la1[1] = jj_gen; 353 if (jj_2_1(2)) { 354 Reference(); 355 } else { 356 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { 357 case SINGLE_LINE_COMMENT_START: 358 case FORMAL_COMMENT: 359 case MULTI_LINE_COMMENT: 360 Comment(); 361 break; 362 case SET_DIRECTIVE: 363 SetDirective(); 364 break; 365 case ESCAPE_DIRECTIVE: 366 EscapedDirective(); 367 break; 368 case DOUBLE_ESCAPE: 369 Escape(); 370 break; 371 case WORD: 372 case BRACKETED_WORD: 373 Directive(); 374 break; 375 case LPAREN: 376 case RPAREN: 377 case ESCAPE: 378 case TEXT: 379 case STRING_LITERAL: 380 case INTEGER_LITERAL: 381 case FLOATING_POINT_LITERAL: 382 case DOT: 383 case LCURLY: 384 case RCURLY: 385 Text(); 386 break; 387 default: 388 jj_la1[2] = jj_gen; 389 jj_consume_token(-1); 390 throw new ParseException(); 391 } 392 } 393 } 394 } 395 396 /** 397 * used to separate the notion of a valid directive that has been 398 * escaped, versus something that looks like a directive and 399 * is just schmoo. This is important to do as a separate production 400 * that creates a node, because we want this, in either case, to stop 401 * the further parsing of the Directive() tree. 402 */ 403 final public void EscapedDirective() throws ParseException { 404 /*@bgen(jjtree) EscapedDirective */ 405 ASTEscapedDirective jjtn000 = new ASTEscapedDirective(this, JJTESCAPEDDIRECTIVE); 406 boolean jjtc000 = true; 407 jjtree.openNodeScope(jjtn000); 408 try { 409 Token t = null; 410 t = jj_consume_token(ESCAPE_DIRECTIVE); 411 jjtree.closeNodeScope(jjtn000, true); 412 jjtc000 = false; 413 /* 414 * churn and burn.. 415 */ 416 t.image = escapedDirective( t.image ); 417 } finally { 418 if (jjtc000) { 419 jjtree.closeNodeScope(jjtn000, true); 420 } 421 } 422 } 423 424 /** 425 * Used to catch and process escape sequences in grammatical constructs 426 * as escapes outside of VTL are just characters. Right now we have both 427 * this and the EscapeDirective() construction because in the EscapeDirective() 428 * case, we want to suck in the #<directive> and here we don't. We just want 429 * the escapes to render correctly 430 */ 431 final public void Escape() throws ParseException { 432 /*@bgen(jjtree) Escape */ 433 ASTEscape jjtn000 = new ASTEscape(this, JJTESCAPE); 434 boolean jjtc000 = true; 435 jjtree.openNodeScope(jjtn000); 436 try { 437 Token t = null; 438 int count = 0; 439 boolean control = false; 440 label_2: 441 while (true) { 442 t = jj_consume_token(DOUBLE_ESCAPE); 443 count++; 444 if (jj_2_2(2)) { 445 ; 446 } else { 447 break label_2; 448 } 449 } 450 jjtree.closeNodeScope(jjtn000, true); 451 jjtc000 = false; 452 /* 453 * first, check to see if we have a control directive 454 */ 455 switch(t.next.kind ) { 456 case IF_DIRECTIVE : 457 case ELSE_DIRECTIVE : 458 case ELSEIF_DIRECTIVE : 459 case END : 460 case STOP_DIRECTIVE : 461 control = true; 462 break; 463 } 464 465 /* 466 * if that failed, lets lookahead to see if we matched a PD or a VM 467 */ 468 String nTag = t.next.image.substring(1); 469 470 if ( isDirective(nTag) ) 471 control = true; 472 else if ( rsvc.isVelocimacro(nTag, currentTemplateName)) 473 control = true; 474 475 jjtn000.val = ""; 476 477 for( int i = 0; i < count; i++) 478 jjtn000.val += ( control ? "\\" : "\\\\"); 479 } finally { 480 if (jjtc000) { 481 jjtree.closeNodeScope(jjtn000, true); 482 } 483 } 484 } 485 486 final public void Comment() throws ParseException { 487 /*@bgen(jjtree) Comment */ 488 ASTComment jjtn000 = new ASTComment(this, JJTCOMMENT); 489 boolean jjtc000 = true; 490 jjtree.openNodeScope(jjtn000); 491 try { 492 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { 493 case SINGLE_LINE_COMMENT_START: 494 jj_consume_token(SINGLE_LINE_COMMENT_START); 495 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { 496 case SINGLE_LINE_COMMENT: 497 jj_consume_token(SINGLE_LINE_COMMENT); 498 break; 499 default: 500 jj_la1[3] = jj_gen; 501 ; 502 } 503 break; 504 case MULTI_LINE_COMMENT: 505 jj_consume_token(MULTI_LINE_COMMENT); 506 break; 507 case FORMAL_COMMENT: 508 jj_consume_token(FORMAL_COMMENT); 509 break; 510 default: 511 jj_la1[4] = jj_gen; 512 jj_consume_token(-1); 513 throw new ParseException(); 514 } 515 } finally { 516 if (jjtc000) { 517 jjtree.closeNodeScope(jjtn000, true); 518 } 519 } 520 } 521 522 final public void FloatingPointLiteral() throws ParseException { 523 /*@bgen(jjtree) FloatingPointLiteral */ 524 ASTFloatingPointLiteral jjtn000 = new ASTFloatingPointLiteral(this, JJTFLOATINGPOINTLITERAL); 525 boolean jjtc000 = true; 526 jjtree.openNodeScope(jjtn000); 527 try { 528 jj_consume_token(FLOATING_POINT_LITERAL); 529 } finally { 530 if (jjtc000) { 531 jjtree.closeNodeScope(jjtn000, true); 532 } 533 } 534 } 535 536 final public void IntegerLiteral() throws ParseException { 537 /*@bgen(jjtree) IntegerLiteral */ 538 ASTIntegerLiteral jjtn000 = new ASTIntegerLiteral(this, JJTINTEGERLITERAL); 539 boolean jjtc000 = true; 540 jjtree.openNodeScope(jjtn000); 541 try { 542 jj_consume_token(INTEGER_LITERAL); 543 } finally { 544 if (jjtc000) { 545 jjtree.closeNodeScope(jjtn000, true); 546 } 547 } 548 } 549 550 final public void StringLiteral() throws ParseException { 551 /*@bgen(jjtree) StringLiteral */ 552 ASTStringLiteral jjtn000 = new ASTStringLiteral(this, JJTSTRINGLITERAL); 553 boolean jjtc000 = true; 554 jjtree.openNodeScope(jjtn000); 555 try { 556 jj_consume_token(STRING_LITERAL); 557 } finally { 558 if (jjtc000) { 559 jjtree.closeNodeScope(jjtn000, true); 560 } 561 } 562 } 563 564 /** 565 * This method corresponds to variable 566 * references in Velocity templates. 567 * The following are examples of variable 568 * references that may be found in a 569 * template: 570 * 571 * $foo 572 * $bar 573 * 574 */ 575 final public void Identifier() throws ParseException { 576 /*@bgen(jjtree) Identifier */ 577 ASTIdentifier jjtn000 = new ASTIdentifier(this, JJTIDENTIFIER); 578 boolean jjtc000 = true; 579 jjtree.openNodeScope(jjtn000); 580 try { 581 jj_consume_token(IDENTIFIER); 582 } finally { 583 if (jjtc000) { 584 jjtree.closeNodeScope(jjtn000, true); 585 } 586 } 587 } 588 589 final public void Word() throws ParseException { 590 /*@bgen(jjtree) Word */ 591 ASTWord jjtn000 = new ASTWord(this, JJTWORD); 592 boolean jjtc000 = true; 593 jjtree.openNodeScope(jjtn000); 594 try { 595 jj_consume_token(WORD); 596 } finally { 597 if (jjtc000) { 598 jjtree.closeNodeScope(jjtn000, true); 599 } 600 } 601 } 602 603 /** 604 * Supports the arguments for the Pluggable Directives 605 */ 606 final public int DirectiveArg() throws ParseException { 607 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { 608 case IDENTIFIER: 609 case LCURLY: 610 Reference(); 611 {if (true) return ParserTreeConstants.JJTREFERENCE;} 612 break; 613 case WORD: 614 Word(); 615 {if (true) return ParserTreeConstants.JJTWORD;} 616 break; 617 case STRING_LITERAL: 618 StringLiteral(); 619 {if (true) return ParserTreeConstants.JJTSTRINGLITERAL;} 620 break; 621 case INTEGER_LITERAL: 622 IntegerLiteral(); 623 {if (true) return ParserTreeConstants.JJTINTEGERLITERAL;} 624 break; 625 default: 626 jj_la1[5] = jj_gen; 627 if (jj_2_3(2147483647)) { 628 IntegerRange(); 629 {if (true) return ParserTreeConstants.JJTINTEGERRANGE;} 630 } else { 631 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { 632 case FLOATING_POINT_LITERAL: 633 FloatingPointLiteral(); 634 {if (true) return ParserTreeConstants.JJTFLOATINGPOINTLITERAL;} 635 break; 636 case LEFT_CURLEY: 637 Map(); 638 {if (true) return ParserTreeConstants.JJTMAP;} 639 break; 640 case LBRACKET: 641 ObjectArray(); 642 {if (true) return ParserTreeConstants.JJTOBJECTARRAY;} 643 break; 644 case TRUE: 645 True(); 646 {if (true) return ParserTreeConstants.JJTTRUE;} 647 break; 648 case FALSE: 649 False(); 650 {if (true) return ParserTreeConstants.JJTFALSE;} 651 break; 652 default: 653 jj_la1[6] = jj_gen; 654 jj_consume_token(-1); 655 throw new ParseException(); 656 } 657 } 658 } 659 throw new Error("Missing return statement in function"); 660 } 661 662 /** 663 * Supports the Pluggable Directives 664 * #foo( arg+ ) 665 */ 666 final public SimpleNode Directive() throws ParseException { 667 /*@bgen(jjtree) Directive */ 668 ASTDirective jjtn000 = new ASTDirective(this, JJTDIRECTIVE); 669 boolean jjtc000 = true; 670 jjtree.openNodeScope(jjtn000);Token t = null; 671 int argType; 672 int argPos = 0; 673 Directive d; 674 int directiveType; 675 boolean isVM = false; 676 boolean doItNow = false; 677 try { 678 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { 679 case WORD: 680 t = jj_consume_token(WORD); 681 break; 682 case BRACKETED_WORD: 683 t = jj_consume_token(BRACKETED_WORD); 684 break; 685 default: 686 jj_la1[7] = jj_gen; 687 jj_consume_token(-1); 688 throw new ParseException(); 689 } 690 String directiveName; 691 if (t.kind == ParserConstants.BRACKETED_WORD) 692 { 693 directiveName = t.image.substring(2, t.image.length() - 1); 694 } 695 else 696 { 697 directiveName = t.image.substring(1); 698 } 699 700 d = (Directive) directives.get(directiveName); 701 702 /* 703 * Velocimacro support : if the directive is macro directive 704 * then set the flag so after the block parsing, we add the VM 705 * right then. (So available if used w/in the current template ) 706 */ 707 708 if (directiveName.equals("macro")) 709 { 710 doItNow = true; 711 } 712 713 /* 714 * set the directive name from here. No reason for the thing to know 715 * about parser tokens 716 */ 717 718 jjtn000.setDirectiveName(directiveName); 719 720 if ( d == null) 721 { 722 /* 723 * if null, then not a real directive, but maybe a Velocimacro 724 */ 725 726 isVM = rsvc.isVelocimacro(directiveName, currentTemplateName); 727 728 /* 729 * Currently, all VMs are LINE directives 730 */ 731 732 directiveType = Directive.LINE; 733 } 734 else 735 { 736 directiveType = d.getType(); 737 } 738 739 /* 740 * now, switch us out of PRE_DIRECTIVE 741 */ 742 743 token_source.SwitchTo(DIRECTIVE); 744 745 argPos = 0; 746 if (isLeftParenthesis()) { 747 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { 748 case WHITESPACE: 749 jj_consume_token(WHITESPACE); 750 break; 751 default: 752 jj_la1[8] = jj_gen; 753 ; 754 } 755 jj_consume_token(LPAREN); 756 label_3: 757 while (true) { 758 if (jj_2_4(2)) { 759 ; 760 } else { 761 break label_3; 762 } 763 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { 764 case WHITESPACE: 765 jj_consume_token(WHITESPACE); 766 break; 767 default: 768 jj_la1[9] = jj_gen; 769 ; 770 } 771 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { 772 case COMMA: 773 jj_consume_token(COMMA); 774 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { 775 case WHITESPACE: 776 jj_consume_token(WHITESPACE); 777 break; 778 default: 779 jj_la1[10] = jj_gen; 780 ; 781 } 782 break; 783 default: 784 jj_la1[11] = jj_gen; 785 ; 786 } 787 argType = DirectiveArg(); 788 if (argType == ParserTreeConstants.JJTWORD) 789 { 790 if (doItNow && argPos == 0) 791 { 792 /* if #macro and it's the 0th arg, ok */ 793 } 794 else if (isVM) 795 { 796 {if (true) throw new MacroParseException("Invalid arg #" 797 + argPos + " in VM " + t.image, currentTemplateName, t);} 798 } 799 /* if #foreach and it's the 2nd arg, ok */ 800 else if (d != null && (!directiveName.equals("foreach") || argPos != 1)) 801 { 802 {if (true) throw new MacroParseException("Invalid arg #" 803 + argPos + " in directive " + t.image, currentTemplateName, t);} 804 } 805 else 806 { 807 /* either schmoo or a late-defined macro, 808 * VelocimacroProxy will have to check for latter. */ 809 } 810 } 811 else 812 { 813 if (doItNow && argPos == 0) 814 { 815 /* if a VM and it's the 0th arg, not ok */ 816 817 {if (true) throw new MacroParseException("Invalid first arg" 818 + " in #macro() directive - must be a" 819 + " word token (no \' or \" surrounding)", currentTemplateName, t);} 820 } 821 } 822 823 argPos++; 824 } 825 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { 826 case WHITESPACE: 827 jj_consume_token(WHITESPACE); 828 break; 829 default: 830 jj_la1[12] = jj_gen; 831 ; 832 } 833 jj_consume_token(RPAREN); 834 if (directiveType == Directive.LINE) 835 { 836 {if (true) return jjtn000;} 837 } 838 } else { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -