1 package org.apache.commons.digester3; 2 3 /* 4 * Licensed to the Apache Software Foundation (ASF) under one 5 * or more contributor license agreements. See the NOTICE file 6 * distributed with this work for additional information 7 * regarding copyright ownership. The ASF licenses this file 8 * to you under the Apache License, Version 2.0 (the 9 * "License"); you may not use this file except in compliance 10 * with the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, 15 * software distributed under the License is distributed on an 16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 * KIND, either express or implied. See the License for the 18 * specific language governing permissions and limitations 19 * under the License. 20 */ 21 22 import static java.lang.String.format; 23 import static org.apache.commons.beanutils.MethodUtils.invokeExactMethod; 24 import static org.apache.commons.beanutils.MethodUtils.invokeMethod; 25 26 import org.xml.sax.Attributes; 27 28 /** 29 * Abstract implementation for {@link org.apache.commons.digester3.SetNextRule}, 30 * {@link org.apache.commons.digester3.SetRootRule} and {@link org.apache.commons.digester3.SetTopRule} rules. 31 * 32 * @since 3.0 33 */ 34 public abstract class AbstractMethodRule 35 extends Rule 36 { 37 38 /** 39 * The method name to call on the parent object. 40 */ 41 protected String methodName = null; 42 43 /** 44 * The Java class name of the parameter type expected by the method. 45 */ 46 protected String paramTypeName = null; 47 48 /** 49 * The Java class name of the parameter type expected by the method. 50 */ 51 protected Class<?> paramType; 52 53 /** 54 * Should we use exact matching. Default is no. 55 */ 56 protected boolean useExactMatch = false; 57 58 /** 59 * Should this rule be invoked when {@link #begin(String, String, Attributes)} (true) 60 * or {@link #end(String, String)} (false) methods are invoked, false by default. 61 */ 62 protected boolean fireOnBegin = false; 63 64 /** 65 * Construct a "set next" rule with the specified method name. The method's argument type is assumed to be the class 66 * of the child object. 67 * 68 * @param methodName Method name of the parent method to call 69 */ 70 public AbstractMethodRule( String methodName ) 71 { 72 this( methodName, (String) null ); 73 } 74 75 /** 76 * Construct a "set next" rule with the specified method name. 77 * 78 * @param methodName Method name of the parent method to call 79 * @param paramType Java class of the parent method's argument (if you wish to use a primitive type, specify the 80 * corresonding Java wrapper class instead, such as <code>java.lang.Boolean</code> for a 81 * <code>boolean</code> parameter) 82 */ 83 public AbstractMethodRule( String methodName, Class<?> paramType ) 84 { 85 this( methodName, paramType.getName() ); 86 this.paramType = paramType; 87 } 88 89 /** 90 * Construct a "set next" rule with the specified method name. 91 * 92 * @param methodName Method name of the parent method to call 93 * @param paramTypeName Java class of the parent method's argument (if you wish to use a primitive type, specify the 94 * corresonding Java wrapper class instead, such as <code>java.lang.Boolean</code> for a 95 * <code>boolean</code> parameter) 96 */ 97 public AbstractMethodRule( String methodName, String paramTypeName ) 98 { 99 this.methodName = methodName; 100 this.paramTypeName = paramTypeName; 101 } 102 103 /** 104 * <p> 105 * Is exact matching being used. 106 * </p> 107 * <p> 108 * This rule uses <code>org.apache.commons.beanutils.MethodUtils</code> to introspect the relevent objects so that 109 * the right method can be called. Originally, <code>MethodUtils.invokeExactMethod</code> was used. This matches 110 * methods very strictly and so may not find a matching method when one exists. This is still the behaviour when 111 * exact matching is enabled. 112 * </p> 113 * <p> 114 * When exact matching is disabled, <code>MethodUtils.invokeMethod</code> is used. This method finds more methods 115 * but is less precise when there are several methods with correct signatures. So, if you want to choose an exact 116 * signature you might need to enable this property. 117 * </p> 118 * <p> 119 * The default setting is to disable exact matches. 120 * </p> 121 * 122 * @return true if exact matching is enabled 123 * @since Digester Release 1.1.1 124 */ 125 public boolean isExactMatch() 126 { 127 return useExactMatch; 128 } 129 130 /** 131 * Sets this rule be invoked when {@link #begin(String, String, Attributes)} (true) 132 * or {@link #end(String, String)} (false) methods are invoked, false by default. 133 * 134 * @param fireOnBegin flag to mark this rule be invoked when {@link #begin(String, String, Attributes)} (true) 135 * or {@link #end(String, String)} (false) methods are invoked, false by default. 136 */ 137 public void setFireOnBegin( boolean fireOnBegin ) 138 { 139 this.fireOnBegin = fireOnBegin; 140 } 141 142 /** 143 * Returns the flag this rule be invoked when {@link #begin(String, String, Attributes)} (true) 144 * or {@link #end(String, String)} (false) methods are invoked, false by default. 145 * 146 * @return the flag this rule be invoked when {@link #begin(String, String, Attributes)} (true) 147 * or {@link #end(String, String)} (false) methods are invoked, false by default. 148 */ 149 public boolean isFireOnBegin() 150 { 151 return fireOnBegin; 152 } 153 154 /** 155 * <p> 156 * Set whether exact matching is enabled. 157 * </p> 158 * <p> 159 * See {@link #isExactMatch()}. 160 * </p> 161 * 162 * @param useExactMatch should this rule use exact method matching 163 * @since Digester Release 1.1.1 164 */ 165 public void setExactMatch( boolean useExactMatch ) 166 { 167 this.useExactMatch = useExactMatch; 168 } 169 170 /** 171 * {@inheritDoc} 172 */ 173 @Override 174 public void begin( String namespace, String name, Attributes attributes ) 175 throws Exception 176 { 177 if ( fireOnBegin ) 178 { 179 invoke(); 180 } 181 } 182 183 /** 184 * {@inheritDoc} 185 */ 186 @Override 187 public void end( String namespace, String name ) 188 throws Exception 189 { 190 if ( !fireOnBegin ) 191 { 192 invoke(); 193 } 194 } 195 196 /** 197 * Just performs the method execution. 198 * 199 * @throws Exception if any error occurs. 200 */ 201 private void invoke() 202 throws Exception 203 { 204 // Identify the objects to be used 205 Object child = getChild(); 206 Object parent = getParent(); 207 if ( getDigester().getLogger().isDebugEnabled() ) 208 { 209 if ( parent == null ) 210 { 211 getDigester().getLogger().debug( format( "[%s]{%s} Call [NULL PARENT].%s(%s)", 212 getClass().getSimpleName(), 213 getDigester().getMatch(), 214 methodName, 215 child ) ); 216 } 217 else 218 { 219 getDigester().getLogger().debug( format( "[%s]{%s} Call %s.%s(%s)", 220 getClass().getSimpleName(), 221 getDigester().getMatch(), 222 parent.getClass().getName(), 223 methodName, 224 child ) ); 225 } 226 } 227 228 // Call the specified method 229 Class<?> paramTypes[] = new Class<?>[1]; 230 if ( paramType != null ) 231 { 232 paramTypes[0] = getDigester().getClassLoader().loadClass( paramTypeName ); 233 } 234 else 235 { 236 paramTypes[0] = child.getClass(); 237 } 238 239 if ( useExactMatch ) 240 { 241 invokeExactMethod( parent, methodName, new Object[] { child }, paramTypes ); 242 } 243 else 244 { 245 invokeMethod( parent, methodName, new Object[] { child }, paramTypes ); 246 } 247 } 248 249 /** 250 * Returns the argument object of method has to be invoked. 251 * 252 * @return the argument object of method has to be invoked. 253 */ 254 protected abstract Object getChild(); 255 256 /** 257 * Returns the target object of method has to be invoked. 258 * 259 * @return the target object of method has to be invoked. 260 */ 261 protected abstract Object getParent(); 262 263 /** 264 * {@inheritDoc} 265 */ 266 @Override 267 public final String toString() 268 { 269 return format( "%s[methodName=%s, paramType=%s, paramTypeName=%s, useExactMatch=%s, fireOnBegin=%s]", 270 getClass().getSimpleName(), methodName, paramType, paramTypeName, useExactMatch, fireOnBegin ); 271 } 272 273 }