You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by oh...@apache.org on 2007/09/19 22:29:42 UTC

svn commit: r577436 - in /commons/proper/configuration/trunk/src: java/org/apache/commons/configuration/XMLConfiguration.java test/org/apache/commons/configuration/TestXMLConfiguration.java

Author: oheger
Date: Wed Sep 19 13:29:42 2007
New Revision: 577436

URL: http://svn.apache.org/viewvc?rev=577436&view=rev
Log:
CONFIGURATION-290: Add ability to XMLConfiguration for registering entity IDs analogous to digester's register() method; this code is based on code of the o.a.c.digester.Digester class

Modified:
    commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/XMLConfiguration.java
    commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestXMLConfiguration.java

Modified: commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/XMLConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/XMLConfiguration.java?rev=577436&r1=577435&r2=577436&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/XMLConfiguration.java (original)
+++ commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/XMLConfiguration.java Wed Sep 19 13:29:42 2007
@@ -18,14 +18,18 @@
 package org.apache.commons.configuration;
 
 import java.io.File;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.Reader;
 import java.io.Writer;
 import java.net.URL;
+import java.net.URLConnection;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
@@ -49,6 +53,7 @@
 import org.w3c.dom.NamedNodeMap;
 import org.w3c.dom.NodeList;
 import org.w3c.dom.Text;
+import org.xml.sax.EntityResolver;
 import org.xml.sax.InputSource;
 import org.xml.sax.SAXException;
 import org.xml.sax.SAXParseException;
@@ -71,9 +76,9 @@
  * path set for this configuration.</p>
  *
  * <p>By inheriting from <code>{@link AbstractConfiguration}</code> this class
- * provides some extended functionaly, e.g. interpolation of property values.
+ * provides some extended functionality, e.g. interpolation of property values.
  * Like in <code>{@link PropertiesConfiguration}</code> property values can
- * contain delimiter characters (the comma ',' per default) and are then splitted
+ * contain delimiter characters (the comma ',' per default) and are then split
  * into multiple values. This works for XML attributes and text content of
  * elements as well. The delimiter can be escaped by a backslash. As an example
  * consider the following XML fragment:</p>
@@ -87,7 +92,7 @@
  * &lt;/config&gt;
  * </pre>
  * </p>
- * <p>Here the content of the <code>array</code> element will be splitted at
+ * <p>Here the content of the <code>array</code> element will be split at
  * the commas, so the <code>array</code> key will be assigned 4 values. In the
  * <code>scalar</code> property and the <code>text</code> attribute of the
  * <code>cite</code> element the comma is escaped, so that no splitting is
@@ -139,6 +144,7 @@
  * @version $Revision$, $Date$
  */
 public class XMLConfiguration extends AbstractHierarchicalFileConfiguration
