/// <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);
		}