/// <summary> /// returns an Iterator to the collection in the #foreach() /// </summary> /// <param name="context"> current context </param> /// <param name="node"> AST node </param> /// <returns>Iterator to do the dataset </returns> private IEnumerator GetIterator(IInternalContextAdapter context, INode node) { // get our list object, and punt if it's null. Object listObject = node.GetChild(2).Value(context); // if we have an event cartridge, get a new value object NVelocity.App.Events.EventCartridge eventCartridge = context.EventCartridge; if (eventCartridge != null) { listObject = eventCartridge.ReferenceInsert(new Stack(), node.GetChild(2).Literal, listObject); } if (listObject == null) { return null; } // See if we already know what type this is. // Use the introspection cache EnumType type = EnumType.Unknown; IntrospectionCacheData introspectionCacheData = context.ICacheGet(this); Type c = listObject.GetType(); // if we have an entry in the cache, and the Class we have // cached is the same as the Class of the data object // then we are ok if (introspectionCacheData != null && introspectionCacheData.ContextData == c) { // dig the type out of the data object type = ((EnumType)introspectionCacheData.Thingy); } // If we still don't know what this is, // figure out what type of object the list // element is, and get the iterator for it if (type == EnumType.Unknown) { if (listObject.GetType().IsArray) { type = EnumType.Array; } else if (listObject is IDictionary) { type = EnumType.Dictionary; } else if (listObject is ICollection) { type = EnumType.Collection; } else if (listObject is IEnumerable) { type = EnumType.Enumerable; } else if (listObject is IEnumerator) { type = EnumType.Enumeration; } // if we did figure it out, cache it if (type != EnumType.Unknown) { introspectionCacheData = new IntrospectionCacheData(c, type); context.ICachePut(this, introspectionCacheData); } } // now based on the type from either cache or examination... switch (type) { case EnumType.Collection: return ((ICollection)listObject).GetEnumerator(); case EnumType.Enumerable: return ((IEnumerable)listObject).GetEnumerator(); case EnumType.Enumeration: runtimeServices.Warn( string.Format( "Warning! The reference {0} is an Enumeration in the #foreach() loop at [{1},{2}] in template {3}. Because it's not resetable, if used in more than once, this may lead to unexpected results.", node.GetChild(2).FirstToken.Image, Line, Column, context.CurrentTemplateName)); return (IEnumerator)listObject; case EnumType.Array: return ((Array)listObject).GetEnumerator(); case EnumType.Dictionary: return ((IDictionary)listObject).GetEnumerator(); default: /* we have no clue what this is */ runtimeServices.Warn( string.Format( "Could not determine type of enumerator ({0}) in #foreach loop for {1} at [{2},{3}] in template {4}", listObject.GetType().Name, node.GetChild(2).FirstToken.Image, Line, Column, context.CurrentTemplateName)); return null; } }
public void ICachePut(Object key, IntrospectionCacheData o) { internalHousekeepingContext.ICachePut(key, o); }
public void ICachePut(Object key, IntrospectionCacheData o) { introspectionCache[key] = o; }
/// <summary> /// invokes the method on the object passed in /// </summary> public override Object Execute(Object o, IInternalContextAdapter context) { bool isString = o.GetType() == typeof(string); bool isDecimal = o.GetType() == typeof(decimal); bool isPrimitive = o.GetType().IsPrimitive; if (identifier == "to_quote" && (isString || isPrimitive || isDecimal)) { return string.Format("\"{0}\"", EscapeDoubleQuote(o.ToString())); } else if (identifier == "to_squote" && (isString || isPrimitive || isDecimal)) { return string.Format("'{0}'", EscapeSingleQuote(o.ToString())); } IDuck duck = o as IDuck; if (duck != null) { return duck.GetInvoke(identifier); } IVelPropertyGet velPropertyGet = null; try { Type c = o.GetType(); // first, see if we have this information cached. IntrospectionCacheData introspectionCacheData = context.ICacheGet(this); // if we have the cache data and the class of the object we are // invoked with is the same as that in the cache, then we must // be all-right. The last 'variable' is the method name, and // that is fixed in the template :) if (introspectionCacheData != null && introspectionCacheData.ContextData == c) { velPropertyGet = (IVelPropertyGet) introspectionCacheData.Thingy; } else { // otherwise, do the introspection, and cache it velPropertyGet = runtimeServices.Uberspect.GetPropertyGet(o, identifier, uberInfo); if (velPropertyGet != null && velPropertyGet.Cacheable) { introspectionCacheData = new IntrospectionCacheData(c, velPropertyGet); context.ICachePut(this, introspectionCacheData); } } } catch(System.Exception e) { runtimeServices.Error(string.Format("ASTIdentifier.execute() : identifier = {0} : {1}", identifier, e)); } // we have no executor... punt... if (velPropertyGet == null) return null; // now try and execute. If we get a TargetInvocationException, // throw that as the app wants to get these. If not, log and punt. try { return velPropertyGet.Invoke(o); } catch(TargetInvocationException targetInvocationException) { EventCartridge ec = context.EventCartridge; // if we have an event cartridge, see if it wants to veto // also, let non-Exception Throwables go... if (ec == null) { // no event cartridge to override. Just throw String message = String.Format( "Invocation of method '{0}' in {1}, template {2} Line {3} Column {4} threw an exception", velPropertyGet.MethodName, o != null ? o.GetType().FullName : string.Empty, uberInfo.TemplateName, uberInfo.Line, uberInfo.Column); throw new MethodInvocationException(message, targetInvocationException.InnerException, velPropertyGet.MethodName); } else { try { return ec.HandleMethodException(o.GetType(), velPropertyGet.MethodName, targetInvocationException.InnerException); } catch(System.Exception) { String message = String.Format( "Invocation of method '{0}' in {1}, template {2} Line {3} Column {4} threw an exception", velPropertyGet.MethodName, o != null ? o.GetType().FullName : string.Empty, uberInfo.TemplateName, uberInfo.Line, uberInfo.Column); throw new MethodInvocationException(message, targetInvocationException.InnerException, velPropertyGet.MethodName); } } } catch(ArgumentException) { return null; } catch(System.Exception e) { runtimeServices.Error( string.Format("ASTIdentifier() : exception invoking method for identifier '{0}' in {1} : {2}", identifier, o.GetType(), e)); } return null; }
/// <summary> /// invokes the method. Returns null if a problem, the /// actual return if the method returns something, or /// an empty string "" if the method returns void /// </summary> public override Object Execute(Object o, IInternalContextAdapter context) { IDuck duck = o as IDuck; object[] parameters = new object[paramCount]; if (duck != null) { EvalParameters(parameters, context); return duck.Invoke(methodName, parameters); } /* * new strategy (strategy!) for introspection. Since we want * to be thread- as well as context-safe, we *must* do it now, * at execution time. There can be no in-node caching, * but if we are careful, we can do it in the context. */ MethodInfo method = null; PropertyInfo property = null; bool preparedAlready = false; object[] methodArguments = new object[paramCount]; try { /* * check the cache */ IntrospectionCacheData introspectionCacheData = context.ICacheGet(this); Type c = o.GetType(); /* * like ASTIdentifier, if we have cache information, and the * Class of Object o is the same as that in the cache, we are * safe. */ EvalParameters(parameters, context); if (introspectionCacheData != null && introspectionCacheData.ContextData == c) { preparedAlready = true; /* * and get the method from the cache */ if (introspectionCacheData.Thingy is MethodInfo) { method = (MethodInfo) introspectionCacheData.Thingy; methodArguments = BuildMethodArgs(method, parameters, paramArrayIndex); } if (introspectionCacheData.Thingy is PropertyInfo) { property = (PropertyInfo) introspectionCacheData.Thingy; } } else { /* * otherwise, do the introspection, and then * cache it */ Object obj = PerformIntrospection(context, c, parameters); if (obj is MethodInfo) { method = (MethodInfo) obj; } if (obj is PropertyInfo) { property = (PropertyInfo) obj; } if (obj != null) { introspectionCacheData = new IntrospectionCacheData(); introspectionCacheData.ContextData = c; introspectionCacheData.Thingy = obj; context.ICachePut(this, introspectionCacheData); } } /* * if we still haven't gotten the method, either we are calling * a method that doesn't exist (which is fine...) or I screwed * it up. */ if (method == null && property == null) { return null; } } catch(System.Exception ex) { runtimeServices.Error(string.Format("ASTMethod.execute() : exception from introspection : {0}", ex)); throw new RuntimeException( String.Format( "Error during object introspection. Check inner exception for details. Node literal {0} Line {1} Column {2}", base.Literal, Line, Column), ex); } try { /* * get the returned object. It may be null, and that is * valid for something declared with a void return type. * Since the caller is expecting something to be returned, * as long as things are peachy, we can return an empty * String so ASTReference() correctly figures out that * all is well. */ Object obj; if (method == null) { obj = property.GetValue(o, null); } else { if (!preparedAlready) { methodArguments = BuildMethodArgs(method, parameters); } obj = method.Invoke(o, methodArguments); if (obj == null && method.ReturnType == typeof(void)) { obj = String.Empty; } } return obj; } catch(TargetInvocationException targetInvocationException) { /* * In the event that the invocation of the method * itself throws an exception, we want to catch that * wrap it, and throw. We don't log here as we want to figure * out which reference threw the exception, so do that * above */ EventCartridge eventCartridge = context.EventCartridge; /* * if we have an event cartridge, see if it wants to veto * also, let non-Exception Throwables go... */ if (eventCartridge == null) { /* * no event cartridge to override. Just throw */ throw new MethodInvocationException( string.Format("Invocation of method '{0}' in {1} threw exception {2} : {3}", methodName, o.GetType(), targetInvocationException.GetBaseException().GetType(), targetInvocationException.GetBaseException().Message), targetInvocationException.GetBaseException(), methodName); } else { try { return eventCartridge.HandleMethodException(o.GetType(), methodName, targetInvocationException.GetBaseException()); } catch(System.Exception e) { throw new MethodInvocationException( string.Format("Invocation of method '{0}' in {1} threw exception {2} : {3}", methodName, o.GetType(), e.GetType(), e.Message), e, methodName); } } } catch(System.Exception e) { runtimeServices.Error( string.Format("ASTMethod.execute() : exception invoking method '{0}' in {1} : {2}", methodName, o.GetType(), e)); throw e; } }
public void ICachePut(Object key, IntrospectionCacheData o) { innerContext.ICachePut(key, o); }