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