sanders 01/04/16 14:16:30
Added: digester/src/java/org/apache/commons/digester
CallMethodRule.java CallParamRule.java
Digester.java ObjectCreateRule.java Rule.java
SetNextRule.java SetPropertiesRule.java
SetPropertyRule.java SetTopRule.java package.html
Log:
Initial move over from Struts
Revision Changes Path
1.1
jakarta-commons-sandbox/digester/src/java/org/apache/commons/digester/CallMethodRule.java
Index: CallMethodRule.java
===================================================================
/*
* $Header:
/home/cvs/jakarta-commons-sandbox/digester/src/java/org/apache/commons/digester/CallMethodRule.java,v
1.1 2001/04/16 21:16:29 sanders Exp $
* $Revision: 1.1 $
* $Date: 2001/04/16 21:16:29 $
*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999-2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Commons", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact [EMAIL PROTECTED]
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.commons.digester;
import java.lang.reflect.Method;
import org.xml.sax.AttributeList;
import org.apache.struts.util.ConvertUtils;
/**
* Rule implementation that calls a method on the top (parent)
* object, passing arguments collected from subsequent
* <code>CallParamRule</code> rules or from the body of this
* element.
*
* @author Craig McClanahan
* @version $Revision: 1.1 $ $Date: 2001/04/16 21:16:29 $
*/
public class CallMethodRule extends Rule {
// ----------------------------------------------------------- Constructors
/**
* Construct a "call method" rule with the specified method name. The
* parameter types (if any) default to java.lang.String.
*
* @param digester The associated Digester
* @param methodName Method name of the parent method to call
* @param paramCount The number of parameters to collect, or
* zero for a single argument from the body of this element.
*/
public CallMethodRule(Digester digester, String methodName,
int paramCount) {
this(digester, methodName, paramCount, (Class[]) null);
}
/**
* Construct a "call method" rule with the specified method name.
*
* @param digester The associated Digester
* @param methodName Method name of the parent method to call
* @param paramCount The number of parameters to collect, or
* zero for a single argument from the body of ths element
* @param paramTypes The Java class names of the arguments
* (if you wish to use a primitive type, specify the corresonding
* Java wrapper class instead, such as <code>java.lang.Boolean</code>
* for a <code>boolean</code> parameter)
*/
public CallMethodRule(Digester digester, String methodName,
int paramCount, String paramTypes[]) {
super(digester);
this.methodName = methodName;
this.paramCount = paramCount;
if (paramTypes == null) {
this.paramTypes = new Class[paramCount];
for (int i = 0; i < this.paramTypes.length; i++)
this.paramTypes[i] = "abc".getClass();
} else {
this.paramTypes = new Class[paramTypes.length];
for (int i = 0; i < this.paramTypes.length; i++) {
try {
this.paramTypes[i] = Class.forName(paramTypes[i]);
} catch (ClassNotFoundException e) {
this.paramTypes[i] = null; // Will cause NPE later
}
}
}
}
/**
* Construct a "call method" rule with the specified method name.
*
* @param digester The associated Digester
* @param methodName Method name of the parent method to call
* @param paramCount The number of parameters to collect, or
* zero for a single argument from the body of ths element
* @param paramTypes The Java classes that represent the
* parameter types of the method arguments
* (if you wish to use a primitive type, specify the corresonding
* Java wrapper class instead, such as <code>java.lang.Boolean.TYPE</code>
* for a <code>boolean</code> parameter)
*/
public CallMethodRule(Digester digester, String methodName,
int paramCount, Class paramTypes[]) {
super(digester);
this.methodName = methodName;
this.paramCount = paramCount;
if (paramTypes == null) {
this.paramTypes = new Class[paramCount];
for (int i = 0; i < this.paramTypes.length; i++)
this.paramTypes[i] = "abc".getClass();
} else {
this.paramTypes = new Class[paramTypes.length];
for (int i = 0; i < this.paramTypes.length; i++)
this.paramTypes[i] = paramTypes[i];
}
}
// ----------------------------------------------------- Instance Variables
/**
* The body text collected from this element.
*/
protected String bodyText = null;
/**
* The method name to call on the parent object.
*/
protected String methodName = null;
/**
* The number of parameters to collect from <code>MethodParam</code> rules.
* If this value is zero, a single parameter will be collected from the
* body of this element.
*/
protected int paramCount = 0;
/**
* The parameter types of the parameters to be collected.
*/
protected Class paramTypes[] = null;
// --------------------------------------------------------- Public Methods
/**
* Process the start of this element.
*
* @param attributes The attribute list for this element
*/
public void begin(AttributeList attributes) throws Exception {
// Push an array to capture the parameter values if necessary
if (paramCount > 0) {
String parameters[] = new String[paramCount];
for (int i = 0; i < parameters.length; i++)
parameters[i] = null;
digester.push(parameters);
}
}
/**
* Process the body text of this element.
*
* @param bodyText The body text of this element
*/
public void body(String bodyText) throws Exception {
if (paramCount == 0)
this.bodyText = bodyText;
}
/**
* Process the end of this element.
*/
public void end() throws Exception {
// Retrieve or construct the parameter values array
String parameters[] = null;
if (paramCount > 0)
parameters = (String[]) digester.pop();
else {
parameters = new String[1];
parameters[0] = bodyText;
if (paramTypes.length == 0) {
paramTypes = new Class[1];
paramTypes[0] = "abc".getClass();
}
}
// Construct the parameter values array we will need
Object paramValues[] = new Object[paramTypes.length];
for (int i = 0; i < paramTypes.length; i++)
paramValues[i] =
ConvertUtils.convert(parameters[i], paramTypes[i]);
// Invoke the required method on the top object
Object top = digester.peek();
if (digester.getDebug() >= 1) {
StringBuffer sb = new StringBuffer("Call ");
sb.append(top.getClass().getName());
sb.append(".");
sb.append(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 (paramTypes[i] == null)
sb.append("null");
else
sb.append(paramTypes[i].getName());
}
sb.append(")");
digester.log(sb.toString());
}
Method method = top.getClass().getMethod(methodName, paramTypes);
method.invoke(top, paramValues);
}
/**
* Clean up after parsing is complete.
*/
public void finish() throws Exception {
bodyText = null;
methodName = null;
paramCount = 0;
paramTypes = null;
}
}
1.1
jakarta-commons-sandbox/digester/src/java/org/apache/commons/digester/CallParamRule.java
Index: CallParamRule.java
===================================================================
/*
* $Header:
/home/cvs/jakarta-commons-sandbox/digester/src/java/org/apache/commons/digester/CallParamRule.java,v
1.1 2001/04/16 21:16:29 sanders Exp $
* $Revision: 1.1 $
* $Date: 2001/04/16 21:16:29 $
*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999-2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Commons", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact [EMAIL PROTECTED]
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.commons.digester;
import java.lang.reflect.Method;
import org.xml.sax.AttributeList;
/**
* Rule implementation that saves a parameter from either an attribute of
* this element, or from the element body, to be used in a call generated
* by a surrounding CallMethodRule rule.
*
* @author Craig McClanahan
* @version $Revision: 1.1 $ $Date: 2001/04/16 21:16:29 $
*/
public class CallParamRule extends Rule {
// ----------------------------------------------------------- Constructors
/**
* Construct a "call parameter" rule that will save the body text of this
* element as the parameter value.
*
* @param digester The associated Digester
* @param paramIndex The zero-relative parameter number
*/
public CallParamRule(Digester digester, int paramIndex) {
this(digester, paramIndex, null);
}
/**
* Construct a "call parameter" rule that will save the value of the
* specified attribute as the parameter value.
*
* @param digester The associated Digester
* @param paramIndex The zero-relative parameter number
* @param attributeName The name of the attribute to save
*/
public CallParamRule(Digester digester, int paramIndex,
String attributeName) {
super(digester);
this.paramIndex = paramIndex;
this.attributeName = attributeName;
}
// ----------------------------------------------------- Instance Variables
/**
* The attribute from which to save the parameter value
*/
protected String attributeName = null;
/**
* The body text collected from this element.
*/
protected String bodyText = null;
/**
* The zero-relative index of the parameter we are saving.
*/
protected int paramIndex = 0;
// --------------------------------------------------------- Public Methods
/**
* Process the start of this element.
*
* @param attributes The attribute list for this element
*/
public void begin(AttributeList attributes) throws Exception {
if (attributeName != null)
bodyText = attributes.getValue(attributeName);
}
/**
* Process the body text of this element.
*
* @param bodyText The body text of this element
*/
public void body(String bodyText) throws Exception {
if (attributeName == null)
this.bodyText = bodyText.trim();
}
/**
* Process the end of this element.
*/
public void end() throws Exception {
String parameters[] = (String[]) digester.peek();
parameters[paramIndex] = bodyText;
}
/**
* Clean up after parsing is complete.
*/
public void finish() throws Exception {
attributeName = null;
bodyText = null;
}
}
1.1
jakarta-commons-sandbox/digester/src/java/org/apache/commons/digester/Digester.java
Index: Digester.java
===================================================================
/*
* $Header:
/home/cvs/jakarta-commons-sandbox/digester/src/java/org/apache/commons/digester/Digester.java,v
1.1 2001/04/16 21:16:29 sanders Exp $
* $Revision: 1.1 $
* $Date: 2001/04/16 21:16:29 $
*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999-2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Commons", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact [EMAIL PROTECTED]
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.commons.digester;
import java.io.File;
import java.io.InputStream;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.EmptyStackException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.apache.struts.util.ArrayStack;
import org.xml.sax.AttributeList;
import org.xml.sax.DocumentHandler;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.HandlerBase;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
/**
* <p>A <strong>Digester</strong> processes an XML input stream by matching a
* series of element nesting patterns to execute Rules that have been added
* prior to the start of parsing. This package was inspired by the
* <code>XmlMapper</code> class that was part of Tomcat 3.0 and 3.1,
* but is organized somewhat differently.</p>
*
* <p>See the <a href="package-summary.html#package_description">Digester
* Developer Guide</a> for more information.</p>
*
* <p><strong>IMPLEMENTATION NOTE</strong> - A single Digester instance may
* only be used within the context of a single thread at a time, and a call
* to <code>parse()</code> must be completed before another can be initiated
* even from the same thread.</p>
*
* @author Craig McClanahan
* @version $Revision: 1.1 $ $Date: 2001/04/16 21:16:29 $
*/
public class Digester extends HandlerBase {
// --------------------------------------------------------- Constructors
/**
* Construct a new Digester with default properties.
*/
public Digester() {
super();
}
// --------------------------------------------------- Instance Variables
/**
* The body text of the current element.
*/
protected StringBuffer bodyText = new StringBuffer();
/**
* The stack of body text string buffers for surrounding elements.
*/
protected ArrayStack bodyTexts = new ArrayStack();
/**
* The debugging detail level of this component.
*/
protected int debug = 0;
/**
* The URLs of DTDs that have been registered, keyed by the public
* identifier that corresponds.
*/
protected HashMap dtds = new HashMap();
/**
* The application-supplied error handler that is notified when parsing
* warnings, errors, or fatal errors occur.
*/
protected ErrorHandler errorHandler = null;
/**
* The Locator associated with our parser.
*/
protected Locator locator = null;
/**
* The current match pattern for nested element processing.
*/
protected String match = "";
/**
* The SAXParser we will use to parse the input stream.
*/
protected SAXParser parser = null;
/**
* The "root" element of the stack (in other words, the last object
* that was popped.
*/
protected Object root = null;
/**
* The set of Rules that have been registered with this Digester. The
* key is the matching pattern against the current element stack, and
* the value is a List containing the Rules for that pattern, in the
* order that they were registered.
*/
protected HashMap rules = new HashMap();
/**
* The object stack being constructed.
*/
protected ArrayStack stack = new ArrayStack();
/**
* Do we want to use a validating parser?
*/
protected boolean validating = false;
// ----------------------------------------------------------- Properties
/**
* Return the current depth of the element stack.
*/
public int getCount() {
return (stack.size());
}
/**
* Return the debugging detail level of this Digester.
*/
public int getDebug() {
return (this.debug);
}
/**
* Set the debugging detail level of this Digester.
*
* @param debug The new debugging detail level
*/
public void setDebug(int debug) {
this.debug = debug;
}
/**
* Return the error handler for this Digester.
*/
public ErrorHandler getErrorHandler() {
return (this.errorHandler);
}
/**
* Set the error handler for this Digester.
*
* @param errorHandler The new error handler
*/
public void setErrorHandler(ErrorHandler errorHandler) {
this.errorHandler = errorHandler;
}
/**
* Return the SAXParser we will use to parse the input stream. If there
* is a problem creating the parser, return <code>null</code>.
*/
public SAXParser getParser() {
// Return the parser we already created (if any)
if (parser != null)
return (parser);
// Create and return a new parser
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(false);
factory.setValidating(validating);
parser = factory.newSAXParser();
return (parser);
} catch (Exception e) {
log("Digester.getParser: ", e);
return (null);
}
}
/**
* Return the validating parser flag.
*/
public boolean getValidating() {
return (this.validating);
}
/**
* Set the validating parser flag. This must be called before
* <code>parse()</code> is called the first time.
*
* @param validating The new validating parser flag.
*/
public void setValidating(boolean validating) {
this.validating = validating;
}
// ---------------------------------------------- DocumentHandler Methods
/**
* Process notification of character data received from the body of
* an XML element.
*
* @param buffer The characters from the XML document
* @param start Starting offset into the buffer
* @param length Number of characters from the buffer
*
* @exception SAXException if a parsing error is to be reported
*/
public void characters(char buffer[], int start, int length)
throws SAXException {
// if (debug >= 3)
// log("characters(" + new String(buffer, start, length) + ")");
bodyText.append(buffer, start, length);
}
/**
* Process notification of the end of the document being reached.
*
* @exception SAXException if a parsing error is to be reported
*/
public void endDocument() throws SAXException {
// if (debug >= 3)
// log("endDocument()");
if (getCount() > 1)
log("endDocument(): " + getCount() + " elements left");
while (getCount() > 1)
pop();
// Fire "finish" events for all defined rules
Iterator keys = this.rules.keySet().iterator();
while (keys.hasNext()) {
String key = (String) keys.next();
List rules = (List) this.rules.get(key);
for (int i = 0; i < rules.size(); i++) {
try {
((Rule) rules.get(i)).finish();
} catch (Exception e) {
log("Finish event threw exception", e);
throw new SAXException(e);
}
}
}
// Perform final cleanup
clear();
}
/**
* Process notification of the end of an XML element being reached.
*
* @param name Name of the element that is ending
*
* @exception SAXException if a parsing error is to be reported
*/
public void endElement(String name) throws SAXException {
// if (debug >= 3)
// log("endElement(" + match + ")");
List rules = getRules(match);
// Fire "body" events for all relevant rules
if (rules != null) {
// if (debug >= 3)
// log(" Firing 'body' events for " + rules.size() + " rules");
String bodyText = this.bodyText.toString();
for (int i = 0; i < rules.size(); i++) {
try {
((Rule) rules.get(i)).body(bodyText);
} catch (Exception e) {
log("Body event threw exception", e);
throw new SAXException(e);
}
}
}
// Recover the body text from the surrounding element
bodyText = (StringBuffer) bodyTexts.pop();
// Fire "end" events for all relevant rules in reverse order
if (rules != null) {
// if (debug >= 3)
// log(" Firing 'end' events for " + rules.size() + " rules");
for (int i = 0; i < rules.size(); i++) {
int j = (rules.size() - i) - 1;
try {
((Rule) rules.get(j)).end();
} catch (Exception e) {
log("End event threw exception", e);
throw new SAXException(e);
}
}
}
// Recover the previous match expression
int slash = match.lastIndexOf('/');
if (slash >= 0)
match = match.substring(0, slash);
else
match = "";
}
/**
* Process notification of ignorable whitespace received from the body of
* an XML element.
*
* @param buffer The characters from the XML document
* @param start Starting offset into the buffer
* @param length Number of characters from the buffer
*
* @exception SAXException if a parsing error is to be reported
*/
public void ignorableWhitespace(char buffer[], int start, int len)
throws SAXException {
// if (debug >= 3)
// log("ignorableWhitespace(" +
// new String(buffer, start, len) + ")");
; // No processing required
}
/**
* Process notification of a processing instruction that was encountered.
*
* @param target The processing instruction target
* @param data The processing instruction data (if any)
*
* @exception SAXException if a parsing error is to be reported
*/
public void processingInstruction(String target, String data)
throws SAXException {
// if (debug >= 3)
// log("processingInstruction('" + target + "', '" + data + "')");
; // No processing is required
}
/**
* Set the document locator associated with our parser.
*
* @param locator The new locator
*/
public void setDocumentLocator(Locator locator) {
// if (debug >= 3)
// log("setDocumentLocator()");
this.locator = locator;
}
/**
* Process notification of the beginning of the document being reached.
*
* @exception SAXException if a parsing error is to be reported
*/
public void startDocument() throws SAXException {
// if (debug >= 3)
// log("startDocument()");
}
/**
* Process notification of the start of an XML element being reached.
*
* @param name Name of the element that is starting
* @param list The attributes associated with this element
*
* @exception SAXException if a parsing error is to be reported
*/
public void startElement(String name, AttributeList list)
throws SAXException {
// Save the body text accumulated for our surrounding element
bodyTexts.push(bodyText);
bodyText.setLength(0);
// Compute the current matching rule
if (match.length() > 0)
match += "/" + name;
else
match = name;
// if (debug >= 3)
// log("startElement(" + match + ")");
// Fire "begin" events for all relevant rules
List rules = getRules(match);
if (rules != null) {
// if (debug >= 3)
// log(" Firing 'begin' events for " + rules.size() + " rules");
String bodyText = this.bodyText.toString();
for (int i = 0; i < rules.size(); i++) {
try {
((Rule) rules.get(i)).begin(list);
} catch (Exception e) {
log("Begin event threw exception", e);
throw new SAXException(e);
}
}
}
}
// --------------------------------------------------- DTDHandler Methods
/**
* Receive notification of a notation declaration event.
*
* @param name The notation name
* @param publicId The public identifier (if any)
* @param systemId The system identifier (if any)
*/
public void notationDecl(String name, String publicId, String systemId) {
if (debug >= 1)
log("notationDecl('" + name + "', '" + publicId + "', '" +
systemId + "')");
}
/**
* Receive notification of an unparsed entity declaration event.
*
* @param name The unparsed entity name
* @param publicId The public identifier (if any)
* @param systemId The system identifier (if any)
* @param notation The name of the associated notation
*/
public void unparsedEntityDecl(String name, String publicId,
String systemId, String notation) {
if (debug >= 1)
log("unparsedEntityDecl('" + name + "', '" + publicId + "', '" +
systemId + "', '" + notation + "')");
}
// ----------------------------------------------- EntityResolver Methods
/**
* Resolve the requested external entity.
*
* @param publicId The public identifier of the entity being referenced
* @param systemId The system identifier of the entity being referenced
*
* @exception SAXException if a parsing exception occurs
*/
public InputSource resolveEntity(String publicId, String systemId)
throws SAXException {
if (debug >= 1)
log("resolveEntity('" + publicId + "', '" + systemId + "')");
// Has this system identifier been registered?
String dtdURL = null;
if (publicId != null)
dtdURL = (String) dtds.get(publicId);
if (dtdURL == null) {
if (debug >= 1)
log(" Not registered, use system identifier");
return (null);
}
// Return an input source to our alternative URL
if (debug >= 1)
log(" Resolving to alternate DTD '" + dtdURL + "'");
try {
URL url = new URL(dtdURL);
InputStream stream = url.openStream();
return (new InputSource(stream));
} catch (Exception e) {
throw new SAXException(e);
}
}
// ------------------------------------------------- ErrorHandler Methods
/**
* Forward notification of a parsing error to the application supplied
* error handler (if any).
*
* @param exception The error information
*
* @exception SAXException if a parsing exception occurs
*/
public void error(SAXParseException exception) throws SAXException {
log("Parse Error at line " + exception.getLineNumber() +
" column " + exception.getColumnNumber() + ": " +
exception.getMessage(), exception);
if (errorHandler != null)
errorHandler.error(exception);
}
/**
* Forward notification of a fatal parsing error to the application
* supplied error handler (if any).
*
* @param exception The fatal error information
*
* @exception SAXException if a parsing exception occurs
*/
public void fatalError(SAXParseException exception) throws SAXException {
log("Parse Fatal Error at line " + exception.getLineNumber() +
" column " + exception.getColumnNumber() + ": " +
exception.getMessage(), exception);
if (errorHandler != null)
errorHandler.fatalError(exception);
}
/**
* Forward notification of a parse warning to the application supplied
* error handler (if any).
*
* @param exception The warning information
*
* @exception SAXException if a parsing exception occurs
*/
public void warning(SAXParseException exception) throws SAXException {
log("Parse Warning at line " + exception.getLineNumber() +
" column " + exception.getColumnNumber() + ": " +
exception.getMessage(), exception);
if (errorHandler != null)
errorHandler.warning(exception);
}
// ------------------------------------------------------ Logging Methods
/**
* Log a message to the log writer associated with this context.
*
* @param message The message to be logged
*/
public void log(String message) {
System.out.println(message);
}
/**
* Log a message and associated exception to the log writer
* associated with this context.
*
* @param message The message to be logged
* @param exception The associated exception to be logged
*/
public void log(String message, Throwable exception) {
System.out.println(message);
exception.printStackTrace(System.out);
}
// ------------------------------------------------------- Public Methods
/**
* Parse the content of the specified file using this Digester. Returns
* the root element from the object stack (if any).
*
* @param file File containing the XML data to be parsed
*
* @exception IOException if an input/output error occurs
* @exception SAXException if a parsing exception occurs
*/
public Object parse(File file) throws IOException, SAXException {
getParser().parse(file, this);
return (root);
}
/**
* Parse the content of the specified input source using this Digester.
* Returns the root element from the object stack (if any).
*
* @param input Input source containing the XML data to be parsed
*
* @exception IOException if an input/output error occurs
* @exception SAXException if a parsing exception occurs
*/
public Object parse(InputSource input) throws IOException, SAXException {
getParser().parse(input, this);
return (root);
}
/**
* Parse the content of the specified input stream using this Digester.
* Returns the root element from the object stack (if any).
*
* @param input Input stream containing the XML data to be parsed
*
* @exception IOException if an input/output error occurs
* @exception SAXException if a parsing exception occurs
*/
public Object parse(InputStream input) throws IOException, SAXException {
getParser().parse(input, this);
return (root);
}
/**
* Parse the content of the specified URI using this Digester.
* Returns the root element from the object stack (if any).
*
* @param uri URI containing the XML data to be parsed
*
* @exception IOException if an input/output error occurs
* @exception SAXException if a parsing exception occurs
*/
public Object parse(String uri) throws IOException, SAXException {
getParser().parse(uri, this);
return (root);
}
/**
* Register the specified DTD URL for the specified public identifier.
* This must be called before the first call to <code>parse()</code>.
*
* @param publicId Public identifier of the DTD to be resolved
* @param dtdURL The URL to use for reading this DTD
*/
public void register(String publicId, String dtdURL) {
if (debug >= 1)
log("register('" + publicId + "', '" + dtdURL + "'");
dtds.put(publicId, dtdURL);
}
// --------------------------------------------------------- Rule Methods
/**
* Register a new Rule matching the specified pattern.
*
* @param pattern Element matching pattern
* @param rule Rule to be registered
*/
public void addRule(String pattern, Rule rule) {
List list = (List) rules.get(pattern);
if (list == null) {
list = new ArrayList();
rules.put(pattern, list);
}
list.add(rule);
}
/**
* Add an "call method" rule for the specified parameters.
*
* @param pattern Element matching pattern
* @param methodName Method name to be called
* @param paramCount Number of expected parameters (or zero
* for a single parameter from the body of this element)
*/
public void addCallMethod(String pattern, String methodName,
int paramCount) {
addRule(pattern,
new CallMethodRule(this, methodName, paramCount));
}
/**
* Add an "call method" rule for the specified parameters.
*
* @param pattern Element matching pattern
* @param methodName Method name to be called
* @param paramCount Number of expected parameters (or zero
* for a single parameter from the body of this element)
* @param paramTypes Set of Java class names for the types
* of the expected parameters
* (if you wish to use a primitive type, specify the corresonding
* Java wrapper class instead, such as <code>java.lang.Boolean</code>
* for a <code>boolean</code> parameter)
*/
public void addCallMethod(String pattern, String methodName,
int paramCount, String paramTypes[]) {
addRule(pattern,
new CallMethodRule(this, methodName,
paramCount, paramTypes));
}
/**
* Add an "call method" rule for the specified parameters.
*
* @param pattern Element matching pattern
* @param methodName Method name to be called
* @param paramCount Number of expected parameters (or zero
* for a single parameter from the body of this element)
* @param paramTypes The Java class names of the arguments
* (if you wish to use a primitive type, specify the corresonding
* Java wrapper class instead, such as <code>java.lang.Boolean</code>
* for a <code>boolean</code> parameter)
*/
public void addCallMethod(String pattern, String methodName,
int paramCount, Class paramTypes[]) {
addRule(pattern,
new CallMethodRule(this, methodName,
paramCount, paramTypes));
}
/**
* Add a "call parameter" rule for the specified parameters.
*
* @param pattern Element matching pattern
* @param paramIndex Zero-relative parameter index to set
* (from the body of this element)
*/
public void addCallParam(String pattern, int paramIndex) {
addRule(pattern,
new CallParamRule(this, paramIndex));
}
/**
* Add a "call parameter" rule for the specified parameters.
*
* @param pattern Element matching pattern
* @param paramIndex Zero-relative parameter index to set
* (from the specified attribute)
* @param attributeName Attribute whose value is used as the
* parameter value
*/
public void addCallParam(String pattern, int paramIndex,
String attributeName) {
addRule(pattern,
new CallParamRule(this, paramIndex, attributeName));
}
/**
* Add an "object create" rule for the specified parameters.
*
* @param pattern Element matching pattern
* @param className Java class name to be created
*/
public void addObjectCreate(String pattern, String className) {
addRule(pattern,
new ObjectCreateRule(this, className));
}
/**
* Add an "object create" rule for the specified parameters.
*
* @param pattern Element matching pattern
* @param className Default Java class name to be created
* @param attributeName Attribute name that optionally overrides
* the default Java class name to be created
*/
public void addObjectCreate(String pattern, String className,
String attributeName) {
addRule(pattern,
new ObjectCreateRule(this, className, attributeName));
}
/**
* Add a "set next" rule for the specified parameters.
*
* @param pattern Element matching pattern
* @param methodName Method name to call on the parent element
*/
public void addSetNext(String pattern, String methodName) {
addRule(pattern,
new SetNextRule(this, methodName));
}
/**
* Add a "set next" rule for the specified parameters.
*
* @param pattern Element matching pattern
* @param methodName Method name to call on the parent element
* @param paramType Java class name of the expected parameter type
* (if you wish to use a primitive type, specify the corresonding
* Java wrapper class instead, such as <code>java.lang.Boolean</code>
* for a <code>boolean</code> parameter)
*/
public void addSetNext(String pattern, String methodName,
String paramType) {
addRule(pattern,
new SetNextRule(this, methodName, paramType));
}
/**
* Add a "set properties" rule for the specified parameters.
*
* @param pattern Element matching pattern
*/
public void addSetProperties(String pattern) {
addRule(pattern,
new SetPropertiesRule(this));
}
/**
* Add a "set property" rule for the specified parameters.
*
* @param pattern Element matching pattern
* @param name Attribute name containing the property name to be set
* @param value Attribute name containing the property value to set
*/
public void addSetProperty(String pattern, String name, String value) {
addRule(pattern,
new SetPropertyRule(this, name, value));
}
/**
* Add a "set top" rule for the specified parameters.
*
* @param pattern Element matching pattern
* @param methodName Method name to call on the parent element
*/
public void addSetTop(String pattern, String methodName) {
addRule(pattern,
new SetTopRule(this, methodName));
}
/**
* Add a "set top" rule for the specified parameters.
*
* @param pattern Element matching pattern
* @param methodName Method name to call on the parent element
* @param paramType Java class name of the expected parameter type
* (if you wish to use a primitive type, specify the corresonding
* Java wrapper class instead, such as <code>java.lang.Boolean</code>
* for a <code>boolean</code> parameter)
*/
public void addSetTop(String pattern, String methodName,
String paramType) {
addRule(pattern,
new SetTopRule(this, methodName, paramType));
}
// -------------------------------------------------------- Stack Methods
/**
* Clear the current contents of the object stack.
*/
public void clear() {
match = "";
bodyTexts.clear();
stack.clear();
}
/**
* Return the top object on the stack without removing it. If there are
* no objects on the stack, return <code>null</code>.
*/
public Object peek() {
try {
return (stack.peek());
} catch (EmptyStackException e) {
return (null);
}
}
/**
* Return the n'th object down the stack, where 0 is the top element
* and [getCount()-1] is the bottom element. If the specified index
* is out of range, return <code>null</code>.
*
* @param n Index of the desired element, where 0 is the top of the stack,
* 1 is the next element down, and so on.
*/
public Object peek(int n) {
try {
return (stack.peek(n));
} catch (EmptyStackException e) {
return (null);
}
}
/**
* Pop the top object off of the stack, and return it. If there are
* no objects on the stack, return <code>null</code>.
*/
public Object pop() {
try {
return (stack.pop());
} catch (EmptyStackException e) {
return (null);
}
}
/**
* Push a new object onto the top of the object stack.
*
* @param object The new object
*/
public void push(Object object) {
if (stack.size() == 0)
root = object;
stack.push(object);
}
// ------------------------------------------------------ Protected Methods
/**
* Return the set of rules that apply to the specified match position.
* The selected rules are those that match exactly, or those rules
* that specify a suffix match and the tail of the rule matches the
* current match position. Exact matches have precedence over
* suffix matches, then (among suffix matches) the longest match
* is preferred.
*
* @param match The current match position
*/
protected List getRules(String match) {
List rulesList = (List) this.rules.get(match);
if (rulesList == null) {
// Find the longest key, ie more discriminant
String longKey = "";
Iterator keys = this.rules.keySet().iterator();
while (keys.hasNext()) {
String key = (String) keys.next();
if (key.startsWith("*/")) {
if (match.endsWith(key.substring(1))) {
if (key.length() > longKey.length()) {
rulesList = (List) this.rules.get(key);
longKey = key;
}
}
}
}
}
return (rulesList);
}
}
1.1
jakarta-commons-sandbox/digester/src/java/org/apache/commons/digester/ObjectCreateRule.java
Index: ObjectCreateRule.java
===================================================================
/*
* $Header:
/home/cvs/jakarta-commons-sandbox/digester/src/java/org/apache/commons/digester/ObjectCreateRule.java,v
1.1 2001/04/16 21:16:29 sanders Exp $
* $Revision: 1.1 $
* $Date: 2001/04/16 21:16:29 $
*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999-2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Commons", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact [EMAIL PROTECTED]
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.commons.digester;
import org.xml.sax.AttributeList;
/**
* Rule implementation that creates a new object and pushes it
* onto the object stack. When the element is complete, the
* object will be popped
*
* @author Craig McClanahan
* @version $Revision: 1.1 $ $Date: 2001/04/16 21:16:29 $
*/
public class ObjectCreateRule extends Rule {
// ----------------------------------------------------------- Constructors
/**
* Construct an object create rule with the specified class name.
*
* @param digester The associated Digester
* @param className Java class name of the object to be created
*/
public ObjectCreateRule(Digester digester, String className) {
this(digester, className, null);
}
/**
* Construct an object create rule with the specified class name and an
* optional attribute name containing an override.
*
* @param digester The associated Digester
* @param className Java class name of the object to be created
* @param attributeName Attribute name which, if present, contains an
* override of the class name to create
*/
public ObjectCreateRule(Digester digester, String className,
String attributeName) {
super(digester);
this.className = className;
this.attributeName = attributeName;
}
// ----------------------------------------------------- Instance Variables
/**
* The attribute containing an override class name if it is present.
*/
protected String attributeName = null;
/**
* The Java class name of the object to be created.
*/
protected String className = null;
// --------------------------------------------------------- Public Methods
/**
* Process the beginning of this element.
*
* @param attributes The attribute list of this element
*/
public void begin(AttributeList attributes) throws Exception {
// Identify the name of the class to instantiate
String realClassName = className;
if (attributeName != null) {
String value = attributes.getValue(attributeName);
if (value != null)
realClassName = value;
}
if (digester.getDebug() >= 1)
digester.log("New " + realClassName);
// Instantiate the new object and push it on the context stack
Class clazz = Class.forName(realClassName);
Object instance = clazz.newInstance();
digester.push(instance);
}
/**
* Process the end of this element.
*/
public void end() throws Exception {
Object top = digester.pop();
if (digester.getDebug() >= 1)
digester.log("Pop " + top.getClass().getName());
}
/**
* Clean up after parsing is complete.
*/
public void finish() throws Exception {
attributeName = null;
className = null;
}
}
1.1
jakarta-commons-sandbox/digester/src/java/org/apache/commons/digester/Rule.java
Index: Rule.java
===================================================================
/*
* $Header:
/home/cvs/jakarta-commons-sandbox/digester/src/java/org/apache/commons/digester/Rule.java,v
1.1 2001/04/16 21:16:30 sanders Exp $
* $Revision: 1.1 $
* $Date: 2001/04/16 21:16:30 $
*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999-2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Commons", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact [EMAIL PROTECTED]
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.commons.digester;
import org.xml.sax.AttributeList;
/**
* Concrete implementations of this class implement actions to be taken when
* a corresponding nested pattern of XML elements has been matched.
*
* @author Craig McClanahan
* @version $Revision: 1.1 $ $Date: 2001/04/16 21:16:30 $
*/
public abstract class Rule {
// ----------------------------------------------------------- Constructors
/**
* Default constructor sets only the the associated Digester.
*
* @param digester The digester with which this rule is associated
*/
public Rule(Digester digester) {
super();
this.digester = digester;
}
// ----------------------------------------------------- Instance Variables
/**
* The Digester with which this Rule is associated.
*/
protected Digester digester = null;
// --------------------------------------------------------- Public Methods
/**
* This method is called when the beginning of a matching XML element
* is encountered.
*
* @param attributes The attribute list of this element
*/
public void begin(AttributeList attributes) throws Exception {
; // The default implementation does nothing
}
/**
* This method is called when the body of a matching XML element
* is encountered. If the element has no body, this method is
* not called at all.
*
* @param text The text of the body of this element
*/
public void body(String text) throws Exception {
; // The default implementation does nothing
}
/**
* This method is called when the end of a matching XML element
* is encountered.
*/
public void end() throws Exception {
; // The default implementation does nothing
}
/**
* This method is called after all parsing methods have been
* called, to allow Rules to remove temporary data.
*/
public void finish() throws Exception {
; // The default implementation does nothing
}
}
1.1
jakarta-commons-sandbox/digester/src/java/org/apache/commons/digester/SetNextRule.java
Index: SetNextRule.java
===================================================================
/*
* $Header:
/home/cvs/jakarta-commons-sandbox/digester/src/java/org/apache/commons/digester/SetNextRule.java,v
1.1 2001/04/16 21:16:30 sanders Exp $
* $Revision: 1.1 $
* $Date: 2001/04/16 21:16:30 $
*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999-2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Commons", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact [EMAIL PROTECTED]
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.commons.digester;
import java.lang.reflect.Method;
import org.xml.sax.AttributeList;
/**
* Rule implementation that calls a method on the (top-1) (parent)
* object, passing the top object (child) as an argument. It is
* commonly used to establish parent-child relationships.
*
* @author Craig McClanahan
* @version $Revision: 1.1 $ $Date: 2001/04/16 21:16:30 $
*/
public class SetNextRule extends Rule {
// ----------------------------------------------------------- Constructors
/**
* Construct a "set next" rule with the specified method name. The
* method's argument type is assumed to be the class of the
* child object.
*
* @param digester The associated Digester
* @param methodName Method name of the parent method to call
*/
public SetNextRule(Digester digester, String methodName) {
this(digester, methodName, null);
}
/**
* Construct a "set next" rule with the specified method name.
*
* @param digester The associated Digester
* @param methodName Method name of the parent method to call
* @param paramType Java class of the parent method's argument
* (if you wish to use a primitive type, specify the corresonding
* Java wrapper class instead, such as <code>java.lang.Boolean</code>
* for a <code>boolean</code> parameter)
*/
public SetNextRule(Digester digester, String methodName,
String paramType) {
super(digester);
this.methodName = methodName;
this.paramType = paramType;
}
// ----------------------------------------------------- Instance Variables
/**
* The method name to call on the parent object.
*/
protected String methodName = null;
/**
* The Java class name of the parameter type expected by the method.
*/
protected String paramType = null;
// --------------------------------------------------------- Public Methods
/**
* Process the end of this element.
*/
public void end() throws Exception {
// Identify the objects to be used
Object child = digester.peek(0);
Object parent = digester.peek(1);
if (digester.getDebug() >= 1)
digester.log("Call " + parent.getClass().getName() + "." +
methodName + "(" + child + ")");
// Call the specified method
Class paramTypes[] = new Class[1];
if (paramType != null)
paramTypes[0] = Class.forName(paramType);
else
paramTypes[0] = child.getClass();
Method method = parent.getClass().getMethod(methodName, paramTypes);
method.invoke(parent, new Object[] { child });
}
/**
* Clean up after parsing is complete.
*/
public void finish() throws Exception {
methodName = null;
paramType = null;
}
}
1.1
jakarta-commons-sandbox/digester/src/java/org/apache/commons/digester/SetPropertiesRule.java
Index: SetPropertiesRule.java
===================================================================
/*
* $Header:
/home/cvs/jakarta-commons-sandbox/digester/src/java/org/apache/commons/digester/SetPropertiesRule.java,v
1.1 2001/04/16 21:16:30 sanders Exp $
* $Revision: 1.1 $
* $Date: 2001/04/16 21:16:30 $
*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999-2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Commons", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact [EMAIL PROTECTED]
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.commons.digester;
import java.util.HashMap;
import org.xml.sax.AttributeList;
import org.apache.struts.util.BeanUtils;
/**
* Rule implementation that sets properties on the object at the top of the
* stack, based on attributes with corresponding names.
*
* @author Craig McClanahan
* @version $Revision: 1.1 $ $Date: 2001/04/16 21:16:30 $
*/
public class SetPropertiesRule extends Rule {
// ----------------------------------------------------------- Constructors
/**
* Default constructor sets only the the associated Digester.
*
* @param digester The digester with which this rule is associated
*/
public SetPropertiesRule(Digester digester) {
super(digester);
}
// --------------------------------------------------------- Public Methods
/**
* Process the beginning of this element.
*
* @param context The associated context
* @param attributes The attribute list of this element
*/
public void begin(AttributeList attributes) throws Exception {
// Build a set of attribute names and corresponding values
HashMap values = new HashMap();
for (int i = 0; i < attributes.getLength(); i++) {
String name = attributes.getName(i);
String value = attributes.getValue(i);
values.put(name, value);
}
// Populate the corresponding properties of the top object
Object top = digester.peek();
if (digester.getDebug() >= 1)
digester.log("Set " + top.getClass().getName() + " properties");
BeanUtils.populate(top, values);
}
}
1.1
jakarta-commons-sandbox/digester/src/java/org/apache/commons/digester/SetPropertyRule.java
Index: SetPropertyRule.java
===================================================================
/*
* $Header:
/home/cvs/jakarta-commons-sandbox/digester/src/java/org/apache/commons/digester/SetPropertyRule.java,v
1.1 2001/04/16 21:16:30 sanders Exp $
* $Revision: 1.1 $
* $Date: 2001/04/16 21:16:30 $
*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999-2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Commons", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact [EMAIL PROTECTED]
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.commons.digester;
import java.util.HashMap;
import org.xml.sax.AttributeList;
import org.apache.struts.util.BeanUtils;
/**
* Rule implementation that sets an individual property on the object at the
* top of the stack, based on attributes with specified names.
*
* @author Craig McClanahan
* @version $Revision: 1.1 $ $Date: 2001/04/16 21:16:30 $
*/
public class SetPropertyRule extends Rule {
// ----------------------------------------------------------- Constructors
/**
* Construct a "set property" rule with the specified name and value
* attributes.
*
* @param digester The digester with which this rule is associated
* @param name Name of the attribute that will contain the name of the
* property to be set
* @param value Name of the attribute that will contain the value to which
* the property should be set
*/
public SetPropertyRule(Digester digester, String name, String value) {
super(digester);
this.name = name;
this.value = value;
}
// ----------------------------------------------------- Instance Variables
/**
* The attribute that will contain the property name.
*/
protected String name = null;
/**
* The attribute that will contain the property value.
*/
protected String value = null;
// --------------------------------------------------------- Public Methods
/**
* Process the beginning of this element.
*
* @param context The associated context
* @param attributes The attribute list of this element
*/
public void begin(AttributeList attributes) throws Exception {
// Identify the actual property name and value to be used
String actualName = null;
String actualValue = null;
HashMap values = new HashMap();
for (int i = 0; i < attributes.getLength(); i++) {
String name = attributes.getName(i);
String value = attributes.getValue(i);
if (name.equals(this.name))
actualName = value;
else if (name.equals(this.value))
actualValue = value;
}
values.put(actualName, actualValue);
// Populate the corresponding property of the top object
Object top = digester.peek();
if (digester.getDebug() >= 1)
digester.log("Set " + top.getClass().getName() + " property " +
actualName + " to " + actualValue);
BeanUtils.populate(top, values);
}
}
1.1
jakarta-commons-sandbox/digester/src/java/org/apache/commons/digester/SetTopRule.java
Index: SetTopRule.java
===================================================================
/*
* $Header:
/home/cvs/jakarta-commons-sandbox/digester/src/java/org/apache/commons/digester/SetTopRule.java,v
1.1 2001/04/16 21:16:30 sanders Exp $
* $Revision: 1.1 $
* $Date: 2001/04/16 21:16:30 $
*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999-2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Commons", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact [EMAIL PROTECTED]
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.commons.digester;
import java.lang.reflect.Method;
import org.xml.sax.AttributeList;
/**
* Rule implementation that calls a method on the top (parent)
* object, passing the (top-1) (child) object as an argument.
*
* @author Craig McClanahan
* @version $Revision: 1.1 $ $Date: 2001/04/16 21:16:30 $
*/
public class SetTopRule extends Rule {
// ----------------------------------------------------------- Constructors
/**
* Construct a "set parent" rule with the specified method name. The
* parent method's argument type is assumed to be the class of the
* child object.
*
* @param digester The associated Digester
* @param methodName Method name of the parent method to call
*/
public SetTopRule(Digester digester, String methodName) {
this(digester, methodName, null);
}
/**
* Construct a "set parent" rule with the specified method name.
*
* @param digester The associated Digester
* @param methodName Method name of the parent method to call
* @param paramType Java class of the parent method's argument
* (if you wish to use a primitive type, specify the corresonding
* Java wrapper class instead, such as <code>java.lang.Boolean</code>
* for a <code>boolean</code> parameter)
*/
public SetTopRule(Digester digester, String methodName,
String paramType) {
super(digester);
this.methodName = methodName;
this.paramType = paramType;
}
// ----------------------------------------------------- Instance Variables
/**
* The method name to call on the parent object.
*/
protected String methodName = null;
/**
* The Java class name of the parameter type expected by the method.
*/
protected String paramType = null;
// --------------------------------------------------------- Public Methods
/**
* Process the end of this element.
*/
public void end() throws Exception {
// Identify the objects to be used
Object child = digester.peek(1);
Object parent = digester.peek(0);
if (digester.getDebug() >= 1)
digester.log("Call " + parent.getClass().getName() + "." +
methodName + "(" + child + ")");
// Call the specified method
Class paramTypes[] = new Class[1];
if (paramType != null)
paramTypes[0] = Class.forName(paramType);
else
paramTypes[0] = child.getClass();
Method method = parent.getClass().getMethod(methodName, paramTypes);
method.invoke(parent, new Object[] { child });
}
/**
* Clean up after parsing is complete.
*/
public void finish() throws Exception {
methodName = null;
paramType = null;
}
}
1.1
jakarta-commons-sandbox/digester/src/java/org/apache/commons/digester/package.html
Index: package.html
===================================================================
<html>
<head>
<title>Package Documentation for org.apache.struts.digester Package</title>
</head>
<body bgcolor="white">
The Digester package provides for rules-based processing of arbitrary
XML documents.
<br><br>
<a name="doc.Description"></a>
<div align="center">
<a href="#doc.Intro">[Introduction]</a>
<a href="#doc.Properties">[Configuration Properties]</a>
<a href="#doc.Stack">[The Object Stack]</a>
<a href="#doc.Patterns">[Element Matching Patterns]</a>
<a href="#doc.Rules">[Processing Rules]</a>
<a href="#doc.Usage">[Usage Example]</a>
</div>
<a name="doc.Intro"></a>
<h3>Introduction</h3>
<p>In many application environments that deal with XML-formatted data, it is
useful to be able to process an XML document in an "event driven" manner,
where particular Java objects are created (or methods of existing objects
are invoked) when particular patterns of nested XML elements have been
recognized. Developers familiar with the Simple API for XML Parsing (SAX)
approach to processing XML documents will recognize that the Digester provides
a higher level, more developer-friendly interface to SAX events, because most
of the details of navigating the XML element hierarchy are hidden -- allowing
the developer to focus on the processing to be performed.</p>
<p>In order to use a Digester, the following basic steps are required:</p>
<ul>
<li>Create a new instance of the
<code>org.apache.struts.digester.Digester</code> class. Previously
created Digester instances may be safely reused, as long as you have
completed any previously requested parse, and you do not try to utilize
a particular Digester instance from more than one thread at a time.</li>
<li>Set any desired <a href="#doc.Properties">configuration properties</a>
that will customize the operation of the Digester when you next initiate
a parse operation.</li>
<li>Push any desired initial object(s) onto the Digester's
<a href="#doc.Stack">object stack</a>.</li>
<li>Register all of the <a href="#doc.Patterns">element matching patterns</a>
for which you wish to have <a href="#doc.Rules">processing rules</a>
fired when this pattern is recognized in an input document. You may
register as many rules as you like for any particular pattern. If there
is more than one rule for a given pattern, the rules will be executed in
the order that they were listed.</li>
<li>Call the <code>digester.parse()</code> method, passing a reference to the
XML document to be parsed in one of a variety of forms. See the
<a href="Digester.html#parse(java.io.File)">Digester.parse()</a>
documentation for details. Note that you will need to be prepared to
catch any <code>IOException</code> or <code>SAXException</code> that is
thrown by the parser, or any runtime expression that is thrown by one of
the processing rules.</li>
</ul>
<a name="doc.Properties"></a>
<h3>Digester Configuration Properties</h3>
<p>A <code>org.apache.struts.digester.Digester</code> instance contains several
configuration properties that can be used to customize its operation. These
properties <strong>must</strong> be configured before you call one of the
<code>parse()</code> variants, in order for them to take effect on that
parse.</p>
<blockquote>
<table border="1">
<tr>
<th width="15%">Property</th>
<th width="85%">Description</th>
</tr>
<tr>
<td align="center">debug</td>
<td>An integer defining the amount of debugging output that will be
written to <code>System.out()</code> as the parse progresses. This
is useful when tracking down where parsing problems are occurring.
The default value of zero means no debugging output will be generated
-- increasing values generally cause the generation of more verbose
and detailed debugging information.</td>
</tr>
<tr>
<td align="center">validating</td>
<td>A boolean that is set to <code>true</code> if you wish to validate
the XML document against a Document Type Definition (DTD) that is
specified in its <code>DOCTYPE</code> declaration. The default
value of <code>false</code> requests a parse that only detects
"well formed" XML documents, rather than "valid" ones.</td>
</tr>
</table>
</blockquote>
<p>In addition to the scalar properties defined above, you can also register
a local copy of a Document Type Definition (DTD) that is referenced in a
<code>DOCTYPE</code> declaration. Such a registration tells the XML parser
that, whenever it encounters a <code>DOCTYPE</code> declaration with the
specified public identifier, it should utilize the actual DTD content at the
registered system identifier (a URL), rather than the one in the
<code>DOCTYPE</code> declaration.</p>
<p>For example, the Struts framework controller servlet uses the following
registration in order to tell Struts to use a local copy of the DTD for the
Struts configuration file. This allows usage of Struts in environments that
are not connected to the Internet, and speeds up processing even at Internet
connected sites (because it avoids the need to go across the network).</p>
<pre>
digester.register
("-//Apache Software Foundation//DTD Struts Configuration 1.0//EN",
"/org/apache/struts/resources/struts-config_1_0.dtd");
</pre>
<p>As a side note, the system identifier used in this example is the path
that would be passed to <code>java.lang.ClassLoader.getResource()</code>
or <code>java.lang.ClassLoader.getResourceAsStream()</code>. The actual DTD
resource is loaded through the same class loader that loads all of the Struts
classes -- typically from the <code>struts.jar</code> file.</p>
<a name="doc.Stack"></a>
<h3>The Object Stack</h3>
<p>One very common use of <code>org.apache.struts.digester.Digester</code>
technology is to dynamically construct a tree of Java objects, whose internal
organization, as well as the details of property settings on these objects,
are configured based on the contents of the XML document. In fact, the
primary reason that the Digester package was created was to facilitate the
way that the Struts <a href="../action/ActionServlet.html">controller
servlet</a> configures itself based on the contents of your application's
<code>struts-config.xml</code> file.</p>
<p>To facilitate this usage, the Digester exposes a stack that can be
manipulated by processing rules that are fired when element matching patterns
are satisfied. The usual stack-related operations are made available,
including the following:</p>
<ul>
<li><a href="Digester.html#clear()">clear()</a> - Clear the current contents
of the object stack.</li>
<li><a href="Digester.html#peek()">peek()</a> - Return a reference to the top
object on the stack, without removing it.</li>
<li><a href="Digester.html#pop()">pop()</a> - Remove the top object from the
stack and return it.</li>
<li><a href="Digester.html#push(java.lang.Object)">push()</a> - Push a new
object onto the top of the stack.</li>
</ul>
<p>A typical design pattern, then, is to fire a rule that creates a new object
and pushes it on the stack when the beginning of a particular XML element is
encountered. The object will remain there while the nested content of this
element is processed, and it will be popped off when the end of the element
is encountered. As we will see, the standard "object create" processing rule
supports exactly this functionalility in a very convenient way.</p>
<p>Several potential issues with this design pattern are addressed by other
features of the Digester functionality:</p>
<ul>
<li><em>How do I relate the objects being created to each other?</em> - The
Digester supports standard processing rules that pass the top object on
the stack as an argument to a named method on the next-to-top object on
the stack (or vice versa). This rule makes it easy to establish
parent-child relationships between these objects. One-to-one and
one-to-many relationships are both easy to construct.</li>
<li><em>How do I retain a reference to the first object that was created?</em>
As you review the description of what the "object create" processing rule
does, it would appear that the first object you create (i.e. the object
created by the outermost XML element you process) will disappear from the
stack by the time that XML parsing is completed, because the end of the
element would have been encountered. To deal with this, the normal
approach is to push a reference to some application global object onto the
stack before the parse begins, and arrange that a parent-child
relationship be created (by appropriate processing rules) between this
manually pushed object and the one that is dynamically created. In this
way, the pushed object will retain a reference to the dynamically created
object (and therefore all of its children) after the parse finishes.</li>
</ul>
<a name="doc.Patterns"></a>
<h3>Element Matching Patterns</h3>
<p>A primary feature of the <code>org.apache.struts.digester.Digester</code>
parser is that the Digester automatically navigates the element hierarchy of
the XML document you are parsing for you, without requiring any developer
attention to this process. Instead, you focus on deciding what functions you
would like to have performed whenver a certain arrangement of nested elements
is encountered in the XML document being parsed. The mechanism for specifying
such arrangements are called <em>element matching patterns</em>.
<p>A very simple element matching pattern is a simple string like "a". This
pattern is matched whenever an <code><a></code> top-level element is
encountered in the XML document, no matter how many times it occurs. Note that
nested <code><a></code> elements will <strong>not</strong> match this
pattern -- we will describe means to support this kind of matching later.</li>
<p>The next step up in matching pattern complexity is "a/b". This pattern will
be matched when a <code><b></code> element is found nested inside a
top-level <code><a></code> element. Again, this match can occur as many
times as desired, depending on the content of the XML document being parsed.
You can use multiple slashes to define a hierarchy of any desired depth that
will be matched appropriately.</p>
<p>For example, assume you have registered processing rules that match patterns
"a", "a/b", and "a/b/c". For an input XML document with the following
contents, the indicated patterns will be matched when the corresponding element
is parsed:</p>
<pre>
<a> -- Matches pattern "a"
<b> -- Matches pattern "a/b"
<c/> -- Matches pattern "a/b/c"
<c/> -- Matches pattern "a/b/c"
</b>
<b> -- Matches pattern "a/b"
<c/> -- Matches pattern "a/b/c"
<c/> -- Matches pattern "a/b/c"
<c/> -- Matches pattern "a/b/c"
</b>
</a>
</pre>
<p>It is also possible to match a particular XML element, no matter how it is
nested (or not nested) in the XML document, by using the "*" wildcard character
in your matching pattern strings. For example, an element matching pattern
of "*/a" will match an <code><a></code> element at any nesting position
within the document.</p>
<p>It is quite possible that, when a particular XML element is being parsed,
the pattern for more than one registered processing rule will be matched
(either because you registered more than one processing rule with the same
matching pattern, or because one more more exact pattern matches and wildcard
pattern matches are satisfied by the same element. When this occurs, the
corresponding processing rules will all be fired, in the order that they were
initially registered with the Digester.</p>
<a name="doc.Rules"></a>
<h3>Processing Rules</h3>
<p>The <a href="#doc.Patterns">previous section</a> documented how you identify
<strong>when</strong> you wish to have certain actions take place. The purpose
of processing rules is to define <strong>what</strong> should happen when the
patterns are matched.</p>
<p>Formally, a processing rule is a Java class that subclasses the
<a href="Rule.html">org.apache.struts.digester.Rule</a> interface. Each Rule
implements one or more of the following event methods that are called at
well-defined times when the matching patterns corresponding to this rule
trigger it:</p>
<ul>
<li><a href="Rule.html#begin(org.xml.sax.AttributeList)">begin()</a> -
Called when the beginning of the matched XML element is encountered. A
data structure containing all of the attributes corresponding to this
element are passed as well.</li>
<li><a href="Rule.html#body(java.lang.String)">body()</a> -
Called when nested content (that is not itself XML elements) of the
matched element is encountered. Any leading or trailing whitespace will
have been removed as part of the parsing process.</li>
<li><a href="Rule.html#end()">end()</a> - Called when the ending of the matched
XML element is encountered. If nested XML elements that matched other
processing rules was included in the body of this element, the appropriate
processing rules for the matched rules will have already been completed
before this method is called.</li>
<li><a href="Rule.html#finish()">finish()</a> - Called when the parse has
been completed, to give each rule a chance to clean up any temporary data
they might have created and cached.</li>
</ul>
<p>As you are configuring your digester, you can call the
<code>addRule()</code> method to register a specific element matching pattern,
along with an instance of a <code>Rule</code> class that will have its event
handling methods called at the appropriate times, as described above. This
mechanism allows you to create <code>Rule</code> implementation classes
dynamically, to implement any desired application specific functionality.</p>
<p>In addition, a set of processing rule implementation classes are provided,
which deal with many common programming scenarios. These classes include the
following:</p>
<ul>
<li><a href="ObjectCreateRule.html">ObjectCreateRule</a> - When the
<code>begin()</code> method is called, this rule instantiates a new
instance of a specified Java class, and pushes it on the stack. The
class name to be used is defaulted according to a parameter passed to
this rule's constructor, but can optionally be overridden by a classname
passed via the specified attribute to the XML element being processed.
When the <code>end()</code> method is called, the top object on the stack
(presumably, the one we added in the <code>begin()</code> method) will
be popped, and any reference to it (within the Digester) will be
discarded.</li>
<li><a href="SetPropertiesRule.html">SetPropertiesRule</a> - When the
<code>begin()</code> method is called, the digester uses the standard
Java Reflection API to identify any JavaBeans property setter methods
(on the object at the top of the digester's stack)
who have property names that match the attributes specified on this XML
element, and then call them individually, passing the corresponding
attribute values. A very common idiom is to define an object create
rule, followed by a set properties rule, with the same element matching
pattern. This causes the creation of a new Java object, followed by
"configuration" of that object's properties based on the attributes
of the same XML element that created this object.</li>
<li><a href="SetPropertyRule.html">SetPropertyRule</a> - When the
<code>begin()</code> method is called, the digester calls a specified
property setter (where the property itself is named by an attribute)
with a specified value (where the value is named by another attribute),
on the object at the top of the digester's stack.
This is useful when your XML file conforms to a particular DTD, and
you wish to configure a particular property that does not have a
corresponding attribute in the DTD.</li>
<li><a href="SetNextRule.html">SetNextRule</a> - When the
<code>begin()</code> method is called, the digester analyzes the
next-to-top element on the stack, looking for a property setter method
for a specified property. It then calls this method, passing the object
at the top of the stack as an argument. This rule is commonly used to
establish one-to-many relationships between the two objects, with the
method name commonly being something like "addChild".</li>
<li><a href="SetTopRule.html">SetTopRule</a> - When the
<code>begin()</code> method is called, the digester analyzes the
top element on the stack, looking for a property setter method for a
specified property. It then calls this method, passing the next-to-top
object on the stack as an argument. This rule would be used as an
alternative to a SetNextRule, with a typical method name "setParent",
if the API supported by your object classes prefers this approach.</li>
<li><a href="CallMethodRule.html">CallMethodRule</a> - This rule sets up a
method call to a named method of the top object on the digester's stack,
which will actually take place when the <code>end()</code> method is
called. You configure this rule by specifying the name of the method
to be called, the number of arguments it takes, and (optionally) the
Java class name(s) defining the type(s) of the method's arguments.
The actual parameter values, if any, will typically be accumulated from
the body content of nested elements within the element that triggered
this rule, using the CallParamRule discussed next.</li>
<li><a href="CallParamRule.html">CallParamRule</a> - This rule identifies
the source of a particular numbered (zero-relative) parameter for a
CallMethodRule within which we are nested. You can specify that the
parameter value be taken from a particular named attribute, or from the
nested body content of this element.</li>
</ul>
<p>You can create instances of the standard <code>Rule</code> classes and
register them by calling <code>digester.addRule()</code>, as described above.
However, because their usage is so common, shorthand registration methods are
defined for each of the standard rules, directly on the <code>Digester</code>
class. For example, the following code sequence:</p>
<pre>
Rule rule = new SetNextRule(digester, "addChild",
"com.mycompany.mypackage.MyChildClass");
digester.addRule("a/b/c", rule);
</pre>
<p>can be replaced by:</p>
<pre>
digester.addSetNext("a/b/c", "addChild",
"com.mycompany.mypackage.MyChildClass");
</pre>
<a name="doc.Usage"></a>
<h3>Usage Examples</h3>
<h5>Processing The Struts Configuration File</h5>
<p>As stated earlier, the primary reason that the
<code>org.apache.struts.digester.Digester</code> package exists is because the
Struts controller servlet itself needed a robust, flexible, easy to extend
mechanism for processing the contents of the <code>struts-config.xml</code>
configuration that describes nearly every aspect of a Struts-based application.
Because of this, the controller servlet contains a comprehensive, real world,
example of how the Digester can be employed for this type of a use case.
See the <code>initDigester()</code> method of class
<code>org.apache.struts.action.ActionServlet</code> for the code that creates
and configures the Digester to be used, and the <code>initMapping()</code>
method for where the parsing actually takes place.</p>
<p>The following discussion highlights a few of the matching patterns and
processing rules that are configured, to illustrate the use of some of the
Digester features. First, let's look at how the Digester instance is
created and initialized:</p>
<pre>
Digester digester = new Digester();
digester.push(this);
digester.setDebug(detail);
digester.setValidating(true);
</pre>
<p>We see that a new Digester instance is created, and is configured to use
a validating parser. Validation will occur against the struts-config_1_0.dtd
DTD that is included with Struts (as discussed earlier). In order to provide
a means of tracking the configured objects, the controller servlet instance
itself will be added to the digester's stack.</p>
<pre>
digester.addObjectCreate("struts-config/global-forwards/forward",
forwardClass, "className");
digester.addSetProperties("struts-config/global-forwards/forward");
digester.addSetNext("struts-config/global-forwards/forward",
"addForward",
"org.apache.struts.action.ActionForward");
digester.addSetProperty
("struts-config/global-forwards/forward/set-property",
"property", "value");
</pre>
<p>The rules created by these lines are used to process the global forward
declarations. When a <code><forward></code> element is encountered,
the following actions take place:</p>
<ul>
<li>A new object instance is created -- the <code>ActionForward</code>
instance that will represent this definition. The Java class name
defaults to that specified as an initialization parameter (which
we have stored in the String variable <code>forwardClass</code>), but can
be overridden by using the "className" attribute (if it is present in the
XML element we are currently parsing). The new <code>ActionForward</code>
instance is pushed onto the stack.</li>
<li>The properties of the <code>ActionForward</code> instance (at the top of
the stack) are configured based on the attributes of the
<code><forward></code> element.</li>
<li>Nested occurrences of the <code><set-property></code> element
cause calls to additional property setter methods to occur. This is
required only if you have provided a custom implementation of the
<code>ActionForward</code> class with additional properties that are
not included in the DTD.</li>
<li>The <code>addForward()</code> method of the next-to-top object on
the stack (i.e. the controller servlet itself) will be called, passing
the object at the top of the stack (i.e. the <code>ActionForward</code>
instance) as an argument. This causes the global forward to be
registered, and as a result of this it will be remembered even after
the stack is popped.</li>
<li>At the end of the <code><forward></code> element, the top element
(i.e. the <code>ActionForward</code> instance) will be popped off the
stack.</li>
</ul>
<p>Later on, the digester is actually executed as follows:</p>
<pre>
InputStream input =
getServletContext().getResourceAsStream(config);
...
try {
digester.parse(input);
input.close();
} catch (SAXException e) {
... deal with the problem ...
}
</pre>
<p>As a result of the call to <code>parse()</code>, all of the configuration
information that was defined in the <code>struts-config.xml</code> file is
now represented as collections of objects cached within the Struts controller
servlet, as well as being exposed as servlet context attributes.</p>
<h5>Parsing Body Text In XML Files</h5>
<p>The Digester module also allows you to process the nested body text in an
XML file, not just the elements and attributes that are encountered. The
following example is based on an assumed need to parse the web application
deployment descriptor (<code>/WEB-INF/web.xml</code>) for the current web
application, and record the configuration information for a particular
servlet. To record this information, assume the existence of a bean class
with the following method signatures (among others):</p>
<pre>
package com.mycompany;
public class ServletBean {
public void setServletName(String servletName);
public void setServletClass(String servletClass);
public void addInitParam(String name, String value);
}
</pre>
<p>We are going to process the <code>web.xml</code> file that declares the
controller servlet in a typical Struts-based application (abridged for
brevity in this example):</p>
<pre>
<web-app>
...
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet<servlet-class>
<init-param>
<param-name>application</param-name>
<param-value>org.apache.struts.example.ApplicationResources<param-value>
</init-param>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml<param-value>
</init-param>
</servlet>
...
</web-app>
</pre>
<p>Next, lets define some Digester processing rules for this input file:</p>
<pre>
digester.addObjectCreate("web-app/servlet",
"com.mycompany.ServletBean");
digester.addCallMethod("web-app/servlet/servlet-name", "setServletName", 0);
digester.addCallMethod("web-app/servlet/servlet-class",
"setServletClass", 0);
digester.addCallMethod("web-app/servlet/init-param",
"addInitParam", 2);
digester.addCallParam("web-app/servlet/init-param/param-name", 0);
digester.addCallParam("web-app/servlet/init-param/param-value", 1);
</pre>
<p>Now, as elements are parsed, the following processing occurs:</p>
<ul>
<li><em><servlet></em> - A new <code>com.mycompany.ServletBean</code>
object is created, and pushed on to the object stack.</li>
<li><em><servlet-name></em> - The <code>setServletName()</code> method
of the top object on the stack (our <code>ServletBean</code>) is called,
passing the body content of this element as a single parameter.</li>
<li><em><servlet-class></em> - The <code>setServletClass()</code> method
of the top object on the stack (our <code>ServletBean</code>) is called,
passing the body content of this element as a single parameter.</li>
<li><em><init-param></em> - A call to the <code>addInitParam</code>
method of the top object on the stack (our <code>ServletBean</code>) is
set up, but it is <strong>not</strong> called yet. The call will be
expecting two <code>String</code> parameters, which must be set up by
subsequent call parameter rules.</li>
<li><em><param-name></em> - The body content of this element is assigned
as the first (zero-relative) argument to the call we are setting up.</li>
<li><em><param-value></em> - The body content of this element is assigned
as the second (zero-relative) argument to the call we are setting up.</li>
<li><em></init-param></em> - The call to <code>addInitParam()</code>
that we have set up is now executed, which will cause a new name-value
combination to be recorded in our bean.</li>
<li><em><init-param></em> - The same set of processing rules are fired
again, causing a second call to <code>addInitParam()</code> with the
second parameter's name and value.</li>
<li><em></servlet></em> - The element on the top of the object stack
(which should be the <code>ServletBean</code> we pushed earlier) is
popped off the object stack.</li>
</ul>
</body>
</html>