001 package org.apache.commons.digester3; 002 003 /* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022 import static java.lang.String.format; 023 import static org.apache.commons.beanutils.MethodUtils.invokeExactMethod; 024 import static org.apache.commons.beanutils.MethodUtils.invokeMethod; 025 026 import org.xml.sax.Attributes; 027 028 /** 029 * Abstract implementation for {@link org.apache.commons.digester3.SetNextRule}, 030 * {@link org.apache.commons.digester3.SetRootRule} and {@link org.apache.commons.digester3.SetTopRule} rules. 031 * 032 * @since 3.0 033 */ 034 public abstract class AbstractMethodRule 035 extends Rule 036 { 037 038 /** 039 * The method name to call on the parent object. 040 */ 041 protected String methodName = null; 042 043 /** 044 * The Java class name of the parameter type expected by the method. 045 */ 046 protected String paramTypeName = null; 047 048 /** 049 * The Java class name of the parameter type expected by the method. 050 */ 051 protected Class<?> paramType; 052 053 /** 054 * Should we use exact matching. Default is no. 055 */ 056 protected boolean useExactMatch = false; 057 058 /** 059 * Should this rule be invoked when {@link #begin(String, String, Attributes)} (true) 060 * or {@link #end(String, String)} (false) methods are invoked, false by default. 061 */ 062 protected boolean fireOnBegin = false; 063 064 /** 065 * Construct a "set next" rule with the specified method name. The method's argument type is assumed to be the class 066 * of the child object. 067 * 068 * @param methodName Method name of the parent method to call 069 */ 070 public AbstractMethodRule( String methodName ) 071 { 072 this( methodName, (String) null ); 073 } 074 075 /** 076 * Construct a "set next" rule with the specified method name. 077 * 078 * @param methodName Method name of the parent method to call 079 * @param paramType Java class of the parent method's argument (if you wish to use a primitive type, specify the 080 * corresonding Java wrapper class instead, such as <code>java.lang.Boolean</code> for a 081 * <code>boolean</code> parameter) 082 */ 083 public AbstractMethodRule( String methodName, Class<?> paramType ) 084 { 085 this( methodName, paramType.getName() ); 086 this.paramType = paramType; 087 } 088 089 /** 090 * Construct a "set next" rule with the specified method name. 091 * 092 * @param methodName Method name of the parent method to call 093 * @param paramTypeName Java class of the parent method's argument (if you wish to use a primitive type, specify the 094 * corresonding Java wrapper class instead, such as <code>java.lang.Boolean</code> for a 095 * <code>boolean</code> parameter) 096 */ 097 public AbstractMethodRule( String methodName, String paramTypeName ) 098 { 099 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 }