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