/*
 * Written by Dawid Kurzyniec and released to the public domain, as explained
 * at http://creativecommons.org/licenses/publicdomain
 */

package edu.emory.mathcs.util.xml;

import org.xml.sax.*;
import java.net.*;
import java.util.*;
import java.io.*;

/**
 * SAX entity resolver that associates public IDs to URLs of their appropriate
 * DTD specifications. For example, DTD may be stored in a JAR file (as a
 * resource); then, the URL of the DTD will be the JAR URL pointing to that
 * resource. When the SAX parser encounters XML document with this public ID,
 * it will be able to resolve and load the DTD, and then validate the document.
 *
 * @author Dawid Kurzyniec
 * @version 1.0
 */

public class DTDResolver implements EntityResolver {

    final Map pubid2url = new HashMap();

    /**
     * Creates a new DTD resolver with no ID to URL associations.
     */
    public DTDResolver() {
    }

    /**
     * Creates a new DTD resolver with the specified ID to URL association.
     *
     * @param pubID the public ID
     * @param dtdUrl the URL of the DTD associated with that public ID
     */
    public DTDResolver(String pubID, URL dtdUrl) {
        addMapping(pubID, dtdUrl);
    }

    /**
     * Adds a specified ID to URL association to the mapping.
     *
     * @param pubID the public ID
     * @param dtdUrl the URL of the DTD associated with that public ID
     */
    public void addMapping(String pubID, URL dtdUrl) {
        if (dtdUrl == null) throw new NullPointerException();
        pubid2url.put(pubID, dtdUrl);
    }

    public InputSource resolveEntity(String pubID, String sysID) throws SAXException {
        URL dtdUrl = (URL)pubid2url.get(pubID);
        if (dtdUrl == null) {
            throw new SAXException("The document public ID is: \"" + pubID +
                "\" while it should be (one of): { " + enumerateMappedIDs() + " }");
        }
        try {
            return new InputSource(dtdUrl.openConnection().getInputStream());
        }
        catch (IOException e) {
            throw new SAXException("Unable to read DTD definition from " + dtdUrl, e);
        }
    }

    private String enumerateMappedIDs() {
        boolean first = true;
        StringBuffer buf = new StringBuffer();
        for (Iterator i = pubid2url.keySet().iterator(); i.hasNext(); ) {
            if (first) {
                first = false;
            } else {
                buf.append(", ");
            }
            buf.append('"').append(i.next()).append('"');
        }
        return buf.toString();
    }
}