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