/** * gets the value of the reference and outputs it to the writer. * * @param context context of data to use in getting value * @param writer writer to render to * @return True if rendering was successful. * @throws IOException * @throws MethodInvocationException */ public boolean render(InternalContextAdapter context, Writer writer) throws IOException, MethodInvocationException { if (referenceType == RUNT) { writer.write(rootString); return true; } Object value = null; if (escaped && strictEscape) { /** * If we are in strict mode and the variable is escaped, then don't bother to retreive the * value since we won't use it. And if the var is not defined it will throw an exception. Set * value to TRUE to fall through below with simply printing $foo, and not \$foo */ value = Boolean.TRUE; } else { value = execute(null, context); } String localNullString = null; /* * if this reference is escaped (\$foo) then we want to do one of two things : 1) if this is * a reference in the context, then we want to print $foo 2) if not, then \$foo (its * considered schmoo, not VTL) */ if (escaped) { localNullString = getNullString(context); if (value == null) { writer.write(escPrefix); writer.write("\\"); writer.write(localNullString); } else { writer.write(escPrefix); writer.write(localNullString); } return true; } /* * the normal processing * * if we have an event cartridge, get a new value object */ value = EventHandlerUtil.referenceInsert(rsvc, context, literal, value); String toString = null; if (value != null) { if (value instanceof Renderable) { Renderable renderable = (Renderable) value; try { if (renderable.render(context, writer)) return true; } catch (RuntimeException e) { // We commonly get here when an error occurs within a block reference. // We want to log where the reference is at so that a developer can easily // know where the offending call is located. This can be seen // as another element of the error stack we report to log. log.error( "Exception rendering " + ((renderable instanceof Reference) ? "block " : "Renderable ") + rootString + " at " + Log.formatFileString(this)); throw e; } } toString = value.toString(); } if (value == null || toString == null) { if (strictRef) { if (referenceType != QUIET_REFERENCE) { log.error( "Prepend the reference with '$!' e.g., $!" + literal().substring(1) + " if you want Velocity to ignore the reference when it evaluates to null"); if (value == null) { throw new VelocityException( "Reference " + literal() + " evaluated to null when attempting to render at " + Log.formatFileString(this)); } else // toString == null { // This will probably rarely happen, but when it does we want to // inform the user that toString == null so they don't pull there // hair out wondering why Velocity thinks the value is null. throw new VelocityException( "Reference " + literal() + " evaluated to object " + value.getClass().getName() + " whose toString() method returned null at " + Log.formatFileString(this)); } } return true; } /* * write prefix twice, because it's schmoo, so the \ don't escape each * other... */ localNullString = getNullString(context); if (!strictEscape) { // If in strict escape mode then we only print escape once. // Yea, I know.. brittle stuff writer.write(escPrefix); } writer.write(escPrefix); writer.write(morePrefix); writer.write(localNullString); if (logOnNull && referenceType != QUIET_REFERENCE && log.isDebugEnabled()) { log.debug( "Null reference [template '" + getTemplateName() + "', line " + this.getLine() + ", column " + this.getColumn() + "] : " + this.literal() + " cannot be resolved."); } return true; } else { /* * non-null processing */ writer.write(escPrefix); writer.write(morePrefix); writer.write(toString); return true; } }
/** * gets an Object that 'is' the value of the reference * * @param o unused Object parameter * @param context context used to generate value * @return The execution result. * @throws MethodInvocationException */ public Object execute(Object o, InternalContextAdapter context) throws MethodInvocationException { if (referenceType == RUNT) return null; /* * get the root object from the context */ Object result = getVariableValue(context, rootString); if (result == null && !strictRef) { return EventHandlerUtil.invalidGetMethod( rsvc, context, getDollarBang() + rootString, null, null, uberInfo); } /* * Iteratively work 'down' (it's flat...) the reference * to get the value, but check to make sure that * every result along the path is valid. For example: * * $hashtable.Customer.Name * * The $hashtable may be valid, but there is no key * 'Customer' in the hashtable so we want to stop * when we find a null value and return the null * so the error gets logged. */ try { Object previousResult = result; int failedChild = -1; for (int i = 0; i < numChildren; i++) { if (strictRef && result == null) { /** * At this point we know that an attempt is about to be made to call a method or property * on a null value. */ String name = jjtGetChild(i).getFirstToken().image; throw new VelocityException( "Attempted to access '" + name + "' on a null value at " + Log.formatFileString( uberInfo.getTemplateName(), +jjtGetChild(i).getLine(), jjtGetChild(i).getColumn())); } previousResult = result; result = jjtGetChild(i).execute(result, context); if (result == null && !strictRef) // If strict and null then well catch this // next time through the loop { failedChild = i; break; } } if (result == null) { if (failedChild == -1) { result = EventHandlerUtil.invalidGetMethod( rsvc, context, getDollarBang() + rootString, previousResult, null, uberInfo); } else { StringBuffer name = new StringBuffer(getDollarBang()).append(rootString); for (int i = 0; i <= failedChild; i++) { Node node = jjtGetChild(i); if (node instanceof ASTMethod) { name.append(".").append(((ASTMethod) node).getMethodName()).append("()"); } else { name.append(".").append(node.getFirstToken().image); } } if (jjtGetChild(failedChild) instanceof ASTMethod) { String methodName = ((ASTMethod) jjtGetChild(failedChild)).getMethodName(); result = EventHandlerUtil.invalidMethod( rsvc, context, name.toString(), previousResult, methodName, uberInfo); } else { String property = jjtGetChild(failedChild).getFirstToken().image; result = EventHandlerUtil.invalidGetMethod( rsvc, context, name.toString(), previousResult, property, uberInfo); } } } return result; } catch (MethodInvocationException mie) { mie.setReferenceName(rootString); throw mie; } }