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: private String _methodName;
033:
034: private int _depth = 1;
035:
036: public InvokeParentRule() {
037:
038: }
039:
040: public InvokeParentRule(String methodName) {
041: _methodName = methodName;
042: }
043:
044: /**
045: * Invokes the named method on the parent object (using reflection).
046: */
047: public void begin(SchemaProcessor processor, Element element) {
048: Object child = processor.peek();
049: Class childClass = child == null ? null : child.getClass();
050: Object parent = processor.peek(_depth);
051:
052: try {
053: Method m = findMethod(parent, _methodName, childClass);
054:
055: m.invoke(parent, new Object[] { child });
056: } catch (Exception ex) {
057: throw new ApplicationRuntimeException(RulesMessages
058: .errorInvokingMethod(_methodName, parent,
059: getLocation(), ex), getLocation(), ex);
060: }
061: }
062:
063: public String getMethodName() {
064: return _methodName;
065: }
066:
067: public void setMethodName(String string) {
068: _methodName = string;
069: }
070:
071: /**
072: * @since 1.1
073: */
074: public int getDepth() {
075: return _depth;
076: }
077:
078: /**
079: * Sets the depth of the parent object. The default is 1.
080: */
081: public void setDepth(int i) {
082: _depth = i;
083: }
084:
085: /**
086: * Searches for the *first* public method the has the right name, and takes a single parameter
087: * that is compatible with the parameter type.
088: *
089: * @throws NoSuchMethodException
090: * if a method can't be found
091: */
092: private Method findMethod(Object target, String name,
093: Class parameterType) throws NoSuchMethodException {
094: Method[] methods = target.getClass().getMethods();
095:
096: for (int i = 0; i < methods.length; i++) {
097: Method m = methods[i];
098: Class[] parameterTypes = m.getParameterTypes();
099:
100: if (parameterTypes.length != 1)
101: continue;
102:
103: if (!m.getName().equals(name))
104: continue;
105:
106: if ((parameterType != null && parameterTypes[0]
107: .isAssignableFrom(parameterType))
108: || (parameterType == null && !parameterTypes[0]
109: .isPrimitive()))
110: return m;
111: }
112:
113: throw new NoSuchMethodException(name);
114: }
115: }
|