Added: jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/Digester.java URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/Digester.java?view=auto&rev=151287 ============================================================================== --- jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/Digester.java (added) +++ jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/Digester.java Thu Feb 3 17:51:43 2005 @@ -0,0 +1,688 @@ +/* $Id: $ + * + * Copyright 2001-2005 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.digester2; + + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.xml.sax.SAXException; +import org.xml.sax.InputSource; +import org.xml.sax.XMLReader; +import org.xml.sax.EntityResolver; +import org.xml.sax.ErrorHandler; + +/** + * <p>A <strong>Digester</strong> processes an XML input stream by matching a + * series of element nesting patterns to execute Actions that have been added + * prior to the start of parsing.</p> + * + * <p>This class is the one that users interact with to configure the + * parsing rules and initiate the parse. It is mostly just a user-friendly + * facade over the SAXHandler class.</p> + * + * <p>See the <a href="package-summary.html#package_description">Digester + * Developer Guide</a> for more information.</p> + * + * <p><strong>IMPLEMENTATION NOTES</strong> + * <ul> + * <li> 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.</li> + * <li> This class requires that JAXP1.1 and a SAX2-compliant xml parser be + * present in the classpath. Versions of java prior to 1.4 include neither + * JAXP nor a parser, so simply including a modern xml parser plus a JAXP1.1 + * implementation in the classpath solves this requirement. Most xml parsers + * provide a suitable implementation as a companion jar to the main download + * (eg xml-apis.jar for the Apache Xerces parser). The 1.4 version of java + * includes JAXP1.1, and bundles an xml parser. While the JAXP apis included + * are fine, the xml parser bundled with this version of java is not adequate + * for most tasks, and it is recommended that a better one be provided in the + * classpath. Using the latest Apache Xerces release can be tricky, however, + * as Sun's bundled parser is xerces, with package names unchanged, and + * classes in the runtime libraries override classes in the classpath by + * default. See documentation on the java "endorsed override" feature for + * solutions, or the apache xerces website. + * Versions of java from 1.5 onward provide xml parsers that are fine for + * most purposes.</li> + * </ul> + */ + +public class Digester { + + // --------------------------------------------------------- Constructors + + /** + * Construct a new Digester with default properties. + */ + public Digester() { + this.saxHandler = new SAXHandler(); + } + + /** + * This method allows customisation of the way that sax events are + * handled, by allowing a modified subclass of SAXHandler to be + * specified as the target for parsing events. + */ + public Digester(SAXHandler saxHandler) { + this.saxHandler = saxHandler; + } + + // --------------------------------------------------- Instance Variables + + /** + * The object used to handle event callbacks from the SAX parser as + * the input xml is being processed. There is a 1:1 relation between + * a Digester and a SAXHandler. + */ + private SAXHandler saxHandler; + + /** + * The reader object used to generate SAX events representing the input. + * Normally, the object does this by parsing input text, but alternatives + * (like walking a DOM tree and generating appropriate events) are also + * possible. In any case, it's not relevant to this app how the sax + * events are generated. + */ + private XMLReader reader = null; + + /** + * See [EMAIL PROTECTED] #setValidating}. + */ + private boolean validating = false; + + // ------------------------------------------------------------- Properties + + /** + * Determine whether we are to validate the xml input against a schema. + * If so, then an error will be reported by the parse() methods if the + * input doesn't comply with the schema. If validation is disabled, then + * performance will be improved, but no error will be reported if the + * input is not correct. + * <p> + * Note that even when validation is disabled, any external schema or DTD + * referenced by the input document must still be read, as it can declare + * default attributes and similar items which affect xml parsing. + * + * @param validating The new validating parser flag. + * + * This must be called before <code>parse()</code> is called the first time. + */ + public void setValidating(boolean validating) { + this.validating = validating; + } + + /** + * Get the validating parser flag. See [EMAIL PROTECTED] #setValidating}. + */ + public boolean getValidating() { + return (this.validating); + } + + /** + * <p>Specify the XMLReader to be used to parse the input.</p> + * + * <p>This can be particularly useful in environments that are unfriendly + * to JAXP; it should always be possible to directly instantiate a parser + * using parser-specific code, and all decent xml parsers should implement + * (ie be castable to) the org.xml.sax.XMLReader interface even when the + * JAXP apis are not available or do not function correctly.</p> + * + * <p>If you have a SAXParser instance that should be used as the source + * of the input, then use: + * <pre> + * setXMLReader(saxParser.getXMLReader()); + * </pre> + * </p> + * + * <p>The reader passed here should be configured with namespace-aware + * parsing enabled, as the digester classes assume this.</p> + */ + public void setXMLReader(XMLReader reader) { + this.reader = reader; + } + + /** + * Set the class loader to be used for instantiating application objects + * when required. If a non-null value is passed to this method, then + * method [EMAIL PROTECTED] #useContextClassLoader} will have no effect. + * <p> + * When an Action is executed due to some xml input, and that Action + * wishes to create an object to represent the input, then the class + * used will be loaded via the specified classloader. + * + * @param classLoader The new class loader to use, or <code>null</code> + * to revert to the standard rules + */ + public void setExplicitClassLoader(ClassLoader classLoader) { + saxHandler.setExplicitClassLoader(classLoader); + } + + /** + * Get the explicit class loader to be used by Actions for instantiating + * objects when required. Null indicates that there is no explicit + * classloader set. + * <p> + * When no explicit classloader has been specified, either the context + * classloader (see [EMAIL PROTECTED] #setUseContextClassLoader}) or the classloader + * that loaded the SAXHandler instance will be used. + * + * @return the classloader previously passed to setExplicitClassLoader, + * or null if no explicit classloader has been set. + */ + public ClassLoader getExplicitClassLoader() { + return saxHandler.getExplicitClassLoader(); + } + + /** + * Set the current logger for this Digester. + */ + public void setLogger(Log log) { + saxHandler.setLogger(log); + } + + /** + * Get the current Logger associated with this instance of the Digester + */ + public Log getLogger() { + return saxHandler.getLogger(); + } + + /** + * Sets the logger used for logging SAX-related information. + * <strong>Note</strong> the output is finely grained. + * @param saxLog Log, not null + */ + public void setSAXLogger(Log saxLog) { + saxHandler.setSAXLogger(saxLog); + } + + /** + * Gets the logger used for logging SAX-related information. + * <strong>Note</strong> the output is finely grained. + */ + public Log getSAXLogger() { + return saxHandler.getSAXLogger(); + } + + /** + * Set the <code>RuleManager</code> implementation object containing our + * rules collection and associated matching policy. + * + * @param ruleManager New RuleManager implementation + */ + public void setRuleManager(RuleManager ruleManager) { + saxHandler.setRuleManager(ruleManager); + } + + /** + * Get the <code>RuleManager</code> object containing our + * rule collection and associated matching policy. If none has been + * established, a default implementation will be created and returned. + */ + public RuleManager getRuleManager() { + return saxHandler.getRuleManager(); + } + + /** + * Set the publid id of the current file being parsed. This will cause + * the declared DOCTYPE (if any) of the input document to be ignored. + * + * Instead the provided publicId will be looked up in the known entities, + * and the resource located at the associated URL will be used as the + * DTD for this input document. + * <p> + * NOTE: if the input document doesn't include a DOCTYPE, then the + * DTD specified by this call is not used. There is currently no way + * to force an input document to be validated against a specific DTD + * (due to a lack of this feature in the xml standard). + * + * @param publicId the DTD/Schema public's id. + */ + public void setPublicId(String publicId){ + saxHandler.setPublicId(publicId); + } + + /** + * Get the public identifier of the DTD we are currently parsing under, + * if any. If setPublicId has been called previously, then the value + * returned here will be the one explicitly set. Otherwise, if we have + * already seen a DOCTYPE declaration in the input data, then the + * public id in that DOCTYPE will be returned. Otherwise (parsing hasn't + * started or the input document had no DOCTYPE) null is returned. + */ + public String getPublicId() { + return saxHandler.getPublicId(); + } + + /** + * Set the XML Schema URI used for validating a XML Instance. + * + * @param schemaLocation a URI to the schema. + */ + public void setSchema(String schemaLocation){ + saxHandler.setSchema(schemaLocation); + } + + /** + * Get the XML Schema URI used for validating an XML instance. + */ + public String getSchema() { + return saxHandler.getSchema(); + } + + /** + * Set the XML Schema language used when parsing. By default, we use W3C. + * + * @param schemaLanguage a URI to the schema language. + */ + public void setSchemaLanguage(String schemaLanguage){ + saxHandler.setSchemaLanguage(schemaLanguage); + } + + /** + * Get the XML Schema language used when parsing. + */ + public String getSchemaLanguage() { + return saxHandler.getSchemaLanguage(); + } + + /** + * Determine whether to use the Context Classloader (the one found by + * calling <code>Thread.currentThread().getContextClassLoader()</code>) + * to resolve/load classes when an Action needs to create an instance of + * an object to represent data in the xml input. If this is set to false, + * and there is no explicit classloader set, then the same classloader + * that loaded the Action class is used. + * <p> + * See [EMAIL PROTECTED] #setClassLoader}. + * + * @param use determines whether to use the Context Classloader. + */ + public void setUseContextClassLoader(boolean use) { + saxHandler.setUseContextClassLoader(use); + } + + /** + * Indicates whether the context classloader will be used by Actions + * when instantiating objects. See [EMAIL PROTECTED] #setUseContextClassLoader}. + */ + public boolean getUseContextClassLoader() { + return saxHandler.getUseContextClassLoader(); + } + + /** + * Sets the <code>Substitutor</code> to be used to perform pre-processing + * on xml attributes and body text before Actions are applied. + * + * @param substitutor the Substitutor to be used, or null if no + * substitution (pre-processing) is to be performed. + */ + public void setSubstitutor(Substitutor substitutor) { + saxHandler.setSubstitutor(substitutor); + } + + /** + * Gets the <code>Substitutor</code> used to perform pre-processing + * on xml attributes and body text before Actions are applied. + * + * @return Substitutor, null if no substitution (pre-processing) is to + * be performed. + */ + public Substitutor getSubstitutor() { + return saxHandler.getSubstitutor(); + } + + /** + * Specifies a map of (publicId->URI) pairings that will be used by the + * default Entity Resolver when resolving entities in the input xml + * (including the DTD or schema specified with the DOCTYPE). + * <p> + * See [EMAIL PROTECTED] #getKnownEntities}, and [EMAIL PROTECTED] #setEntityResolver}. + */ + public void setKnownEntities(Map knownEntities) { + saxHandler.setKnownEntities(knownEntities); + } + + /** + * <p>Register a mapping between a public or system ID (denoting an external + * entity, aka resource) and a URL indicating where that resource can be + * found, for use by the default Entity Resolver. It is particularly useful + * to register local locations for DTDs or schemas that the input xml may + * reference.</p> + * + *<p>When the input xml refers to the entity via a public or system id, the + * resource pointed to by the registered URL is returned. This is commonly + * done for the input document's DTD, so that the DTD can be retrieved + * from a local file.</p> + * + * <p>This implementation provides only basic functionality. If more + * sophisticated features are required,using [EMAIL PROTECTED] #setEntityResolver} to + * set a custom resolver is recommended. Note in particular that if the + * input xml uses a system-ID to refer to an entity that is not registered, + * then the parser will attempt to use the system-id directly, potentially + * downloading the resource from a remote location.</p> + * + * <p> + * <strong>Note:</strong> This method will have no effect when a custom + * <code>EntityResolver</code> has been set. (Setting a custom + * <code>EntityResolver</code> overrides the internal implementation.) + * </p> + * + * @param publicOrSystemId Public or system identifier of the entity + * to be resolved + * @param entityURL The URL to use for reading this entity + */ + public void registerKnownEntity(String publicOrSystemId, String entityURL) { + saxHandler.registerKnownEntity(publicOrSystemId, entityURL); + } + + /** + * Returns a map of (publicId->URI) pairings that will be used by the + * default EntityResolver. + * <p> + * See [EMAIL PROTECTED] #setKnownEntities}, and [EMAIL PROTECTED] #setEntityResolver}. + */ + public Map getKnownEntities() { + return saxHandler.getKnownEntities(); + } + + // ------------------------------------------------------- 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 DigestionException, IOException, SAXException { + try { + InputSource input = new InputSource(new FileInputStream(file)); + input.setSystemId(file.toURL().toString()); + getXMLReader().parse(input); + return saxHandler.getRoot(); + } + catch(RuntimeException re) { + throw new DigestionException(re); + } + } + + /** + * 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 DigestionException, IOException, SAXException { + try { + getXMLReader().parse(input); + return saxHandler.getRoot(); + } + catch(RuntimeException re) { + throw new DigestionException(re); + } + } + + /** + * Parse the content of the specified input stream using this Digester. + * Returns the root element from the object stack (if any). + * <p> + * Note that because the xml parser has no idea what the "real location" + * of the input is supposed to be, no relative referenced from the + * input xml document will work. If you need this behaviour, then + * use the parse(org.xml.sax.InputSource) variant, together with the + * InputSource.setSystemId method to tell the input source what the + * logical location of the input is supposed to be. + * + * @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 DigestionException, IOException, SAXException { + try { + InputSource is = new InputSource(input); + getXMLReader().parse(is); + return saxHandler.getRoot(); + } + catch(RuntimeException re) { + throw new DigestionException(re); + } + } + + /** + * Parse the content of the specified reader using this Digester. + * Returns the root element from the object stack (if any). + * <p> + * Note that because the xml parser has no idea what the "real location" + * of the input is supposed to be, no relative referenced from the + * input xml document will work. If you need this behaviour, then + * use the parse(org.xml.sax.InputSource) variant, together with the + * InputSource.setSystemId method to tell the input source what the + * logical location of the input is supposed to be. + * + * @param reader Reader 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(Reader reader) + throws DigestionException, IOException, SAXException { + + try { + InputSource is = new InputSource(reader); + getXMLReader().parse(is); + return saxHandler.getRoot(); + } + catch(RuntimeException re) { + throw new DigestionException(re); + } + } + + /** + * Parse the content of the specified URI using this Digester. + * Returns the root element from the object stack (if any). + * <p> + * Note that because the xml parser has no idea what the "real location" + * of the input is supposed to be, no relative referenced from the + * input xml document will work. If you need this behaviour, then + * use the parse(org.xml.sax.InputSource) variant, together with the + * InputSource.setSystemId method to tell the input source what the + * logical location of the input is supposed to be. + * + * @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 DigestionException, IOException, SAXException { + try { + InputSource is = new InputSource(uri); + getXMLReader().parse(is); + return saxHandler.getRoot(); + } + catch(RuntimeException re) { + throw new DigestionException(re); + } + } + + // --------------------------------------------------------- Rule Methods + + /** + * <p>Register a new rule (pattern/action pair). + * + * @param pattern Element matching pattern + * @param action Action to be registered + */ + public void addRule(String pattern, Action action) + throws InvalidRuleException { + saxHandler.addRule(pattern, action); + } + + // --------------------------------------------------- Object Stack Methods + + /** + * Set the initial object that will form the root of the tree of + * objects generated during a parse. + * <p> + * Note that this is optional; an ObjectCreateAction associated with the + * root element of the input document performs the same task. + * + * @param object The new object + */ + public void setInitialObject(Object object) { + saxHandler.setInitialObject(object); + } + + /** + * Returns the root element of the tree of objects created as a result + * of applying the rules to the input XML. + * <p> + * If the digester stack was "primed" by explicitly pushing a root + * object onto the stack before parsing started, then that root object + * is returned here. + * <p> + * Alternatively, if an Action which creates an object (eg ObjectCreateAction) + * matched the root element of the xml, then the object created will be + * returned here. + * <p> + * In other cases, the object most recently pushed onto an empty digester + * stack is returned. This would be a most unusual use of digester, however; + * one of the previous configurations is much more likely. + * <p> + * Note that when using one of the Digester.parse methods, the return + * value from the parse method is exactly the same as the return value + * from this method. However when the Digester is being used as a + * SAXContentHandler, no such return value is available; in this case, this + * method allows you to access the root object that has been created + * after parsing has completed. + * + * @return the root object that has been created after parsing + * or null if the digester has not parsed any XML yet. + */ + public Object getRoot() { + return saxHandler.getRoot(); + } + + /** + * Return the XMLReader to be used for parsing the input document. + * + * @exception SAXException if no XMLReader can be instantiated + */ + public XMLReader getXMLReader() throws SAXException { + if (reader != null) { + return reader; + } + + try { + SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setNamespaceAware(true); + factory.setValidating(validating); + + SAXParser parser = factory.newSAXParser(); + reader = parser.getXMLReader(); + } catch (Exception e) { + Log log = saxHandler.getLogger(); + log.error("Digester.getXMLReader: ", e); + return null; + } + + reader.setDTDHandler(saxHandler); + reader.setContentHandler(saxHandler); + reader.setEntityResolver(saxHandler); + reader.setErrorHandler(saxHandler); + return reader; + } + + /** + * Specify the Entity Resolver used to determine the physical location + * of resources (such as DTDs or schemas) referred to by the input xml. + * <p> + * The value <i>null</i> indicates that the SAXHandler object will be + * used as the entity resolver. In this case, see methods: + * <ul> + * <li>[EMAIL PROTECTED] #setKnownEntities}</li> + * <li>[EMAIL PROTECTED] #registerKnownEntity}</li> + * </ul> + * + * @param entityResolver a class that implement the + * <code>EntityResolver</code> interface, or <i>null</i> to restore + * the default behaviour. + */ + public void setEntityResolver(EntityResolver entityResolver){ + saxHandler.setEntityResolver(entityResolver); + } + + /** + * Return the Entity Resolver used to determine the physical location + * of resources (such as DTDs or schemas) referred to by the input xml. + * <p> + * The value <i>null</i> indicates that the SAXHandler object will be + * used as the entity resolver. In this case, see methods: + * <ul> + * <li>[EMAIL PROTECTED] #setKnownEntities}</li> + * <li>[EMAIL PROTECTED] #registerKnownEntity}</li> + * </ul> + * + * @return the Entity Resolver to be used. + */ + public EntityResolver getEntityResolver(){ + return saxHandler.getEntityResolver(); + } + + /** + * Return the error handler which will be used if the xml parser detects + * errors in the xml input being parsed. + */ + public ErrorHandler getErrorHandler() { + return saxHandler.getErrorHandler(); + } + + /** + * Set the error handler for this Digester. + * + * @param errorHandler The new error handler + */ + public void setErrorHandler(ErrorHandler errorHandler) { + saxHandler.setErrorHandler(errorHandler); + } +}
Added: jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/DigestionException.java URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/DigestionException.java?view=auto&rev=151287 ============================================================================== --- jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/DigestionException.java (added) +++ jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/DigestionException.java Thu Feb 3 17:51:43 2005 @@ -0,0 +1,37 @@ +/* $Id: $ + * + * Copyright 2001-2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package org.apache.commons.digester2; + +/** + */ + +public class DigestionException extends Exception { + public DigestionException(String msg) { + super(msg); + } + + public DigestionException(String msg, Throwable t) { + super(msg, t); + } + + public DigestionException(Throwable t) { + super(t); + } +} + Added: jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/InvalidRuleException.java URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/InvalidRuleException.java?view=auto&rev=151287 ============================================================================== --- jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/InvalidRuleException.java (added) +++ jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/InvalidRuleException.java Thu Feb 3 17:51:43 2005 @@ -0,0 +1,41 @@ +/* $Id: $ + * + * Copyright 2001-2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package org.apache.commons.digester2; + +/** + * Thrown when a problem is detected as a rule is added to a RuleManager. + * One possible cause is that the pattern is invalid, but there may be + * others depending upon the RuleManager and Action involved. + */ + +public class InvalidRuleException extends DigestionException { + public InvalidRuleException(String msg) { + super(msg); + } + + public InvalidRuleException(Throwable t) { + super(t); + } + + public InvalidRuleException(String msg, Throwable t) { + super(msg, t); + } + +} + Added: jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/MultiHashMap.java URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/MultiHashMap.java?view=auto&rev=151287 ============================================================================== --- jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/MultiHashMap.java (added) +++ jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/MultiHashMap.java Thu Feb 3 17:51:43 2005 @@ -0,0 +1,446 @@ +/* + * Copyright 2001-2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.digester2; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.util.AbstractCollection; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; + +/** + * A <code>MultiMap</code> is a Map with slightly different semantics. + * Putting a value into the map will add the value to a Collection at that key. + * Getting a value will return a Collection, holding all the values put to that key. + * <p> + * This implementation uses an <code>ArrayList</code> as the collection. + * The internal storage list is made available without cloning via the + * <code>get(Object)</code> and <code>entrySet()</code> methods. + * The implementation returns <code>null</code> when there are no values mapped to a key. + * <p> + * For example: + * <pre> + * MultiMap mhm = new MultiHashMap(); + * mhm.put(key, "A"); + * mhm.put(key, "B"); + * mhm.put(key, "C"); + * List list = (List) mhm.get(key);</pre> + * <p> + * <code>list</code> will be a list containing "A", "B", "C". + * + * This code is derived from the class in apache-commons-collections v3.1. + * Thanks go to the authors of commons-collections! + * + */ +public class MultiHashMap extends HashMap { + + private static final Iterator EMPTY_ITERATOR = + java.util.Collections.EMPTY_LIST.iterator(); + + // backed values collection + private transient Collection values = null; + + // compatibility with commons-collection releases 2.0/2.1 + private static final long serialVersionUID = 1943563828307035349L; + + /** + * Constructor. + */ + public MultiHashMap() { + super(); + } + + /** + * Constructor. + * + * @param initialCapacity the initial map capacity + */ + public MultiHashMap(int initialCapacity) { + super(initialCapacity); + } + + /** + * Constructor. + * + * @param initialCapacity the initial map capacity + * @param loadFactor the amount 0.0-1.0 at which to resize the map + */ + public MultiHashMap(int initialCapacity, float loadFactor) { + super(initialCapacity, loadFactor); + } + + /** + * Constructor. Note that this class has special behaviour if the source + * object being copied is a MultiHashMap; in that case, a copy of the + * collection is made too, so that adding an entry to the original + * MultiHashMap does not cause the value in the copy to change! + * + * @param mapToCopy a Map to copy + */ + public MultiHashMap(Map mapToCopy) { + super((int) mapToCopy.size()); + if (mapToCopy instanceof MultiHashMap) { + for (Iterator it = mapToCopy.entrySet().iterator(); it.hasNext();) { + Map.Entry entry = (Map.Entry) it.next(); + Collection coll = (Collection) entry.getValue(); + Collection newColl = createCollection(coll); + super.put(entry.getKey(), newColl); + } + } else { + putAll(mapToCopy); + } + } + + /** + * Read the object during deserialization. + */ + private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { + // This method is needed because the 1.2/1.3 Java deserialisation called + // put and thus messed up that method + + // default read object + s.defaultReadObject(); + + // problem only with jvm <1.4 + String version = "1.2"; + try { + version = System.getProperty("java.version"); + } catch (SecurityException ex) { + // ignore and treat as 1.2/1.3 + } + + if (version.startsWith("1.2") || version.startsWith("1.3")) { + for (Iterator iterator = entrySet().iterator(); iterator.hasNext();) { + Map.Entry entry = (Map.Entry) iterator.next(); + // put has created a extra collection level, remove it + super.put(entry.getKey(), ((Collection) entry.getValue()).iterator().next()); + } + } + } + + //----------------------------------------------------------------------- + /** + * Gets the total size of the map by counting all the values. + * + * @return the total size of the map counting all values + * @since Commons Collections 3.1 + */ + public int totalSize() { + int total = 0; + Collection values = super.values(); + for (Iterator it = values.iterator(); it.hasNext();) { + Collection coll = (Collection) it.next(); + total += coll.size(); + } + return total; + } + + /** + * Gets the collection mapped to the specified key. + * This method is a convenience method to typecast the result of <code>get(key)</code>. + * + * @param key the key to retrieve + * @return the collection mapped to the key, null if no mapping + * @since Commons Collections 3.1 + */ + public Collection getCollection(Object key) { + return (Collection) get(key); + } + + /** + * Gets the size of the collection mapped to the specified key. + * + * @param key the key to get size for + * @return the size of the collection at the key, zero if key not in map + * @since Commons Collections 3.1 + */ + public int size(Object key) { + Collection coll = getCollection(key); + if (coll == null) { + return 0; + } + return coll.size(); + } + + /** + * Gets an iterator for the collection mapped to the specified key. + * + * @param key the key to get an iterator for + * @return the iterator of the collection at the key, empty iterator if key not in map + * @since Commons Collections 3.1 + */ + public Iterator iterator(Object key) { + Collection coll = getCollection(key); + if (coll == null) { + return EMPTY_ITERATOR; + } + return coll.iterator(); + } + + /** + * Adds the value to the collection associated with the specified key. + * <p> + * Unlike a normal <code>Map</code> the previous value is not replaced. + * Instead the new value is added to the collection stored against the key. + * + * @param key the key to store against + * @param value the value to add to the collection at the key + * @return the value added if the map changed and null if the map did not change + */ + public Object put(Object key, Object value) { + // NOTE:: put is called during deserialization in JDK < 1.4 !!!!!! + // so we must have a readObject() + Collection coll = getCollection(key); + if (coll == null) { + coll = createCollection(null); + super.put(key, coll); + } + boolean results = coll.add(value); + return (results ? value : null); + } + + /** + * Adds a collection of values to the collection associated with the specified key. + * + * @param key the key to store against + * @param values the values to add to the collection at the key, null ignored + * @return true if this map changed + * @since Commons Collections 3.1 + */ + public boolean putAll(Object key, Collection values) { + if (values == null || values.size() == 0) { + return false; + } + Collection coll = getCollection(key); + if (coll == null) { + coll = createCollection(values); + if (coll.size() == 0) { + return false; + } + super.put(key, coll); + return true; + } else { + return coll.addAll(values); + } + } + + /** + * Checks whether the map contains the value specified. + * <p> + * This checks all collections against all keys for the value, and thus could be slow. + * + * @param value the value to search for + * @return true if the map contains the value + */ + public boolean containsValue(Object value) { + Set pairs = super.entrySet(); + + if (pairs == null) { + return false; + } + Iterator pairsIterator = pairs.iterator(); + while (pairsIterator.hasNext()) { + Map.Entry keyValuePair = (Map.Entry) pairsIterator.next(); + Collection coll = (Collection) keyValuePair.getValue(); + if (coll.contains(value)) { + return true; + } + } + return false; + } + + /** + * Checks whether the collection at the specified key contains the value. + * + * @param value the value to search for + * @return true if the map contains the value + * @since Commons Collections 3.1 + */ + public boolean containsValue(Object key, Object value) { + Collection coll = getCollection(key); + if (coll == null) { + return false; + } + return coll.contains(value); + } + + /** + * Removes a specific value from map. + * <p> + * The item is removed from the collection mapped to the specified key. + * Other values attached to that key are unaffected. + * <p> + * If the last value for a key is removed, <code>null</code> will be returned + * from a subsequant <code>get(key)</code>. + * + * @param key the key to remove from + * @param item the value to remove + * @return the value removed (which was passed in), null if nothing removed + */ + public Object remove(Object key, Object item) { + Collection valuesForKey = getCollection(key); + if (valuesForKey == null) { + return null; + } + valuesForKey.remove(item); + + // remove the list if it is now empty + // (saves space, and allows equals to work) + if (valuesForKey.isEmpty()){ + remove(key); + } + return item; + } + + /** + * Clear the map. + * <p> + * This clears each collection in the map, and so may be slow. + */ + public void clear() { + // For gc, clear each list in the map + Set pairs = super.entrySet(); + Iterator pairsIterator = pairs.iterator(); + while (pairsIterator.hasNext()) { + Map.Entry keyValuePair = (Map.Entry) pairsIterator.next(); + Collection coll = (Collection) keyValuePair.getValue(); + coll.clear(); + } + super.clear(); + } + + /** + * Gets a collection containing all the values in the map. + * <p> + * This returns a collection containing the combination of values from all keys. + * + * @return a collection view of the values contained in this map + */ + public Collection values() { + Collection vs = values; + return (vs != null ? vs : (values = new Values())); + } + + //----------------------------------------------------------------------- + /** + * Inner class to view the elements. + */ + private class Values extends AbstractCollection { + + public Iterator iterator() { + return new ValueIterator(); + } + + public int size() { + int compt = 0; + Iterator it = iterator(); + while (it.hasNext()) { + it.next(); + compt++; + } + return compt; + } + + public void clear() { + MultiHashMap.this.clear(); + } + + } + + /** + * Inner iterator to view the elements. + */ + private class ValueIterator implements Iterator { + private Iterator backedIterator; + private Iterator tempIterator; + + private ValueIterator() { + backedIterator = MultiHashMap.super.values().iterator(); + } + + private boolean searchNextIterator() { + while (tempIterator == null || tempIterator.hasNext() == false) { + if (backedIterator.hasNext() == false) { + return false; + } + tempIterator = ((Collection) backedIterator.next()).iterator(); + } + return true; + } + + public boolean hasNext() { + return searchNextIterator(); + } + + public Object next() { + if (searchNextIterator() == false) { + throw new NoSuchElementException(); + } + return tempIterator.next(); + } + + public void remove() { + if (tempIterator == null) { + throw new IllegalStateException(); + } + tempIterator.remove(); + } + + } + + //----------------------------------------------------------------------- + /** + * Clone the map. + * <p> + * The clone will shallow clone the collections as well as the map. + * + * @return the cloned map + */ + public Object clone() { + MultiHashMap obj = (MultiHashMap) super.clone(); + + // clone each Collection container + for (Iterator it = entrySet().iterator(); it.hasNext();) { + Map.Entry entry = (Map.Entry) it.next(); + Collection coll = (Collection) entry.getValue(); + Collection newColl = createCollection(coll); + entry.setValue(newColl); + } + return obj; + } + + /** + * Creates a new instance of the map value Collection container. + * <p> + * This method can be overridden to use your own collection type. + * + * @param coll the collection to copy, may be null + * @return the new collection + */ + protected Collection createCollection(Collection coll) { + if (coll == null) { + return new ArrayList(); + } else { + return new ArrayList(coll); + } + } + +} Added: jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/NestedSAXException.java URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/NestedSAXException.java?view=auto&rev=151287 ============================================================================== --- jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/NestedSAXException.java (added) +++ jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/NestedSAXException.java Thu Feb 3 17:51:43 2005 @@ -0,0 +1,47 @@ +/* $Id: $ + * + * Copyright 2001-2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package org.apache.commons.digester2; + +/** + * In some places in SAXHandler classes, we are permitted only to throw + * subclasses of SAXException. This class can be used to wrap the exception + * we really want to throw; the receiver should unwrap it via the getCause + * method. + * <p> + * We implement getCause directly here, in order to be able to support + * java platforms earlier than 1.4. + */ + +public class NestedSAXException extends org.xml.sax.SAXException { + private Throwable cause; + + public NestedSAXException(String msg, Throwable t) { + super(msg); + cause = t; + } + + public NestedSAXException(Throwable t) { + cause = t; + } + + public Throwable getCause() { + return cause; + } +} + Added: jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/ParseException.java URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/ParseException.java?view=auto&rev=151287 ============================================================================== --- jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/ParseException.java (added) +++ jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/ParseException.java Thu Feb 3 17:51:43 2005 @@ -0,0 +1,38 @@ +/* $Id: $ + * + * Copyright 2001-2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package org.apache.commons.digester2; + +/** + */ + +public class ParseException extends DigestionException { + public ParseException(String msg) { + super(msg); + } + + public ParseException(Throwable t) { + super(t); + } + + public ParseException(String msg, Throwable t) { + super(msg, t); + } + +} + Added: jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/Path.java URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/Path.java?view=auto&rev=151287 ============================================================================== --- jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/Path.java (added) +++ jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/Path.java Thu Feb 3 17:51:43 2005 @@ -0,0 +1,57 @@ +/* $Id: $ + * + * Copyright 2001-2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package org.apache.commons.digester2; + +/** + * A simple class that represents the path to a particular XML element. + */ + +public class Path { + StringBuffer buf = new StringBuffer(); + ArrayStack lengths = new ArrayStack(); + + public Path() { + } + + public void push(String namespace, String elementName) { + lengths.push(new Integer(buf.length())); + buf.append("/"); + if ((namespace != null) && (namespace.length()>0)) { + buf.append('{'); + buf.append(namespace); + buf.append('}'); + } + buf.append(elementName); + } + + public void pop() { + int length = ((Integer)lengths.pop()).intValue(); + buf.setLength(length); + } + + public String getPath() { + return buf.toString(); + } + + public void clear() { + buf.setLength(0); + lengths.clear(); + } +} + Added: jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/RuleManager.java URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/RuleManager.java?view=auto&rev=151287 ============================================================================== --- jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/RuleManager.java (added) +++ jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/RuleManager.java Thu Feb 3 17:51:43 2005 @@ -0,0 +1,98 @@ +/* $Id: $ + * + * Copyright 2001-2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.digester2; + +import java.util.List; + +/** + * Public interface defining a collection of Action instances (and corresponding + * matching patterns) plus an implementation of a matching policy that selects + * the Actions that match a particular pattern of nested elements discovered + * during parsing. + * <p> + * Terminology: + * <ul> + * <li>Pattern: a string with namespace prefixes in it, eg "/foo:bob"</li> + * <li>Path: a string with namespace uris in it, eg /{urn:foo}bob"</li> + * </ul> + * + */ + +public abstract class RuleManager { + + // --------------------------------------------------------- Public Methods + + /** + * Returns a new instance with the same type as the concrete object this + * method is invoked on, complete with contained Actions and patterns. Note + * that the new RuleManager instance may contain references to the same + * Action instances as the old one, as Action instances are expected to be + * stateless and therefore can be safely shared between RuleManagers. + */ + public abstract RuleManager copy(); + + /** + * Invoked before parsing each input document, this method gives the + * RuleManager and the managed Action objects the opportunity to do + * per-parse initialisation if required. + */ + public void startParse(Context context) throws DigestionException {} + + /** + * Invoked after parsing each input document, this method gives the + * RuleManager and the managed Action objects the opportunity to do + * per-parse cleanup if required. + */ + public void finishParse(Context context) throws DigestionException {} + + /** + * Define a mapping between xml element prefix and namespace uri + * for use when rule patterns contain namespace prefixes. + */ + public abstract void addNamespace(String prefix, String uri); + + /** + * Cause the specified Action to be invoked whenever an xml element + * is encountered in the input which matches the specified pattern. + * <p> + * If the pattern contains any namespace prefixes, eg "/myns:item", + * then an exception will be thrown unless that prefix has previously + * been defined via a call to method addNamespace. + * <p> + * Note that it is permitted for the same Action to be added multiple + * times with different associated patterns. + */ + public abstract void addRule(String pattern, Action action) + throws InvalidRuleException; + + /** + * Return a List of all registered Action instances that match the specified + * nesting pattern, or a zero-length List if there are no matches. If more + * than one Action instance matches, they <strong>must</strong> be returned + * in the order originally registered through the <code>addRule()</code> + * method. + * + * @param path is a string of form + * <pre>/{namespace}elementName/{namespace}elementName"</pre> + * identifying the path from the root of the input document to the element + * for which the caller wants the set of matching Action objects. If an + * element has no namespace, then the {} part is omitted. + */ + public abstract List getMatchingActions(String path) + throws DigestionException; +} --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
