コード例 #1
0
ファイル: ASTMethod.cs プロジェクト: Telligent/NVelocity
        /// <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 (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,
                              methodName);
                }
                else
                {
                    try
                    {
                        return(eventCartridge.HandleMethodException(o.GetType(), methodName, targetInvocationException));
                    }
                    catch (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 (Exception e)
            {
                runtimeServices.Error(
                    string.Format("ASTMethod.execute() : exception invoking method '{0}' in {1} : {2}", methodName, o.GetType(), e));
                throw;
            }
        }