001    /* $Id: SetRootRule.java 729106 2008-12-23 20:48:09Z rahul $
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 root object on the stack,
028     * passing the top object (child) as an argument.
029     * It is important to remember that this rule acts on <code>end</code>.</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    
037    public class SetRootRule extends Rule {
038    
039    
040        // ----------------------------------------------------------- Constructors
041    
042    
043        /**
044         * Construct a "set root" rule with the specified method name.  The
045         * method's argument type is assumed to be the class of the
046         * child object.
047         *
048         * @param digester The associated Digester
049         * @param methodName Method name of the parent method to call
050         *
051         * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
052         * Use {@link #SetRootRule(String methodName)} instead.
053         */
054        public SetRootRule(Digester digester, String methodName) {
055    
056            this(methodName);
057    
058        }
059    
060    
061        /**
062         * Construct a "set root" rule with the specified method name.
063         *
064         * @param digester The associated Digester
065         * @param methodName Method name of the parent method to call
066         * @param paramType Java class of the parent method's argument
067         *  (if you wish to use a primitive type, specify the corresonding
068         *  Java wrapper class instead, such as <code>java.lang.Boolean</code>
069         *  for a <code>boolean</code> parameter)
070         *
071         * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
072         * Use {@link #SetRootRule(String methodName,String paramType)} instead.
073         */
074        public SetRootRule(Digester digester, String methodName,
075                           String paramType) {
076    
077            this(methodName, paramType);
078    
079        }
080    
081        /**
082         * Construct a "set root" rule with the specified method name.  The
083         * method's argument type is assumed to be the class of the
084         * child object.
085         *
086         * @param methodName Method name of the parent method to call
087         */
088        public SetRootRule(String methodName) {
089    
090            this(methodName, null);
091    
092        }
093    
094    
095        /**
096         * Construct a "set root" rule with the specified method name.
097         *
098         * @param methodName Method name of the parent method to call
099         * @param paramType Java class of the parent method's argument
100         *  (if you wish to use a primitive type, specify the corresonding
101         *  Java wrapper class instead, such as <code>java.lang.Boolean</code>
102         *  for a <code>boolean</code> parameter)
103         */
104        public SetRootRule(String methodName,
105                           String paramType) {
106    
107            this.methodName = methodName;
108            this.paramType = paramType;
109    
110        }
111    
112        // ----------------------------------------------------- Instance Variables
113    
114    
115        /**
116         * The method name to call on the parent object.
117         */
118        protected String methodName = null;
119    
120    
121        /**
122         * The Java class name of the parameter type expected by the method.
123         */
124        protected String paramType = null;
125        
126        /**
127         * Should we use exact matching. Default is no.
128         */
129        protected boolean useExactMatch = false;
130    
131    
132        // --------------------------------------------------------- Public Methods
133    
134    
135        /**
136         * <p>Is exact matching being used.</p>
137         *
138         * <p>This rule uses <code>org.apache.commons.beanutils.MethodUtils</code> 
139         * to introspect the relevent objects so that the right method can be called.
140         * Originally, <code>MethodUtils.invokeExactMethod</code> was used.
141         * This matches methods very strictly 
142         * and so may not find a matching method when one exists.
143         * This is still the behaviour when exact matching is enabled.</p>
144         *
145         * <p>When exact matching is disabled, <code>MethodUtils.invokeMethod</code> is used.
146         * This method finds more methods but is less precise when there are several methods 
147         * with correct signatures.
148         * So, if you want to choose an exact signature you might need to enable this property.</p>
149         *
150         * <p>The default setting is to disable exact matches.</p>
151         *
152         * @return true iff exact matching is enabled
153         * @since Digester Release 1.1.1
154         */
155        public boolean isExactMatch() {
156        
157            return useExactMatch;
158        }
159        
160        
161        /**
162         * <p>Set whether exact matching is enabled.</p>
163         *
164         * <p>See {@link #isExactMatch()}.</p>
165         *
166         * @param useExactMatch should this rule use exact method matching
167         * @since Digester Release 1.1.1
168         */
169        public void setExactMatch(boolean useExactMatch) {
170    
171            this.useExactMatch = useExactMatch;
172        }
173    
174        /**
175         * Process the end of this element.
176         */
177        public void end() throws Exception {
178    
179            // Identify the objects to be used
180            Object child = digester.peek(0);
181            Object parent = digester.root;
182            if (digester.log.isDebugEnabled()) {
183                if (parent == null) {
184                    digester.log.debug("[SetRootRule]{" + digester.match +
185                            "} Call [NULL ROOT]." +
186                            methodName + "(" + child + ")");
187                } else {
188                    digester.log.debug("[SetRootRule]{" + digester.match +
189                            "} Call " + parent.getClass().getName() + "." +
190                            methodName + "(" + child + ")");
191                }
192            }
193    
194            // Call the specified method
195            Class<?> paramTypes[] = new Class<?>[1];
196            if (paramType != null) {
197                paramTypes[0] =
198                        digester.getClassLoader().loadClass(paramType);
199            } else {
200                paramTypes[0] = child.getClass();
201            }
202            
203            if (useExactMatch) {
204            
205                MethodUtils.invokeExactMethod(parent, methodName,
206                    new Object[]{ child }, paramTypes);
207                    
208            } else {
209            
210                MethodUtils.invokeMethod(parent, methodName,
211                    new Object[]{ child }, paramTypes);
212            
213            }
214        }
215    
216    
217        /**
218         * Render a printable version of this Rule.
219         */
220        public String toString() {
221    
222            StringBuffer sb = new StringBuffer("SetRootRule[");
223            sb.append("methodName=");
224            sb.append(methodName);
225            sb.append(", paramType=");
226            sb.append(paramType);
227            sb.append("]");
228            return (sb.toString());
229    
230        }
231    
232    
233    }