001    // Copyright 2004, 2005 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    //     http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.hivemind.schema.rules;
016    
017    import java.lang.reflect.Method;
018    
019    import org.apache.hivemind.ApplicationRuntimeException;
020    import org.apache.hivemind.Element;
021    import org.apache.hivemind.schema.SchemaProcessor;
022    
023    /**
024     * Rule used to connect a child object to its parent by invoking a method on the parent, passing the
025     * child. The child object is the top object on the stack and the parent object is the next object
026     * down on the stack. Created from the <code>&lt;invoke-parent&gt;</code> element. Generally, this
027     * is the last rule in a sequence of rules.
028     * 
029     * @author Howard Lewis Ship
030     */
031    public class InvokeParentRule extends BaseRule
032    {
033        private String _methodName;
034    
035        private int _depth = 1;
036    
037        public InvokeParentRule()
038        {
039    
040        }
041    
042        public InvokeParentRule(String methodName)
043        {
044            _methodName = methodName;
045        }
046    
047        /**
048         * Invokes the named method on the parent object (using reflection).
049         */
050        public void begin(SchemaProcessor processor, Element element)
051        {
052            Object child = processor.peek();
053            Class childClass = child == null ? null : child.getClass();
054            Object parent = processor.peek(_depth);
055    
056            try
057            {
058                Method m = findMethod(parent, _methodName, childClass);
059    
060                m.invoke(parent, new Object[]
061                { child });
062            }
063            catch (Exception ex)
064            {
065                throw new ApplicationRuntimeException(RulesMessages.errorInvokingMethod(
066                        _methodName,
067                        parent,
068                        getLocation(),
069                        ex), getLocation(), ex);
070            }
071        }
072    
073        public String getMethodName()
074        {
075            return _methodName;
076        }
077    
078        public void setMethodName(String string)
079        {
080            _methodName = string;
081        }
082    
083        /**
084         * @since 1.1
085         */
086        public int getDepth()
087        {
088            return _depth;
089        }
090    
091        /**
092         * Sets the depth of the parent object. The default is 1.
093         */
094        public void setDepth(int i)
095        {
096            _depth = i;
097        }
098    
099        /**
100         * Searches for the *first* public method the has the right name, and takes a single parameter
101         * that is compatible with the parameter type.
102         * 
103         * @throws NoSuchMethodException
104         *             if a method can't be found
105         */
106        private Method findMethod(Object target, String name, Class parameterType)
107                throws NoSuchMethodException
108        {
109            Method[] methods = target.getClass().getMethods();
110    
111            for (int i = 0; i < methods.length; i++)
112            {
113                Method m = methods[i];
114                Class[] parameterTypes = m.getParameterTypes();
115    
116                if (parameterTypes.length != 1)
117                    continue;
118    
119                if (!m.getName().equals(name))
120                    continue;
121    
122                if ((parameterType != null && parameterTypes[0].isAssignableFrom(parameterType))
123                        || (parameterType == null && !parameterTypes[0].isPrimitive()))
124                    return m;
125            }
126    
127            throw new NoSuchMethodException(name);
128        }
129    }