//$Id: DTDEntityResolver.java 10936 2009-05-21 19:27:34Z norman.richards@jboss.com $
//Contributed by Markus Meissner
package org.jboss.seam.util;

import java.io.InputStream;
import java.io.Serializable;

import org.jboss.seam.contexts.ServletLifecycle;
import org.jboss.seam.log.LogProvider;
import org.jboss.seam.log.Logging;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;

/**
 * An {@link EntityResolver} implementation which attempts to resolve
 * various systemId URLs to local classpath lookups<ol>
 * <li>Any systemId URL beginning with <tt>http://jboss.org/schema/seam/</tt> is
 * searched for as a classpath resource in the classloader which loaded the
 * Seam classes.</li>
 * <li>Any systemId URL using <tt>classpath</tt> as the scheme (i.e. starting
 * with <tt>classpath://</tt> is searched for as a classpath resource using first
 * the current thread context classloader and then the classloader which loaded
 * the Seam classes.
 * </ol>
 * <p/>
 * Any entity references which cannot be resolved in relation to the above
 * rules result in returning null, which should force the SAX reader to
 * handle the entity reference in its default manner.
 */
public class DTDEntityResolver implements EntityResolver, Serializable 
{

    private static final long serialVersionUID = -4553926061006790714L;

    private static final LogProvider log = Logging.getLogProvider(DTDEntityResolver.class);

    private static final String SEAM_NAMESPACE = "http://jboss.org/schema/seam/";
    private static final String USER_NAMESPACE = "classpath://";

    public InputSource resolveEntity(String publicId, String systemId) 
    {
        if (systemId != null) {
            log.trace("trying to resolve system-id [" + systemId + "]");
            if (systemId.startsWith(SEAM_NAMESPACE)) {
                log.trace("recognized Seam namespace; attempting to resolve on classpath under org/jboss/seam/");
                String path = "org/jboss/seam/" + systemId.substring(SEAM_NAMESPACE.length());
                
                InputStream dtdStream = resolveInSeamNamespace(path);
                if (dtdStream == null)  {
                    log.warn("unable to locate [" + systemId + "] on classpath");
                } else {
                    log.debug("located [" + systemId + "] in classpath");
                    InputSource source = new InputSource(dtdStream);
                    source.setPublicId(publicId);
                    source.setSystemId(systemId);
                    return source;
                }
            } else if (systemId.startsWith(USER_NAMESPACE)) {
                log.trace("recognized local namespace; attempting to resolve on classpath");
                String path = systemId.substring(USER_NAMESPACE.length());
                
                InputStream stream = resolveInLocalNamespace(path);
                if (stream == null) {
                    log.warn("unable to locate [" + systemId + "] on classpath");                
                } else {
                    log.debug("located [" + systemId + "] in classpath");
                    InputSource source = new InputSource(stream);
                    source.setPublicId(publicId);
                    source.setSystemId(systemId);
                    return source;
                }
            }
        }
        // use default behavior
        return null;
    }

    protected InputStream resolveInSeamNamespace(String path) 
    {
        return this.getClass().getClassLoader().getResourceAsStream(path);
    }

    protected InputStream resolveInLocalNamespace(String path) 
    {
        try  {
            return Resources.getResourceAsStream(path, ServletLifecycle.getServletContext());
        } catch (Throwable t) {
            return null;
        }
    }
}
