    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    }