001 /* $Id: SetNextRule.java 992060 2010-09-02 19:09:47Z simonetripodi $ 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one or more 004 * contributor license agreements. See the NOTICE file distributed with 005 * this work for additional information regarding copyright ownership. 006 * The ASF licenses this file to You under the Apache License, Version 2.0 007 * (the "License"); you may not use this file except in compliance with 008 * the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019 020 package org.apache.commons.digester; 021 022 023 import org.apache.commons.beanutils.MethodUtils; 024 025 026 /** 027 * <p>Rule implementation that calls a method on the (top-1) (parent) 028 * object, passing the top object (child) as an argument. It is 029 * commonly used to establish parent-child relationships.</p> 030 * 031 * <p>This rule now supports more flexible method matching by default. 032 * It is possible that this may break (some) code 033 * written against release 1.1.1 or earlier. 034 * See {@link #isExactMatch()} for more details.</p> 035 * 036 * <p>Note that while CallMethodRule uses commons-beanutils' data-conversion 037 * functionality (ConvertUtils class) to convert parameter values into 038 * the appropriate type for the parameter to the called method, this 039 * rule does not. Needing to use ConvertUtils functionality when building 040 * parent-child relationships is expected to be very rare; however if you 041 * do need this then instead of using this rule, create a CallMethodRule 042 * specifying targetOffset of 1 in the constructor.</p> 043 */ 044 045 public class SetNextRule extends Rule { 046 047 048 // ----------------------------------------------------------- Constructors 049 050 051 /** 052 * Construct a "set next" rule with the specified method name. The 053 * method's argument type is assumed to be the class of the 054 * child object. 055 * 056 * @param digester The associated Digester 057 * @param methodName Method name of the parent method to call 058 * 059 * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 060 * Use {@link #SetNextRule(String methodName)} instead. 061 */ 062 @Deprecated 063 public SetNextRule(Digester digester, String methodName) { 064 065 this(methodName); 066 067 } 068 069 070 /** 071 * Construct a "set next" rule with the specified method name. 072 * 073 * @param digester The associated Digester 074 * @param methodName Method name of the parent method to call 075 * @param paramType Java class of the parent method's argument 076 * (if you wish to use a primitive type, specify the corresonding 077 * Java wrapper class instead, such as <code>java.lang.Boolean</code> 078 * for a <code>boolean</code> parameter) 079 * 080 * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 081 * Use {@link #SetNextRule(String methodName,String paramType)} instead. 082 */ 083 @Deprecated 084 public SetNextRule(Digester digester, String methodName, 085 String paramType) { 086 087 this(methodName, paramType); 088 089 } 090 091 /** 092 * Construct a "set next" rule with the specified method name. The 093 * method's argument type is assumed to be the class of the 094 * child object. 095 * 096 * @param methodName Method name of the parent method to call 097 */ 098 public SetNextRule(String methodName) { 099 100 this(methodName, null); 101 102 } 103 104 105 /** 106 * Construct a "set next" rule with the specified method name. 107 * 108 * @param methodName Method name of the parent method to call 109 * @param paramType Java class of the parent method's argument 110 * (if you wish to use a primitive type, specify the corresonding 111 * Java wrapper class instead, such as <code>java.lang.Boolean</code> 112 * for a <code>boolean</code> parameter) 113 */ 114 public SetNextRule(String methodName, 115 String paramType) { 116 117 this.methodName = methodName; 118 this.paramType = paramType; 119 120 } 121 122 123 // ----------------------------------------------------- Instance Variables 124 125 126 /** 127 * The method name to call on the parent object. 128 */ 129 protected String methodName = null; 130 131 132 /** 133 * The Java class name of the parameter type expected by the method. 134 */ 135 protected String paramType = null; 136 137 /** 138 * Should we use exact matching. Default is no. 139 */ 140 protected boolean useExactMatch = false; 141 142 // --------------------------------------------------------- Public Methods 143 144 145 /** 146 * <p>Is exact matching being used.</p> 147 * 148 * <p>This rule uses <code>org.apache.commons.beanutils.MethodUtils</code> 149 * to introspect the relevent objects so that the right method can be called. 150 * Originally, <code>MethodUtils.invokeExactMethod</code> was used. 151 * This matches methods very strictly 152 * and so may not find a matching method when one exists. 153 * This is still the behaviour when exact matching is enabled.</p> 154 * 155 * <p>When exact matching is disabled, <code>MethodUtils.invokeMethod</code> is used. 156 * This method finds more methods but is less precise when there are several methods 157 * with correct signatures. 158 * So, if you want to choose an exact signature you might need to enable this property.</p> 159 * 160 * <p>The default setting is to disable exact matches.</p> 161 * 162 * @return true iff exact matching is enabled 163 * @since Digester Release 1.1.1 164 */ 165 public boolean isExactMatch() { 166 167 return useExactMatch; 168 } 169 170 /** 171 * <p>Set whether exact matching is enabled.</p> 172 * 173 * <p>See {@link #isExactMatch()}.</p> 174 * 175 * @param useExactMatch should this rule use exact method matching 176 * @since Digester Release 1.1.1 177 */ 178 public void setExactMatch(boolean useExactMatch) { 179 180 this.useExactMatch = useExactMatch; 181 } 182 183 /** 184 * Process the end of this element. 185 */ 186 @Override 187 public void end() throws Exception { 188 189 // Identify the objects to be used 190 Object child = digester.peek(0); 191 Object parent = digester.peek(1); 192 if (digester.log.isDebugEnabled()) { 193 if (parent == null) { 194 digester.log.debug("[SetNextRule]{" + digester.match + 195 "} Call [NULL PARENT]." + 196 methodName + "(" + child + ")"); 197 } else { 198 digester.log.debug("[SetNextRule]{" + digester.match + 199 "} Call " + parent.getClass().getName() + "." + 200 methodName + "(" + child + ")"); 201 } 202 } 203 204 // Call the specified method 205 Class<?> paramTypes[] = new Class<?>[1]; 206 if (paramType != null) { 207 paramTypes[0] = 208 digester.getClassLoader().loadClass(paramType); 209 } else { 210 paramTypes[0] = child.getClass(); 211 } 212 213 if (useExactMatch) { 214 215 MethodUtils.invokeExactMethod(parent, methodName, 216 new Object[]{ child }, paramTypes); 217 218 } else { 219 220 MethodUtils.invokeMethod(parent, methodName, 221 new Object[]{ child }, paramTypes); 222 223 } 224 } 225 226 227 /** 228 * Render a printable version of this Rule. 229 */ 230 @Override 231 public String toString() { 232 233 StringBuffer sb = new StringBuffer("SetNextRule["); 234 sb.append("methodName="); 235 sb.append(methodName); 236 sb.append(", paramType="); 237 sb.append(paramType); 238 sb.append("]"); 239 return (sb.toString()); 240 241 } 242 243 244 }