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