1 package org.apache.velocity.runtime.parser.node; 2 3 /* 4 * Licensed to the Apache Software Foundation (ASF) under one 5 * or more contributor license agreements. See the NOTICE file 6 * distributed with this work for additional information 7 * regarding copyright ownership. The ASF licenses this file 8 * to you under the Apache License, Version 2.0 (the 9 * "License"); you may not use this file except in compliance 10 * with the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, 15 * software distributed under the License is distributed on an 16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 * KIND, either express or implied. See the License for the 18 * specific language governing permissions and limitations 19 * under the License. 20 */ 21 22 23 import java.io.IOException; 24 import java.io.OutputStreamWriter; 25 import java.io.PrintStream; 26 import java.io.PrintWriter; 27 import java.io.Writer; 28 import java.nio.charset.Charset; 29 30 import org.apache.velocity.Template; 31 import org.apache.velocity.context.InternalContextAdapter; 32 import org.apache.velocity.exception.MethodInvocationException; 33 import org.apache.velocity.exception.ParseErrorException; 34 import org.apache.velocity.exception.ResourceNotFoundException; 35 import org.apache.velocity.exception.TemplateInitException; 36 import org.apache.velocity.runtime.RuntimeConstants; 37 import org.apache.velocity.runtime.RuntimeServices; 38 import org.apache.velocity.runtime.parser.Parser; 39 import org.apache.velocity.runtime.parser.Token; 40 import org.apache.velocity.util.StringUtils; 41 42 import org.slf4j.Logger; 43 44 /** 45 * 46 */ 47 public class SimpleNode implements Node, Cloneable 48 { 49 /** */ 50 protected RuntimeServices rsvc = null; 51 52 /** */ 53 protected Logger log = null; 54 55 /** */ 56 protected Node parent; 57 58 /** */ 59 protected Node[] children; 60 61 /** */ 62 protected int id; 63 64 /** */ 65 protected Parser parser; 66 67 /** */ 68 protected int info; 69 70 /** */ 71 public boolean state; 72 73 /** */ 74 protected boolean invalid = false; 75 76 /** */ 77 protected Token first; 78 79 /** */ 80 protected Token last; 81 82 protected Template template; 83 84 /** 85 * For caching the literal value. 86 */ 87 protected String literal = null; 88 89 /** 90 * Line number for this Node in the vm source file. 91 */ 92 93 protected int line; 94 95 /** 96 * Column number for this Node in the vm source file. 97 */ 98 protected int column; 99 100 /** 101 * String image variable of the first Token element that was parsed and connected to this Node. 102 */ 103 protected String firstImage; 104 105 /** 106 * String image variable of the last Token element that was parsed and connected to this Node. 107 */ 108 protected String lastImage; 109 getRuntimeServices()110 public RuntimeServices getRuntimeServices() 111 { 112 return rsvc; 113 } 114 115 /** 116 * @param i 117 */ SimpleNode(int i)118 public SimpleNode(int i) 119 { 120 id = i; 121 } 122 123 /** 124 * @param p 125 * @param i 126 */ SimpleNode(Parser p, int i)127 public SimpleNode(Parser p, int i) 128 { 129 this(i); 130 parser = p; 131 template = parser.getCurrentTemplate(); 132 } 133 134 /** 135 * @see org.apache.velocity.runtime.parser.node.Node#jjtOpen() 136 */ 137 @Override jjtOpen()138 public void jjtOpen() 139 { 140 first = parser.getToken(1); // added 141 } 142 143 /** 144 * @see org.apache.velocity.runtime.parser.node.Node#jjtClose() 145 */ 146 @Override jjtClose()147 public void jjtClose() 148 { 149 last = parser.getToken(0); // added 150 } 151 152 /** 153 * @param t 154 */ setFirstToken(Token t)155 public void setFirstToken(Token t) 156 { 157 this.first = t; 158 } 159 160 /** 161 * @see org.apache.velocity.runtime.parser.node.Node#getFirstToken() 162 */ 163 @Override getFirstToken()164 public Token getFirstToken() 165 { 166 return first; 167 } 168 169 /** 170 * @see org.apache.velocity.runtime.parser.node.Node#getLastToken() 171 */ 172 @Override getLastToken()173 public Token getLastToken() 174 { 175 return last; 176 } 177 178 /** 179 * @see org.apache.velocity.runtime.parser.node.Node#jjtSetParent(org.apache.velocity.runtime.parser.node.Node) 180 */ 181 @Override jjtSetParent(Node n)182 public void jjtSetParent(Node n) 183 { 184 parent = n; 185 } 186 187 /** 188 * @see org.apache.velocity.runtime.parser.node.Node#jjtGetParent() 189 */ 190 @Override jjtGetParent()191 public Node jjtGetParent() 192 { 193 return parent; 194 } 195 196 /** 197 * @see org.apache.velocity.runtime.parser.node.Node#jjtAddChild(org.apache.velocity.runtime.parser.node.Node, int) 198 */ 199 @Override jjtAddChild(Node n, int i)200 public void jjtAddChild(Node n, int i) 201 { 202 if (children == null) 203 { 204 children = new Node[i + 1]; 205 } 206 else if (i >= children.length) 207 { 208 Node c[] = new Node[i + 1]; 209 System.arraycopy(children, 0, c, 0, children.length); 210 children = c; 211 } 212 children[i] = n; 213 } 214 215 /** 216 * @see org.apache.velocity.runtime.parser.node.Node#jjtGetChild(int) 217 */ 218 @Override jjtGetChild(int i)219 public Node jjtGetChild(int i) 220 { 221 return children[i]; 222 } 223 224 /** 225 * @see org.apache.velocity.runtime.parser.node.Node#jjtGetNumChildren() 226 */ 227 @Override jjtGetNumChildren()228 public int jjtGetNumChildren() 229 { 230 return (children == null) ? 0 : children.length; 231 } 232 233 234 /** 235 * @see org.apache.velocity.runtime.parser.node.Node#jjtAccept(org.apache.velocity.runtime.parser.node.ParserVisitor, java.lang.Object) 236 */ 237 @Override jjtAccept(ParserVisitor visitor, Object data)238 public Object jjtAccept(ParserVisitor visitor, Object data) 239 { 240 return visitor.visit(this, data); 241 } 242 243 244 /** 245 * @see org.apache.velocity.runtime.parser.node.Node#childrenAccept(org.apache.velocity.runtime.parser.node.ParserVisitor, java.lang.Object) 246 */ 247 @Override childrenAccept(ParserVisitor visitor, Object data)248 public Object childrenAccept(ParserVisitor visitor, Object data) 249 { 250 if (children != null) 251 { 252 for (Node aChildren : children) 253 { 254 aChildren.jjtAccept(visitor, data); 255 } 256 } 257 return data; 258 } 259 260 /* You can override these two methods in subclasses of SimpleNode to 261 customize the way the node appears when the tree is dumped. If 262 your output uses more than one line you should override 263 toString(String), otherwise overriding toString() is probably all 264 you need to do. */ 265 /** 266 * @param prefix display prefix 267 * @return String representation of this node. 268 */ toString(String prefix)269 public String toString(String prefix) 270 { 271 return prefix + "_" + toString(); 272 } 273 274 /** 275 * <p>Dumps nodes tree on System.out.</p> 276 * <p>Override {@link #dump(String, PrintWriter)} if you want to customize 277 * how the node dumps out its children. 278 * 279 * @param prefix 280 */ dump(String prefix)281 public final void dump(String prefix) 282 { 283 dump(prefix, System.out); 284 } 285 286 /** 287 * <p>Dumps nodes tree on System.out.</p> 288 * <p>Override {@link #dump(String, PrintWriter)} if you want to customize 289 * how the node dumps out its children. 290 * 291 * @param prefix display prefix 292 * @param out output print stream 293 */ dump(String prefix, PrintStream out)294 public final void dump(String prefix, PrintStream out) 295 { 296 Charset charset = null; 297 if (rsvc != null) /* may be null if node isn't yet initialized */ 298 { 299 String encoding = rsvc.getString(RuntimeConstants.INPUT_ENCODING); 300 try 301 { 302 charset = Charset.forName(encoding); 303 } 304 catch (Exception e) {} 305 } 306 if (charset == null) 307 { 308 charset = Charset.defaultCharset(); 309 } 310 PrintWriter pw = new PrintWriter(new OutputStreamWriter(out, charset)); 311 dump(prefix, pw); 312 pw.flush(); 313 } 314 315 /** 316 * <p>Dumps nodes tree on System.out.</p> 317 * <p>Override this method if you want to customize how the node dumps 318 * out its children.</p> 319 * 320 * @param prefix display prefix 321 * @param out output print writer 322 */ dump(String prefix, PrintWriter out)323 public void dump(String prefix, PrintWriter out) 324 { 325 out.println(toString()); 326 if (children != null) 327 { 328 for (int i = 0; i < children.length; ++i) 329 { 330 SimpleNode n = (SimpleNode) children[i]; 331 out.print(prefix + " |_"); 332 if (n != null) 333 { 334 n.dump(prefix + ( i == children.length - 1 ? " " : " | " ), out); 335 } 336 } 337 } 338 } 339 340 /** 341 * Return a string that tells the current location of this node. 342 * @param context 343 * @return location 344 */ getLocation(InternalContextAdapter context)345 protected String getLocation(InternalContextAdapter context) 346 { 347 return StringUtils.formatFileString(this); 348 } 349 350 // All additional methods 351 352 /** 353 * @see org.apache.velocity.runtime.parser.node.Node#literal() 354 */ 355 @Override literal()356 public String literal() 357 { 358 if( literal != null ) 359 { 360 return literal; 361 } 362 363 // if we have only one string, just return it and avoid 364 // buffer allocation. VELOCITY-606 365 if (first == last) 366 { 367 literal = NodeUtils.tokenLiteral(parser, first); 368 return literal; 369 } 370 371 Token t = first; 372 StringBuilder sb = new StringBuilder(NodeUtils.tokenLiteral(parser, t)); 373 while (t != last) 374 { 375 t = t.next; 376 sb.append(NodeUtils.tokenLiteral(parser, t)); 377 } 378 literal = sb.toString(); 379 return literal; 380 } 381 382 /** 383 * @throws TemplateInitException 384 * @see org.apache.velocity.runtime.parser.node.Node#init(org.apache.velocity.context.InternalContextAdapter, java.lang.Object) 385 */ 386 @Override init(InternalContextAdapter context, Object data)387 public Object init(InternalContextAdapter context, Object data) throws TemplateInitException 388 { 389 /* 390 * hold onto the RuntimeServices 391 */ 392 393 rsvc = (RuntimeServices) data; 394 log = rsvc.getLog("rendering"); 395 396 int i, k = jjtGetNumChildren(); 397 398 for (i = 0; i < k; i++) 399 { 400 jjtGetChild(i).init( context, data); 401 } 402 403 line = first.beginLine; 404 column = first.beginColumn; 405 406 return data; 407 } 408 409 /** 410 * @see org.apache.velocity.runtime.parser.node.Node#evaluate(org.apache.velocity.context.InternalContextAdapter) 411 */ 412 @Override evaluate(InternalContextAdapter context)413 public boolean evaluate(InternalContextAdapter context) 414 throws MethodInvocationException 415 { 416 return false; 417 } 418 419 /** 420 * @see org.apache.velocity.runtime.parser.node.Node#value(org.apache.velocity.context.InternalContextAdapter) 421 */ 422 @Override value(InternalContextAdapter context)423 public Object value(InternalContextAdapter context) 424 throws MethodInvocationException 425 { 426 return null; 427 } 428 429 /** 430 * @see org.apache.velocity.runtime.parser.node.Node#render(org.apache.velocity.context.InternalContextAdapter, java.io.Writer) 431 */ 432 @Override render(InternalContextAdapter context, Writer writer)433 public boolean render(InternalContextAdapter context, Writer writer) 434 throws IOException, MethodInvocationException, ParseErrorException, ResourceNotFoundException 435 { 436 int i, k = jjtGetNumChildren(); 437 438 for (i = 0; i < k; i++) 439 jjtGetChild(i).render(context, writer); 440 441 return true; 442 } 443 444 /** 445 * @see org.apache.velocity.runtime.parser.node.Node#execute(java.lang.Object, org.apache.velocity.context.InternalContextAdapter) 446 */ 447 @Override execute(Object o, InternalContextAdapter context)448 public Object execute(Object o, InternalContextAdapter context) 449 throws MethodInvocationException 450 { 451 return null; 452 } 453 454 /** 455 * @see org.apache.velocity.runtime.parser.node.Node#getType() 456 */ 457 @Override getType()458 public int getType() 459 { 460 return id; 461 } 462 463 /** 464 * @see org.apache.velocity.runtime.parser.node.Node#setInfo(int) 465 */ 466 @Override setInfo(int info)467 public void setInfo(int info) 468 { 469 this.info = info; 470 } 471 472 /** 473 * @see org.apache.velocity.runtime.parser.node.Node#getInfo() 474 */ 475 @Override getInfo()476 public int getInfo() 477 { 478 return info; 479 } 480 481 /** 482 * @see org.apache.velocity.runtime.parser.node.Node#setInvalid() 483 */ 484 @Override setInvalid()485 public void setInvalid() 486 { 487 invalid = true; 488 } 489 490 /** 491 * @see org.apache.velocity.runtime.parser.node.Node#isInvalid() 492 */ 493 @Override isInvalid()494 public boolean isInvalid() 495 { 496 return invalid; 497 } 498 499 /** 500 * @see org.apache.velocity.runtime.parser.node.Node#getLine() 501 */ 502 @Override getLine()503 public int getLine() 504 { 505 return line; 506 } 507 508 /** 509 * @see org.apache.velocity.runtime.parser.node.Node#getColumn() 510 */ 511 @Override getColumn()512 public int getColumn() 513 { 514 return column; 515 } 516 517 /** 518 * @since 1.5 519 */ toString()520 public String toString() 521 { 522 StringBuilder tokens = new StringBuilder(); 523 524 for (Token t = getFirstToken(); t != null; ) 525 { 526 tokens.append("[").append(t.image.replace("\n", "\\n")).append("]"); 527 if (t.next != null) 528 { 529 if (t.equals(getLastToken())) 530 { 531 break; 532 } 533 else 534 { 535 tokens.append(", "); 536 } 537 } 538 t = t.next; 539 } 540 String tok = tokens.toString(); 541 if (tok.length() > 50) tok = tok.substring(0, 50) + "..."; 542 return getClass().getSimpleName() + " [id=" + id + ", info=" + info + ", invalid=" 543 + invalid 544 + ", tokens=" + tok + "]"; 545 } 546 547 @Override getTemplateName()548 public String getTemplateName() 549 { 550 return template.getName(); 551 } 552 553 /** 554 * Call before calling cleanupParserAndTokens() if you want to store image of 555 * the first and last token of this node. 556 */ saveTokenImages()557 public void saveTokenImages() 558 { 559 if( first != null ) 560 { 561 this.firstImage = first.image; 562 } 563 if( last != null ) 564 { 565 this.lastImage = last.image; 566 } 567 } 568 569 /** 570 * Removes references to Parser and Tokens since they are not needed anymore at this point. 571 * 572 * This allows us to save memory quite a bit. 573 */ cleanupParserAndTokens()574 public void cleanupParserAndTokens() 575 { 576 this.parser = null; 577 this.first = null; 578 this.last = null; 579 } 580 581 /** 582 * @return String image variable of the first Token element that was parsed and connected to this Node. 583 */ 584 @Override getFirstTokenImage()585 public String getFirstTokenImage() 586 { 587 return firstImage; 588 } 589 590 /** 591 * @return String image variable of the last Token element that was parsed and connected to this Node. 592 */ 593 @Override getLastTokenImage()594 public String getLastTokenImage() 595 { 596 return lastImage; 597 } 598 599 @Override getTemplate()600 public Template getTemplate() { return template; } 601 602 /** 603 * @return the parser which created this node 604 * @since 2.2 605 */ 606 @Override getParser()607 public Parser getParser() 608 { 609 return parser; 610 } 611 612 /** 613 * Root node deep cloning 614 * @param template owner template 615 * @return cloned node 616 * @throws CloneNotSupportedException 617 * @since 2.4 618 */ clone(Template template)619 public Node clone(Template template) throws CloneNotSupportedException 620 { 621 if (parent != null) { 622 throw new IllegalStateException("cannot clone a child node without knowing its parent"); 623 } 624 return clone(template, null); 625 } 626 627 /** 628 * Child node deep cloning 629 * @param template owner template 630 * @param parent parent node 631 * @return cloned node 632 * @throws CloneNotSupportedException 633 * @since 2.4 634 */ clone(Template template, Node parent)635 protected Node clone(Template template, Node parent) throws CloneNotSupportedException 636 { 637 SimpleNode clone = (SimpleNode)super.clone(); 638 clone.template = template; 639 clone.parent = parent; 640 if (children != null) 641 { 642 clone.children = new SimpleNode[children.length]; 643 for (int i = 0; i < children.length; ++i) 644 { 645 clone.children[i] = ((SimpleNode)children[i]).clone(template, clone); 646 } 647 } 648 return clone; 649 } 650 } 651