+    implements EntityResolver
 {
     /**
      * The serial version UID.
@@ -154,6 +160,9 @@
     /** The document from this configuration's data source. */
     private Document document;
 
+    /** Stores a map with the registered public IDs.*/
+    private Map registeredEntities = new HashMap();
+
     /** Stores the name of the root element. */
     private String rootElementName;
 
@@ -566,6 +575,7 @@
                     .newInstance();
             factory.setValidating(isValidating());
             DocumentBuilder result = factory.newDocumentBuilder();
+            result.setEntityResolver(this);
 
             if (isValidating())
             {
@@ -831,6 +841,97 @@
             nd.addAttribute(convertToXMLNode((ConfigurationNode) it.next()));
         }
         return nd;
+    }
+
+    /**
+     * <p>
+     * Registers the specified DTD URL for the specified public identifier.
+     * </p>
+     * <p>
+     * <code>XMLConfiguration</code> contains an internal
+     * <code>EntityResolver</code> implementation. This maps
+     * <code>PUBLICID</code>'s to URLs (from which the resource will be
+     * loaded). A common use case for this method is to register local URLs
+     * (possibly computed at runtime by a class loader) for DTDs. This allows
+     * the performance advantage of using a local version without having to
+     * ensure every <code>SYSTEM</code> URI on every processed XML document is
+     * local. This implementation provides only basic functionality. If more
+     * sophisticated features are required, using
+     * {@link #setDocumentBuilder(DocumentBuilder)} to set a custom
+     * <code>DocumentBuilder</code> (which also can be initialized with a
+     * custom <code>EntityResolver</code>) is recommended.
+     * </p>
+     * <p>
+     * <strong>Note:</strong> This method will have no effect when a custom
+     * <code>DocumentBuilder</code> has been set. (Setting a custom
+     * <code>DocumentBuilder</code> overrides the internal implementation.)
+     * </p>
+     * <p>
+     * <strong>Note:</strong> This method must be called before the
+     * configuration is loaded. So the default constructor of
+     * <code>XMLConfiguration</code> should be used, the location of the
+     * configuration file set, <code>registerEntityId()</code> called, and
+     * finally the <code>load()</code> method can be invoked.
+     * </p>
+     *
+     * @param publicId Public identifier of the DTD to be resolved
+     * @param entityURL The URL to use for reading this DTD
+     * @throws IllegalArgumentException if the public ID is undefined
+     * @since 1.5
+     */
+    public void registerEntityId(String publicId, URL entityURL)
+    {
+        if (publicId == null)
+        {
+            throw new IllegalArgumentException("Public ID must not be null!");
+        }
+        registeredEntities.put(publicId, entityURL);
+    }
+
+    /**
+     * Resolves the requested external entity. This is the default
+     * implementation of the <code>EntityResolver</code> interface. It checks
+     * the passed in public ID against the registered entity IDs and uses a
+     * local URL if possible.
+     *
+     * @param publicId the public identifier of the entity being referenced
+     * @param systemId the system identifier of the entity being referenced
+     * @throws SAXException if a parsing exception occurs
+     * @since 1.5
+     */
+    public InputSource resolveEntity(String publicId, String systemId)
+            throws SAXException
+    {
+        // Has this system identifier been registered?
+        URL entityURL = null;
+        if (publicId != null)
+        {
+            entityURL = (URL) registeredEntities.get(publicId);
+        }
+
+        if (entityURL != null)
+        {
+            // Obtain an InputSource for this URL. This code is based on the
+            // createInputSourceFromURL() method of Commons Digester.
+            try
+            {
+                URLConnection connection = entityURL.openConnection();
+                connection.setUseCaches(false);
+                InputStream stream = connection.getInputStream();
+                InputSource source = new InputSource(stream);
+                source.setSystemId(entityURL.toExternalForm());
+                return source;
+            }
+            catch (IOException e)
+            {
+                throw new SAXException(e);
+            }
+        }
+        else
+        {
+            // default processing behavior
+            return null;
+        }
     }
 
     /**

Modified: commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestXMLConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestXMLConfiguration.java?rev=577436&r1=577435&r2=577436&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestXMLConfiguration.java (original)
+++ commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestXMLConfiguration.java Wed Sep 19 13:29:42 2007
@@ -1227,6 +1227,40 @@
     }
 
     /**
+     * Tests registering the publicId of a DTD.
+     */
+    public void testRegisterEntityId() throws ConfigurationException,
+            IOException
+    {
+        File dtdFile = new File("conf/properties.dtd");
+        final String publicId = "http://commons.apache.org/test/properties.dtd";
+        conf = new XMLConfiguration("testDtd.xml");
+        conf.setPublicID(publicId);
+        conf.save(testSaveConf);
+        XMLConfiguration checkConfig = new XMLConfiguration();
+        checkConfig.setFile(testSaveConf);
+        checkConfig.registerEntityId(publicId, dtdFile.toURL());
+        checkConfig.setValidating(true);
+        checkSavedConfig(checkConfig);
+    }
+
+    /**
+     * Tries to register a null public ID. This should cause an exception.
+     */
+    public void testRegisterEntityIdNull() throws IOException
+    {
+        try
+        {
+            conf.registerEntityId(null, new URL("http://commons.apache.org"));
+            fail("Could register null public ID!");
+        }
+        catch (IllegalArgumentException iex)
+        {
+            // ok
+        }
+    }
+
+    /**
      * Prepares a configuration object for testing a reload operation.
      *
      * @return the initialized configuration