1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18
19 package org.apache.tomcat.util.digester;
20
21 import org.apache.tomcat.util.IntrospectionUtils;
22
23
24 /**
25  * <p>Rule implementation that calls a method on the (top-1) (parent)
26  * object, passing the top object (child) as an argument.  It is
27  * commonly used to establish parent-child relationships.</p>
28  *
29  * <p>This rule now supports more flexible method matching by default.
30  * It is possible that this may break (some) code
31  * written against release 1.1.1 or earlier.
32  * See {@link #isExactMatch()} for more details.</p>
33  */

34
35 public class SetNextRule extends Rule {
36
37     // ----------------------------------------------------------- Constructors
38
39     /**
40      * Construct a "set next" rule with the specified method name.
41      *
42      * @param methodName Method name of the parent method to call
43      * @param paramType Java class of the parent method's argument
44      *  (if you wish to use a primitive type, specify the corresponding
45      *  Java wrapper class instead, such as <code>java.lang.Boolean</code>
46      *  for a <code>boolean</code> parameter)
47      */

48     public SetNextRule(String methodName,
49                        String paramType) {
50
51         this.methodName = methodName;
52         this.paramType = paramType;
53
54     }
55
56
57     // ----------------------------------------------------- Instance Variables
58
59
60     /**
61      * The method name to call on the parent object.
62      */

63     protected String methodName = null;
64
65
66     /**
67      * The Java class name of the parameter type expected by the method.
68      */

69     protected String paramType = null;
70
71     /**
72      * Should we use exact matching. Default is no.
73      */

74     protected boolean useExactMatch = false;
75
76     // --------------------------------------------------------- Public Methods
77
78
79     /**
80      * <p>Is exact matching being used.</p>
81      *
82      * <p>This rule uses <code>org.apache.commons.beanutils.MethodUtils</code>
83      * to introspect the relevant objects so that the right method can be called.
84      * Originally, <code>MethodUtils.invokeExactMethod</code> was used.
85      * This matches methods very strictly
86      * and so may not find a matching method when one exists.
87      * This is still the behaviour when exact matching is enabled.</p>
88      *
89      * <p>When exact matching is disabled, <code>MethodUtils.invokeMethod</code> is used.
90      * This method finds more methods but is less precise when there are several methods
91      * with correct signatures.
92      * So, if you want to choose an exact signature you might need to enable this property.</p>
93      *
94      * <p>The default setting is to disable exact matches.</p>
95      *
96      * @return true iff exact matching is enabled
97      * @since Digester Release 1.1.1
98      */

99     public boolean isExactMatch() {
100
101         return useExactMatch;
102     }
103
104     /**
105      * <p>Set whether exact matching is enabled.</p>
106      *
107      * <p>See {@link #isExactMatch()}.</p>
108      *
109      * @param useExactMatch should this rule use exact method matching
110      * @since Digester Release 1.1.1
111      */

112     public void setExactMatch(boolean useExactMatch) {
113
114         this.useExactMatch = useExactMatch;
115     }
116
117     /**
118      * Process the end of this element.
119      *
120      * @param namespace the namespace URI of the matching element, or an
121      *   empty string if the parser is not namespace aware or the element has
122      *   no namespace
123      * @param name the local name if the parser is namespace aware, or just
124      *   the element name otherwise
125      */

126     @Override
127     public void end(String namespace, String name) throws Exception {
128
129         // Identify the objects to be used
130         Object child = digester.peek(0);
131         Object parent = digester.peek(1);
132         if (digester.log.isDebugEnabled()) {
133             if (parent == null) {
134                 digester.log.debug("[SetNextRule]{" + digester.match +
135                         "} Call [NULL PARENT]." +
136                         methodName + "(" + child + ")");
137             } else {
138                 digester.log.debug("[SetNextRule]{" + digester.match +
139                         "} Call " + parent.getClass().getName() + "." +
140                         methodName + "(" + child + ")");
141             }
142         }
143
144         // Call the specified method
145         IntrospectionUtils.callMethod1(parent, methodName,
146                 child, paramType, digester.getClassLoader());
147
148     }
149
150
151     /**
152      * Render a printable version of this Rule.
153      */

154     @Override
155     public String toString() {
156         StringBuilder sb = new StringBuilder("SetNextRule[");
157         sb.append("methodName=");
158         sb.append(methodName);
159         sb.append(", paramType=");
160         sb.append(paramType);
161         sb.append("]");
162         return sb.toString();
163     }
164
165
166 }
167