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><invoke-parent></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 }