1 package org.apache.velocity.runtime.parser.node;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.IOException;
23 import java.io.Writer;
24 import java.lang.reflect.InvocationTargetException;
25
26 import org.apache.velocity.app.event.EventHandlerUtil;
27 import org.apache.velocity.context.Context;
28 import org.apache.velocity.context.InternalContextAdapter;
29 import org.apache.velocity.exception.MethodInvocationException;
30 import org.apache.velocity.exception.TemplateInitException;
31 import org.apache.velocity.runtime.RuntimeConstants;
32 import org.apache.velocity.runtime.parser.Parser;
33 import org.apache.velocity.runtime.parser.ParserVisitor;
34 import org.apache.velocity.runtime.parser.Token;
35 import org.apache.velocity.util.introspection.Info;
36 import org.apache.velocity.util.introspection.VelPropertySet;
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51 public class ASTReference extends SimpleNode
52 {
53
54 private static final int NORMAL_REFERENCE = 1;
55 private static final int FORMAL_REFERENCE = 2;
56 private static final int QUIET_REFERENCE = 3;
57 private static final int RUNT = 4;
58
59 private int referenceType;
60 private String nullString;
61 private String rootString;
62 private boolean escaped = false;
63 private boolean computableReference = true;
64 private boolean logOnNull = true;
65 private String escPrefix = "";
66 private String morePrefix = "";
67 private String identifier = "";
68
69 private String literal = null;
70
71 private int numChildren = 0;
72
73 protected Info uberInfo;
74
75
76
77
78 public ASTReference(int id)
79 {
80 super(id);
81 }
82
83
84
85
86
87 public ASTReference(Parser p, int id)
88 {
89 super(p, id);
90 }
91
92
93
94
95 public Object jjtAccept(ParserVisitor visitor, Object data)
96 {
97 return visitor.visit(this, data);
98 }
99
100
101
102
103 public Object init(InternalContextAdapter context, Object data)
104 throws TemplateInitException
105 {
106
107
108
109
110 super.init(context, data);
111
112
113
114
115
116
117
118 rootString = getRoot();
119
120 numChildren = jjtGetNumChildren();
121
122
123
124
125
126 if (numChildren > 0 )
127 {
128 identifier = jjtGetChild(numChildren - 1).getFirstToken().image;
129 }
130
131
132
133
134
135 uberInfo = new Info(context.getCurrentTemplateName(),
136 getLine(),getColumn());
137
138
139
140
141 logOnNull =
142 rsvc.getBoolean(RuntimeConstants.RUNTIME_LOG_REFERENCE_LOG_INVALID, true);
143
144 return data;
145 }
146
147
148
149
150
151 public String getRootString()
152 {
153 return rootString;
154 }
155
156
157
158
159
160
161
162
163
164 public Object execute(Object o, InternalContextAdapter context)
165 throws MethodInvocationException
166 {
167
168 if (referenceType == RUNT)
169 return null;
170
171
172
173
174
175 Object result = getVariableValue(context, rootString);
176
177 if (result == null)
178 {
179 return EventHandlerUtil.invalidGetMethod(rsvc, context,
180 "$" + rootString, null, null, uberInfo);
181 }
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196 try
197 {
198 Object previousResult = result;
199 int failedChild = -1;
200 for (int i = 0; i < numChildren; i++)
201 {
202 previousResult = result;
203 result = jjtGetChild(i).execute(result,context);
204 if (result == null)
205 {
206 failedChild = i;
207 break;
208 }
209 }
210
211 if (result == null)
212 {
213 if (failedChild == -1)
214 {
215 result = EventHandlerUtil.invalidGetMethod(rsvc, context,
216 "$" + rootString, previousResult, null, uberInfo);
217 }
218 else
219 {
220 StringBuffer name = new StringBuffer("$").append(rootString);
221 for (int i = 0; i <= failedChild; i++)
222 {
223 Node node = jjtGetChild(i);
224 if (node instanceof ASTMethod)
225 {
226 name.append(".").append(((ASTMethod) node).getMethodName()).append("()");
227 }
228 else
229 {
230 name.append(".").append(node.getFirstToken().image);
231 }
232 }
233
234 if (jjtGetChild(failedChild) instanceof ASTMethod)
235 {
236 String methodName = ((ASTMethod) jjtGetChild(failedChild)).getMethodName();
237 result = EventHandlerUtil.invalidMethod(rsvc, context,
238 name.toString(), previousResult, methodName, uberInfo);
239 }
240 else
241 {
242 String property = jjtGetChild(failedChild).getFirstToken().image;
243 result = EventHandlerUtil.invalidGetMethod(rsvc, context,
244 name.toString(), previousResult, property, uberInfo);
245 }
246 }
247
248 }
249
250 return result;
251 }
252 catch(MethodInvocationException mie)
253 {
254
255
256
257
258 log.error("Method " + mie.getMethodName()
259 + " threw exception for reference $"
260 + rootString
261 + " in template " + context.getCurrentTemplateName()
262 + " at " + " [" + this.getLine() + ","
263 + this.getColumn() + "]");
264
265 mie.setReferenceName(rootString);
266 throw mie;
267 }
268 }
269
270
271
272
273
274
275
276
277
278
279
280 public boolean render(InternalContextAdapter context, Writer writer)
281 throws IOException, MethodInvocationException
282 {
283
284 if (referenceType == RUNT)
285 {
286 if (context.getAllowRendering())
287 {
288 writer.write(rootString);
289 }
290
291 return true;
292 }
293
294 Object value = execute(null, context);
295
296
297
298
299
300
301
302 if (escaped)
303 {
304 if (value == null)
305 {
306 if (context.getAllowRendering())
307 {
308 writer.write(escPrefix);
309 writer.write("\\");
310 writer.write(nullString);
311 }
312 }
313 else
314 {
315 if (context.getAllowRendering())
316 {
317 writer.write(escPrefix);
318 writer.write(nullString);
319 }
320 }
321
322 return true;
323 }
324
325
326
327
328
329
330
331 value = EventHandlerUtil.referenceInsert(rsvc, context, literal(), value);
332
333 String toString = null;
334 if (value != null)
335 {
336 toString = value.toString();
337 }
338
339
340
341
342
343
344 if ( value == null || toString == null)
345 {
346
347
348
349
350 if (context.getAllowRendering())
351 {
352 writer.write(escPrefix);
353 writer.write(escPrefix);
354 writer.write(morePrefix);
355 writer.write(nullString);
356 }
357
358 if (logOnNull && referenceType != QUIET_REFERENCE && log.isInfoEnabled())
359 {
360 log.info("Null reference [template '"
361 + context.getCurrentTemplateName() + "', line "
362 + this.getLine() + ", column " + this.getColumn()
363 + "] : " + this.literal() + " cannot be resolved.");
364 }
365 return true;
366 }
367 else
368 {
369
370
371
372
373 if (context.getAllowRendering())
374 {
375 writer.write(escPrefix);
376 writer.write(morePrefix);
377 writer.write(toString);
378 }
379
380 return true;
381 }
382 }
383
384
385
386
387
388
389
390
391
392
393 public boolean evaluate(InternalContextAdapter context)
394 throws MethodInvocationException
395 {
396 Object value = execute(null, context);
397
398 if (value == null)
399 {
400 return false;
401 }
402 else if (value instanceof Boolean)
403 {
404 if (((Boolean) value).booleanValue())
405 return true;
406 else
407 return false;
408 }
409 else
410 return true;
411 }
412
413
414
415
416 public Object value(InternalContextAdapter context)
417 throws MethodInvocationException
418 {
419 return (computableReference ? execute(null, context) : null);
420 }
421
422
423
424
425
426
427
428
429
430
431
432
433 public boolean setValue( InternalContextAdapter context, Object value)
434 throws MethodInvocationException
435 {
436 if (jjtGetNumChildren() == 0)
437 {
438 context.put(rootString, value);
439 return true;
440 }
441
442
443
444
445
446
447
448 Object result = getVariableValue(context, rootString);
449
450 if (result == null)
451 {
452 String msg = "reference set : template = "
453 + context.getCurrentTemplateName() +
454 " [line " + getLine() + ",column " +
455 getColumn() + "] : " + literal() +
456 " is not a valid reference.";
457
458 log.error(msg);
459 return false;
460 }
461
462
463
464
465
466 for (int i = 0; i < numChildren - 1; i++)
467 {
468 result = jjtGetChild(i).execute(result, context);
469
470 if (result == null)
471 {
472 String msg = "reference set : template = "
473 + context.getCurrentTemplateName() +
474 " [line " + getLine() + ",column " +
475 getColumn() + "] : " + literal() +
476 " is not a valid reference.";
477
478 log.error(msg);
479
480 return false;
481 }
482 }
483
484
485
486
487
488
489
490 try
491 {
492 VelPropertySet vs =
493 rsvc.getUberspect().getPropertySet(result, identifier,
494 value, uberInfo);
495
496 if (vs == null)
497 return false;
498
499 vs.invoke(result, value);
500 }
501 catch(InvocationTargetException ite)
502 {
503
504
505
506
507 throw new MethodInvocationException(
508 "ASTReference : Invocation of method '"
509 + identifier + "' in " + result.getClass()
510 + " threw exception "
511 + ite.getTargetException().toString(),
512 ite.getTargetException(), identifier, context.getCurrentTemplateName(), this.getLine(), this.getColumn());
513 }
514
515
516
517 catch( RuntimeException e )
518 {
519 throw e;
520 }
521 catch(Exception e)
522 {
523
524
525
526 log.error("ASTReference setValue() : exception : " + e
527 + " template = " + context.getCurrentTemplateName()
528 + " [" + this.getLine() + "," + this.getColumn() + "]");
529 return false;
530 }
531
532 return true;
533 }
534
535 private String getRoot()
536 {
537 Token t = getFirstToken();
538
539
540
541
542
543
544
545
546
547
548 int slashbang = t.image.indexOf("\\!");
549
550 if (slashbang != -1)
551 {
552
553
554
555
556
557
558
559
560
561
562
563
564 int i = 0;
565 int len = t.image.length();
566
567 i = t.image.indexOf('$');
568
569 if (i == -1)
570 {
571
572 log.error("ASTReference.getRoot() : internal error : "
573 + "no $ found for slashbang.");
574 computableReference = false;
575 nullString = t.image;
576 return nullString;
577 }
578
579 while (i < len && t.image.charAt(i) != '\\')
580 {
581 i++;
582 }
583
584
585
586 int start = i;
587 int count = 0;
588
589 while (i < len && t.image.charAt(i++) == '\\')
590 {
591 count++;
592 }
593
594
595
596
597
598
599 nullString = t.image.substring(0,start);
600 nullString += t.image.substring(start, start + count-1 );
601 nullString += t.image.substring(start+count);
602
603
604
605
606
607
608 computableReference = false;
609
610 return nullString;
611 }
612
613
614
615
616
617
618
619
620 escaped = false;
621
622 if (t.image.startsWith("\\"))
623 {
624
625
626
627
628 int i = 0;
629 int len = t.image.length();
630
631 while (i < len && t.image.charAt(i) == '\\')
632 {
633 i++;
634 }
635
636 if ((i % 2) != 0)
637 escaped = true;
638
639 if (i > 0)
640 escPrefix = t.image.substring(0, i / 2 );
641
642 t.image = t.image.substring(i);
643 }
644
645
646
647
648
649
650
651 int loc1 = t.image.lastIndexOf('$');
652
653
654
655
656
657
658 if (loc1 > 0)
659 {
660 morePrefix = morePrefix + t.image.substring(0, loc1);
661 t.image = t.image.substring(loc1);
662 }
663
664
665
666
667
668
669
670 nullString = literal();
671
672 if (t.image.startsWith("$!"))
673 {
674 referenceType = QUIET_REFERENCE;
675
676
677
678
679
680 if (!escaped)
681 nullString = "";
682
683 if (t.image.startsWith("$!{"))
684 {
685
686
687
688
689 return t.next.image;
690 }
691 else
692 {
693
694
695
696
697 return t.image.substring(2);
698 }
699 }
700 else if (t.image.equals("${"))
701 {
702
703
704
705
706 referenceType = FORMAL_REFERENCE;
707 return t.next.image;
708 }
709 else if (t.image.startsWith("$"))
710 {
711
712
713
714
715
716 referenceType = NORMAL_REFERENCE;
717 return t.image.substring(1);
718 }
719 else
720 {
721
722
723
724
725
726 referenceType = RUNT;
727 return t.image;
728 }
729
730 }
731
732
733
734
735
736
737
738 public Object getVariableValue(Context context, String variable) throws MethodInvocationException
739 {
740 return context.get(variable);
741 }
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756 public void setLiteral(String literal)
757 {
758
759
760
761
762 if( this.literal == null)
763 this.literal = literal;
764 }
765
766
767
768
769
770
771
772
773 public String literal()
774 {
775 if (literal != null)
776 return literal;
777
778 return super.literal();
779 }
780 }