All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opencms.configuration.CmsSetNextRule Maven / Gradle / Ivy

Go to download

OpenCms is an enterprise-ready, easy to use website content management system based on Java and XML technology. Offering a complete set of features, OpenCms helps content managers worldwide to create and maintain beautiful websites fast and efficiently.

There is a newer version: 17.0
Show newest version
/*
 * This library is part of OpenCms -
 * the Open Source Content Management System
 *
 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * For further information about Alkacon Software GmbH & Co. KG, please see the
 * company website: http://www.alkacon.com
 *
 * For further information about OpenCms, please see the
 * project website: http://www.opencms.org
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *
 * This file is based upon:
 * org.apache.commons.digester3.CallMethodRule.
 *
 * Copyright 2001-2004 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.opencms.configuration;

import org.opencms.file.CmsObject;
import org.opencms.main.CmsLog;

import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.MethodUtils;
import org.apache.commons.digester3.Digester;
import org.apache.commons.digester3.Rule;
import org.apache.commons.logging.Log;

import org.xml.sax.Attributes;

/**
 * Rule implementation that invokes a method on the (top-1) (parent) object,
 * passing as implicit first argument of type {@link org.opencms.file.CmsObject}
 * and as a further argument the top stack instance. 

* * If no subsequent CallParamRule are matched for CmsObject * which is the case in the OpenCms usage the first argument CmsObject * will be null at method invocation time.

* This is an alternative for {@link org.apache.commons.digester3.SetNextRule} * if a parent to child-property configuration has been done but the setter for that * property requires additional arguments that are only available at real runtime * of the application.

* * The top stack element (child) that has to be set is matched against the constructor * given {@link java.lang.Class}[]: It is used as argument on the position * where the Class[] has an instance of the same type as it's own Class.

* * @see org.apache.commons.digester3.CallMethodRule * @see org.apache.commons.digester3.SetNextRule * * @since 6.0.0 */ public class CmsSetNextRule extends Rule { /** The log object of this class. */ private static final Log LOG = CmsLog.getLog(CmsSetNextRule.class); /** * The body text collected from this element. */ protected String m_bodyText; /** * The method name to call on the parent object. */ protected String m_methodName; /** * The number of parameters to collect from MethodParam rules. * If this value is zero, a single parameter will be collected from the * body of this element. */ protected int m_paramCount; /** * The parameter types of the parameters to be collected. */ protected Class[] m_paramTypes; /** * Should MethodUtils.invokeExactMethod be used for reflection. */ protected boolean m_useExactMatch; /** * location of the target object for the call, relative to the * top of the digester object stack. The default value of zero * means the target object is the one on top of the stack. */ private int m_targetOffset; /** * Construct a "call method" rule with the specified method name.

* * * The 1st argument of the method will be of type {@link CmsObject}. * It's value will remain null (except subsequent * {@link org.apache.commons.digester3.CallParamRule} would put a value * which currently is impossible at initialization time within OpenCms).

* * The 2nd argument will be the top-stack element at digestion time. * That instance has to be of the same type as the clazz argument to succeed.

* * * @param methodName Method name of the parent method to call * @param clazz The class of the top-stack element (child) that will be present at digestion-time */ public CmsSetNextRule(String methodName, Class clazz) { this(methodName, new Class[] {clazz}); } /** * Construct a "call method" rule with the specified method name * and additional parameters.

* * * The 1st argument of the method will be of type {@link CmsObject}. * It's value will remain null (except subsequent * {@link org.apache.commons.digester3.CallParamRule} would put a value * which currently is impossible at initialization time within OpenCms).

* * The further arguments will be filled by the subsequent {@link org.apache.commons.digester3.CallParamRule} * matches. If the first Class in the given array matches the top stack element * (child) that value will be used. If at digestion time no parameters are found for the given * types their values for invocation of the method remain null.

* * * @param methodName Method name of the parent method to call * @param clazzes an array with all parameter types for the method to invoke at digestion time */ public CmsSetNextRule(String methodName, Class[] clazzes) { m_targetOffset = 0; m_methodName = methodName; m_paramCount = clazzes.length + 1; m_paramTypes = new Class[m_paramCount]; m_paramTypes[0] = CmsObject.class; System.arraycopy(clazzes, 0, m_paramTypes, 1, clazzes.length); } /** * Process the start of this element. * * @param attributes The attribute list for this element * @param namespace the namespace URI of the matching element, or an empty string if the parser is not namespace * aware or the element has no namespace * @param name the local name if the parser is namespace aware, or just the element name otherwise * @throws Exception if something goes wrong */ @Override public void begin(String namespace, String name, Attributes attributes) throws Exception { // not now: 6.0.0 // digester.setLogger(CmsLog.getLog(digester.getClass())); // Push an array to capture the parameter values if necessary if (m_paramCount > 0) { Object[] parameters = new Object[m_paramCount]; for (int i = 0; i < parameters.length; i++) { parameters[i] = null; } getDigester().pushParams(parameters); } } /** * Process the body text of this element.

* * @param bodyText The body text of this element * @param namespace the namespace URI of the matching element, or an empty string if the parser is not namespace * aware or the element has no namespace * @param name the local name if the parser is namespace aware, or just the element name otherwise * @throws Exception if something goes wrong */ @Override public void body(String namespace, String name, String bodyText) throws Exception { if (m_paramCount == 0) { m_bodyText = bodyText.trim(); } } /** * Process the end of this element.

* * @param namespace the namespace URI of the matching element, or an empty string if the parser is not namespace * aware or the element has no namespace * @param name the local name if the parser is namespace aware, or just the element name otherwise * @throws Exception if something goes wrong */ @Override public void end(String namespace, String name) throws Exception { // Determine the target object for the method call: the parent object Object parent = getDigester().peek(1); Object child = getDigester().peek(0); // Retrieve or construct the parameter values array Object[] parameters = null; if (m_paramCount > 0) { parameters = getDigester().popParams(); if (LOG.isTraceEnabled()) { for (int i = 0, size = parameters.length; i < size; i++) { LOG.trace("[SetNextRuleWithParams](" + i + ")" + parameters[i]); } } // In the case where the target method takes a single parameter // and that parameter does not exist (the CallParamRule never // executed or the CallParamRule was intended to set the parameter // from an attribute but the attribute wasn't present etc) then // skip the method call. // // This is useful when a class has a "default" value that should // only be overridden if data is present in the XML. I don't // know why this should only apply to methods taking *one* // parameter, but it always has been so we can't change it now. if ((m_paramCount == 1) && (parameters[0] == null)) { return; } } else if (m_paramTypes.length != 0) { // Having paramCount == 0 and paramTypes.length == 1 indicates // that we have the special case where the target method has one // parameter being the body text of the current element. // There is no body text included in the source XML file, // so skip the method call if (m_bodyText == null) { return; } parameters = new Object[1]; parameters[0] = m_bodyText; if (m_paramTypes.length == 0) { m_paramTypes = new Class[1]; m_paramTypes[0] = String.class; } } else { // When paramCount is zero and paramTypes.length is zero it // means that we truly are calling a method with no parameters. // Nothing special needs to be done here. parameters = new Object[0]; } // Construct the parameter values array we will need // We only do the conversion if the param value is a String and // the specified paramType is not String. Object[] paramValues = new Object[m_paramTypes.length]; Class propertyClass = child.getClass(); for (int i = 0; i < m_paramTypes.length; i++) { if (m_paramTypes[i] == propertyClass) { // implant the original child to set if Class matches: paramValues[i] = child; } else if ((parameters[i] == null) || ((parameters[i] instanceof String) && !String.class.isAssignableFrom(m_paramTypes[i]))) { // convert nulls and convert stringy parameters // for non-stringy param types if (parameters[i] == null) { paramValues[i] = null; } else { paramValues[i] = ConvertUtils.convert((String)parameters[i], m_paramTypes[i]); } } else { paramValues[i] = parameters[i]; } } if (parent == null) { StringBuffer sb = new StringBuffer(); sb.append("[SetNextRuleWithParams]{"); sb.append(getDigester().getMatch()); sb.append("} Call target is null ("); sb.append("targetOffset="); sb.append(m_targetOffset); sb.append(",stackdepth="); sb.append(getDigester().getCount()); sb.append(")"); throw new org.xml.sax.SAXException(sb.toString()); } // Invoke the required method on the top object if (LOG.isDebugEnabled()) { StringBuffer sb = new StringBuffer("[SetNextRuleWithParams]{"); sb.append(getDigester().getMatch()); sb.append("} Call "); sb.append(parent.getClass().getName()); sb.append("."); sb.append(m_methodName); sb.append("("); for (int i = 0; i < paramValues.length; i++) { if (i > 0) { sb.append(","); } if (paramValues[i] == null) { sb.append("null"); } else { sb.append(paramValues[i].toString()); } sb.append("/"); if (m_paramTypes[i] == null) { sb.append("null"); } else { sb.append(m_paramTypes[i].getName()); } } sb.append(")"); LOG.debug(sb.toString()); } Object result = null; if (m_useExactMatch) { // invoke using exact match result = MethodUtils.invokeExactMethod(parent, m_methodName, paramValues, m_paramTypes); } else { // invoke using fuzzier match result = MethodUtils.invokeMethod(parent, m_methodName, paramValues, m_paramTypes); } processMethodCallResult(result); } /** * Clean up after parsing is complete.

* * @param namespace the namespace URI of the matching element, or an empty string if the parser is not namespace * aware or the element has no namespace * @param name the local name if the parser is namespace aware, or just the element name otherwise * @throws Exception if something goes wrong */ public void finish(String namespace, String name) throws Exception { String dummy = name; dummy = namespace; dummy = null; m_bodyText = dummy; } /** * Returns true if MethodUtils.invokeExactMethod * shall be used for the reflection.

* * @return true if MethodUtils.invokeExactMethod * shall be used for the reflection. */ public boolean getUseExactMatch() { return m_useExactMatch; } /** * Set the associated digester.

* * The digester gets assigned to use the OpenCms conform logging * * If needed, this class loads the parameter classes from their names.

* * @param aDigester the associated digester to set */ @Override public void setDigester(Digester aDigester) { aDigester.setLogger(CmsLog.getLog(aDigester.getClass())); // call superclass super.setDigester(aDigester); } /** * Set the value to use for MethodUtils.invokeExactMethod * to use.

* * @param useExactMatch the value to use for MethodUtils.invokeExactMethod * to use */ public void setUseExactMatch(boolean useExactMatch) { m_useExactMatch = useExactMatch; } /** * Returns a printable version of this Rule.

* * @return a printable version of this Rule */ @Override public String toString() { StringBuffer sb = new StringBuffer("CallMethodRule["); sb.append("methodName="); sb.append(m_methodName); sb.append(", paramCount="); sb.append(m_paramCount); sb.append(", paramTypes={"); if (m_paramTypes != null) { for (int i = 0; i < m_paramTypes.length; i++) { if (i > 0) { sb.append(", "); } sb.append(m_paramTypes[i].getName()); } } sb.append("}"); sb.append("]"); return (sb.toString()); } /** * Subclasses may override this method to perform additional processing of the * invoked method's result.

* * @param result the Object returned by the method invoked, possibly null */ protected void processMethodCallResult(Object result) { // do nothing but to fool checkstyle if (result != null) { // nop } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy