quiet references should never trigger invalid reference events

git-svn-id: https://svn.apache.org/repos/asf/velocity/engine/trunk@1752609 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTReference.java b/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTReference.java
index ff35fd8..ef24e7d 100644
--- a/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTReference.java
+++ b/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTReference.java
@@ -243,9 +243,9 @@ public Object execute(Object o, InternalContextAdapter context)
         {
             /*
              * do not trigger an invalid reference if the reference is present, but with a null value
-             * don't either inside an #if/#elseif evaluation context
+             * don't either for a quiet reference or inside an #if/#elseif evaluation context
              */
-            if (!context.containsKey(rootString) && !onlyTestingReference)
+            if (!context.containsKey(rootString) && referenceType != QUIET_REFERENCE && !onlyTestingReference)
             {
                 return EventHandlerUtil.invalidGetMethod(rsvc, context,
                         "$" + rootString, null, null, uberInfo);
@@ -290,9 +290,11 @@ public Object execute(Object o, InternalContextAdapter context)
                 {
                     // do not call bad reference handler if the getter is present
                     // (it means the getter has been called and returned null)
-                    // do not either if the *last* child failed while testing the reference
+                    // do not either for a quiet reference or if the *last* child failed while testing the reference
                     Object getter = context.icacheGet(jjtGetChild(i));
-                    if (getter == null && (!onlyTestingReference || i < jjtGetNumChildren() - 1))
+                    if (getter == null &&
+                            referenceType != QUIET_REFERENCE  &&
+                            (!onlyTestingReference || i < jjtGetNumChildren() - 1))
                     {
                         failedChild = i;
                         break;
@@ -306,9 +308,10 @@ public Object execute(Object o, InternalContextAdapter context)
                 {
                     /*
                      * do not trigger an invalid reference if the reference is present, but with a null value
-                     * don't either inside an #if/#elseif evaluation context when there's no child
+                     * don't either for a quiet reference,
+                     * or inside an #if/#elseif evaluation context when there's no child
                      */
-                    if (!context.containsKey(rootString) && (!onlyTestingReference || jjtGetNumChildren() > 0))
+                    if (!context.containsKey(rootString) && referenceType != QUIET_REFERENCE && (!onlyTestingReference || jjtGetNumChildren() > 0))
                     {
                         result = EventHandlerUtil.invalidGetMethod(rsvc, context,
                                 "$" + rootString, previousResult, null, uberInfo);
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/InvalidEventHandlerTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/InvalidEventHandlerTestCase.java
index e094173..2ef5085 100644
--- a/velocity-engine-core/src/test/java/org/apache/velocity/test/InvalidEventHandlerTestCase.java
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/InvalidEventHandlerTestCase.java
@@ -238,7 +238,13 @@ private void doTestInvalidReferenceEventHandler2(VelocityEngine ve, VelocityCont
             ve.evaluate( context, w, "mystring", s );
             fail("Expected exception.");
         } catch (RuntimeException e) {}
-        
+
+        // good object, bad method, quiet reference
+        s = "$!a1.afternoon()";
+        w = new StringWriter();
+        ve.evaluate( context, w, "mystring", s );
+        assertEquals("", w.toString());
+
         // bad object, bad method -- fails on get
         s = "$zz.daylight()";
         w = new StringWriter();
@@ -247,6 +253,12 @@ private void doTestInvalidReferenceEventHandler2(VelocityEngine ve, VelocityCont
             fail("Expected exception.");
         } catch (RuntimeException e) {}
 
+        // bad object, bad method, quiet reference
+        s = "$!zz.daylight()";
+        w = new StringWriter();
+        ve.evaluate( context, w, "mystring", s );
+        assertEquals("", w.toString());
+
         // change result
         s = "$b1.baby()";
         w = new StringWriter();
@@ -283,6 +295,12 @@ private void doTestInvalidReferenceEventHandler1(VelocityEngine ve, VelocityCont
             fail("Expected exception.");
         } catch (RuntimeException e) {}
 
+        // same one as a quiet reference should not fail
+        s = "$!a1.foobar";
+        w = new StringWriter();
+        ve.evaluate( context, w, "mystring", s );
+        assertEquals("",w.toString());
+
         // same one inside an #if statement should not fail
         s = "#if($a1.foobar)yes#{else}no#end";
         w = new StringWriter();
@@ -298,6 +316,12 @@ private void doTestInvalidReferenceEventHandler1(VelocityEngine ve, VelocityCont
             fail("Expected exception.");
         } catch (RuntimeException e) {}
 
+        // same one as a quiet reference should not fail
+        s = "$!a2.foobar";
+        w = new StringWriter();
+        ve.evaluate( context, w, "mystring", s );
+        assertEquals("",w.toString());
+
         // same one inside an #if statement should still fail
         s = "#if($a2.foobar)yes#{else}no#end";
         w = new StringWriter();
@@ -320,6 +344,13 @@ private void doTestInvalidReferenceEventHandler1(VelocityEngine ve, VelocityCont
             fail("Expected exception.");
         } catch (RuntimeException e) {}
 
+        // bad object, no property as quiet reference should not fail
+        s = "$!a3";
+        w = new StringWriter();
+        ve.evaluate(context, w, "mystring", s);
+        result = w.toString();
+        assertEquals("", result);
+
         // bad object, no property as #if condition should not fail
         s = "#if($a3)yes#{else}no#end";
         w = new StringWriter();