KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > opencms > configuration > CmsSetNextRule


1 /*
2  * File : $Source: /usr/local/cvs/opencms/src/org/opencms/configuration/CmsSetNextRule.java,v $
3  * Date : $Date: 2006/03/27 14:52:46 $
4  * Version: $Revision: 1.8 $
5  *
6  * This library is part of OpenCms -
7  * the Open Source Content Mananagement System
8  *
9  * Copyright (c) 2005 Alkacon Software GmbH (http://www.alkacon.com)
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2.1 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  * Lesser General Public License for more details.
20  *
21  * For further information about Alkacon Software GmbH, please see the
22  * company website: http://www.alkacon.com
23  *
24  * For further information about OpenCms, please see the
25  * project website: http://www.opencms.org
26  *
27  * You should have received a copy of the GNU Lesser General Public
28  * License along with this library; if not, write to the Free Software
29  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30  *
31  *
32  * This file is based upon:
33  * org.apache.commons.digester.CallMethodRule.
34  *
35  * Copyright 2001-2004 The Apache Software Foundation.
36  *
37  * Licensed under the Apache License, Version 2.0 (the "License");
38  * you may not use this file except in compliance with the License.
39  * You may obtain a copy of the License at
40  *
41  * http://www.apache.org/licenses/LICENSE-2.0
42  *
43  * Unless required by applicable law or agreed to in writing, software
44  * distributed under the License is distributed on an "AS IS" BASIS,
45  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
46  * See the License for the specific language governing permissions and
47  * limitations under the License.
48  */

49
50 package org.opencms.configuration;
51
52 import org.opencms.file.CmsObject;
53 import org.opencms.main.CmsLog;
54
55 import org.apache.commons.beanutils.ConvertUtils;
56 import org.apache.commons.beanutils.MethodUtils;
57 import org.apache.commons.digester.Digester;
58 import org.apache.commons.digester.Rule;
59 import org.apache.commons.logging.Log;
60
61 import org.xml.sax.Attributes JavaDoc;
62
63 /**
64  * Rule implementation that invokes a method on the (top-1) (parent) object,
65  * passing as implicit first argument of type <code>{@link org.opencms.file.CmsObject}</code>
66  * and as a further argument the top stack instance. <p>
67  *
68  * If no subsequent <code>CallParamRule</code> are matched for <code>CmsObject</code>
69  * which is the case in the OpenCms usage the first argument <code>CmsObject</code>
70  * will be null at method invocation time. <p>
71
72  * This is an alternative for <code>{@link org.apache.commons.digester.SetNextRule}</code>
73  * if a parent to child-property configuration has been done but the setter for that
74  * property requires additional arguments that are only available at real runtime
75  * of the application.<p>
76  *
77  * The top stack element (child) that has to be set is matched against the constructor
78  * given <code>{@link java.lang.Class}[]</code>: It is used as argument on the position
79  * where the <code>Class[]</code> has an instance of the same type as it's own <code>Class</code>.<p>
80  *
81  * @see org.apache.commons.digester.CallMethodRule
82  * @see org.apache.commons.digester.SetNextRule
83  *
84  * @author Craig McClanahan
85  * @author Achim Westermann
86  *
87  * @version $Revision: 1.8 $
88  *
89  * @since 6.0.0
90  */

