    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    }