91
92 public class CmsSetNextRule extends Rule {
93
94     /** The log object of this class. */
95     private static final Log LOG = CmsLog.getLog(CmsSetNextRule.class);
96     /**
97      * The body text collected from this element.
98      */

99     protected String JavaDoc m_bodyText = null;
100
101     /**
102      * The method name to call on the parent object.
103      */

104     protected String JavaDoc m_methodName = null;
105
106     /**
107      * The number of parameters to collect from <code>MethodParam</code> rules.
108      * If this value is zero, a single parameter will be collected from the
109      * body of this element.
110      */

111     protected int m_paramCount = 0;
112
113     /**
114      * The parameter types of the parameters to be collected.
115      */

116     protected Class JavaDoc[] m_paramTypes = null;
117
118     /**
119      * Should <code>MethodUtils.invokeExactMethod</code> be used for reflection.
120      */

121     protected boolean m_useExactMatch = false;
122
123     /**
124      * The names of the classes of the parameters to be collected.
125      * This attribute allows creation of the classes to be postponed until the digester is set.
126      */

127     private String JavaDoc[] m_paramClassNames = null;
128
129     /**
130      * location of the target object for the call, relative to the
131      * top of the digester object stack. The default value of zero
132      * means the target object is the one on top of the stack.
133      */

134     private int m_targetOffset = 0;
135
136     /**
137      * Construct a "call method" rule with the specified method name.<p>
138      *
139      *
140      * The 1<sup>st</sup> argument of the method will be of type <code>{@link CmsObject}</code>.
141      * It's value will remain null (except subsequent
142      * <code>{@link org.apache.commons.digester.CallParamRule}</code> would put a value
143      * which currently is impossible at initialization time within OpenCms).<p>
144      *
145      * The 2<sup>nd</sup> argument will be the top-stack element at digestion time.
146      * That instance has to be of the same type as the <code>clazz</code> argument to succeed.<p>
147      *
148      *
149      * @param methodName Method name of the parent method to call
150      * @param clazz The class of the top-stack element (child) that will be present at digestion-time
151      */

152     public CmsSetNextRule(String JavaDoc methodName, Class JavaDoc clazz) {
153
154         this(methodName, new Class JavaDoc[] {clazz});
155     }
156
157     /**
158      * Construct a "call method" rule with the specified method name
159      * and additional parameters.<p>
160      *
161      *
162      * The 1<sup>st</sup> argument of the method will be of type <code>{@link CmsObject}</code>.
163      * It's value will remain null (except subsequent
164      * <code>{@link org.apache.commons.digester.CallParamRule}</code> would put a value
165      * which currently is impossible at initialization time within OpenCms).<p>
166      *
167      * The further arguments will be filled by the subsequent <code>{@link org.apache.commons.digester.CallParamRule}</code>
168      * matches. If the first <code>Class</code> in the given array matches the top stack element
169      * (child) that value will be used. If at digestion time no parameters are found for the given
170      * types their values for invocation of the method remain null.<p>
171      *
172      *
173      * @param methodName Method name of the parent method to call
174      * @param clazzes an array with all parameter types for the method to invoke at digestion time
175      */

176     public CmsSetNextRule(String JavaDoc methodName, Class JavaDoc[] clazzes) {
177
178         m_targetOffset = 0;
179         m_methodName = methodName;
180         m_paramCount = clazzes.length + 1;
181         m_paramTypes = new Class JavaDoc[m_paramCount];
182         m_paramTypes[0] = CmsObject.class;
183         System.arraycopy(clazzes, 0, m_paramTypes, 1, clazzes.length);
184     }
185
186     /**
187      * Process the start of this element.
188      *
189      * @param attributes The attribute list for this element
190      * @param namespace the namespace URI of the matching element, or an empty string if the parser is not namespace
191      * aware or the element has no namespace
192      * @param name the local name if the parser is namespace aware, or just the element name otherwise
193      * @throws Exception if something goes wrong
194      */

195     public void begin(java.lang.String JavaDoc namespace, java.lang.String JavaDoc name, Attributes JavaDoc attributes) throws Exception JavaDoc {
196
197         // not now: 6.0.0
198
//digester.setLogger(CmsLog.getLog(digester.getClass()));
199

200         // Push an array to capture the parameter values if necessary
201
if (m_paramCount > 0) {
202             Object JavaDoc[] parameters = new Object JavaDoc[m_paramCount];
203             for (int i = 0; i < parameters.length; i++) {
204                 parameters[i] = null;
205             }
206             digester.pushParams(parameters);
207         }
208     }
209
210     /**
211      * Process the body text of this element.<p>
212      *
213      * @param bodyText The body text of this element
214      * @param namespace the namespace URI of the matching element, or an empty string if the parser is not namespace
215      * aware or the element has no namespace
216      * @param name the local name if the parser is namespace aware, or just the element name otherwise
217      * @throws Exception if something goes wrong
218      */

219     public void body(java.lang.String JavaDoc namespace, java.lang.String JavaDoc name, String JavaDoc bodyText) throws Exception JavaDoc {
220
221         if (m_paramCount == 0) {
222             m_bodyText = bodyText.trim();
223         }
224     }
225
226     /**
227      * Process the end of this element.<p>
228      *
229      * @param namespace the namespace URI of the matching element, or an empty string if the parser is not namespace
230      * aware or the element has no namespace
231      * @param name the local name if the parser is namespace aware, or just the element name otherwise
232      * @throws Exception if something goes wrong
233      */

234     public void end(java.lang.String JavaDoc namespace, java.lang.String JavaDoc name) throws Exception JavaDoc {
235
236         // Determine the target object for the method call: the parent object
237
Object JavaDoc parent = digester.peek(1);
238         Object JavaDoc child = digester.peek(0);
239
240         // Retrieve or construct the parameter values array
241
Object JavaDoc[] parameters = null;
242         if (m_paramCount > 0) {
243             parameters = (Object JavaDoc[])digester.popParams();
244             if (LOG.isTraceEnabled()) {
245                 for (int i = 0, size = parameters.length; i < size; i++) {
246                     LOG.trace("[SetNextRuleWithParams](" + i + ")" + parameters[i]);
247                 }
248             }
249
250             // In the case where the target method takes a single parameter
251
// and that parameter does not exist (the CallParamRule never
252
// executed or the CallParamRule was intended to set the parameter
253
// from an attribute but the attribute wasn't present etc) then
254
// skip the method call.
255
//
256
// This is useful when a class has a "default" value that should
257
// only be overridden if data is present in the XML. I don't
258
// know why this should only apply to methods taking *one*
259
// parameter, but it always has been so we can't change it now.
260
if (m_paramCount == 1 && parameters[0] == null) {
261                 return;
262             }
263
264         } else if (m_paramTypes != null && m_paramTypes.length != 0) {
265             // Having paramCount == 0 and paramTypes.length == 1 indicates
266
// that we have the special case where the target method has one
267
// parameter being the body text of the current element.
268

269             // There is no body text included in the source XML file,
270
// so skip the method call
271
if (m_bodyText == null) {
272                 return;
273             }
274
275             parameters = new Object JavaDoc[1];
276             parameters[0] = m_bodyText;
277             if (m_paramTypes.length == 0) {
278                 m_paramTypes = new Class JavaDoc[1];
279                 m_paramTypes[0] = String JavaDoc.class;
280             }
281
282         } else {
283             // When paramCount is zero and paramTypes.length is zero it
284
// means that we truly are calling a method with no parameters.
285
// Nothing special needs to be done here.
286
}
287
288         // Construct the parameter values array we will need
289
// We only do the conversion if the param value is a String and
290
// the specified paramType is not String.
291
Object JavaDoc[] paramValues = new Object JavaDoc[m_paramTypes.length];
292
293         Class JavaDoc propertyClass = child.getClass();
294         for (int i = 0; i < m_paramTypes.length; i++) {
295             if (m_paramTypes[i] == propertyClass) {
296                 // implant the original child to set if Class matches:
297
paramValues[i] = child;
298             } else if (parameters[i] == null
299                 || (parameters[i] instanceof String JavaDoc && !String JavaDoc.class.isAssignableFrom(m_paramTypes[i]))) {
300                 // convert nulls and convert stringy parameters
301
// for non-stringy param types
302
paramValues[i] = ConvertUtils.convert((String JavaDoc)parameters[i], m_paramTypes[i]);
303             } else {
304                 paramValues[i] = parameters[i];
305             }
306         }
307
308         if (parent == null) {
309             StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
310             sb.append("[SetNextRuleWithParams]{");
311             sb.append(digester.getMatch());
312             sb.append("} Call target is null (");
313             sb.append("targetOffset=");
314             sb.append(m_targetOffset);
315             sb.append(",stackdepth=");
316             sb.append(digester.getCount());
317             sb.append(")");
318             throw new org.xml.sax.SAXException JavaDoc(sb.toString());
319         }
320
321         // Invoke the required method on the top object
322
if (LOG.isDebugEnabled()) {
323             StringBuffer JavaDoc sb = new StringBuffer JavaDoc("[SetNextRuleWithParams]{");
324             sb.append(digester.getMatch());
325             sb.append("} Call ");
326             sb.append(parent.getClass().getName());
327             sb.append(".");
328             sb.append(m_methodName);
329             sb.append("(");
330             for (int i = 0; i < paramValues.length; i++) {
331                 if (i > 0) {
332                     sb.append(",");
333                 }
334                 if (paramValues[i] == null) {
335                     sb.append("null");
336                 } else {
337                     sb.append(paramValues[i].toString());
338                 }
339                 sb.append("/");
340                 if (m_paramTypes[i] == null) {
341                     sb.append("null");
342                 } else {
343                     sb.append(m_paramTypes[i].getName());
344                 }
345             }
346             sb.append(")");
347             LOG.debug(sb.toString());
348         }
349
350         Object JavaDoc result = null;
351         if (m_useExactMatch) {
352             // invoke using exact match
353
result = MethodUtils.invokeExactMethod(parent, m_methodName, paramValues, m_paramTypes);
354
355         } else {
356             // invoke using fuzzier match
357
result = MethodUtils.invokeMethod(parent, m_methodName, paramValues, m_paramTypes);
358         }
359
360         processMethodCallResult(result);
361     }
362
363     /**
364      * Clean up after parsing is complete.<p>
365      *
366      * @param namespace the namespace URI of the matching element, or an empty string if the parser is not namespace
367      * aware or the element has no namespace
368      * @param name the local name if the parser is namespace aware, or just the element name otherwise
369      * @throws Exception if something goes wrong
370      */

371     public void finish(String JavaDoc namespace, String JavaDoc name) throws Exception JavaDoc {
372
373         String JavaDoc dummy = name;
374         dummy = namespace;
375         dummy = null;
376         m_bodyText = dummy;
377     }
378
379     /**
380      * Returns true if <code>MethodUtils.invokeExactMethod</code>
381      * shall be used for the reflection.<p>
382      *
383      * @return true if <code>MethodUtils.invokeExactMethod</code>
384      * shall be used for the reflection.
385      */

386     public boolean getUseExactMatch() {
387
388         return m_useExactMatch;
389     }
390
391     /**
392      * Set the associated digester.<p>
393      *
394      * The digester gets assigned to use the OpenCms conform logging
395      *
396      * If needed, this class loads the parameter classes from their names.<p>
397      *
398      * @param aDigester the associated digester to set
399      */

400     public void setDigester(Digester aDigester) {
401
402         aDigester.setLogger(CmsLog.getLog(aDigester.getClass()));
403         // call superclass
404
super.setDigester(aDigester);
405         // if necessary, load parameter classes
406
if (m_paramClassNames != null) {
407             m_paramTypes = new Class JavaDoc[m_paramClassNames.length];
408             for (int i = 0; i < m_paramClassNames.length; i++) {
409                 try {
410                     m_paramTypes[i] = aDigester.getClassLoader().loadClass(m_paramClassNames[i]);
411                 } catch (ClassNotFoundException JavaDoc e) {
412                     // use the digester log
413
LOG.error(Messages.get().getBundle().key(Messages.ERR_LOAD_CLASS_1, m_paramClassNames[i]), e);
414                     m_paramTypes[i] = null; // Will cause NPE later
415
}
416             }
417         }
418     }
419
420     /**
421      * Set the value to use for <code>MethodUtils.invokeExactMethod</code>
422      * to use.<p>
423      *
424      * @param useExactMatch the value to use for <code>MethodUtils.invokeExactMethod</code>
425      * to use
426      */

427     public void setUseExactMatch(boolean useExactMatch) {
428
429         m_useExactMatch = useExactMatch;
430     }
431
432     /**
433      * Returns a printable version of this Rule.<p>
434      *
435      * @return a printable version of this Rule
436      */

437     public String JavaDoc toString() {
438
439         StringBuffer JavaDoc sb = new StringBuffer JavaDoc("CallMethodRule[");
440         sb.append("methodName=");
441         sb.append(m_methodName);
442         sb.append(", paramCount=");
443         sb.append(m_paramCount);
444         sb.append(", paramTypes={");
445         if (m_paramTypes != null) {
446             for (int i = 0; i < m_paramTypes.length; i++) {
447                 if (i > 0) {
448                     sb.append(", ");
449                 }
450                 sb.append(m_paramTypes[i].getName());
451             }
452         }
453         sb.append("}");
454         sb.append("]");
455         return (sb.toString());
456
457     }
458
459     /**
460      * Subclasses may override this method to perform additional processing of the
461      * invoked method's result.
462      *
463      * @param result the Object returned by the method invoked, possibly null
464      */

465     protected void processMethodCallResult(Object JavaDoc result) {
466
467         // do nothing but to fool checkstyle
468
if (result != null) {
469             // nop
470
}
471     }
472 }
Popular Tags