package nu.validator.localentities;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;

import nu.validator.xml.TypedInputSource;

import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
 * @version $Id: LocalCacheEntityResolver.java 74 2008-09-17 10:34:15Z hsivonen $
 * @author hsivonen
 */
public class LocalCacheEntityResolver implements EntityResolver {

    private static final ClassLoader LOADER = LocalCacheEntityResolver.class.getClassLoader();

    private static final Map<String, String> PATH_MAP = new HashMap<>();

    static {
        try {
            BufferedReader r = new BufferedReader(new InputStreamReader(
                    LOADER.getResourceAsStream("nu/validator/localentities/files/entitymap"), "UTF-8"));
            String line;
            while ((line = r.readLine()) != null) {
                if ("".equals(line.trim())) {
                    break;
                }
                String s[] = line.split("\t");
                PATH_MAP.put(s[0], "nu/validator/localentities/files/" + s[1]);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static InputStream getPresetsAsStream() {
        return LOADER.getResourceAsStream("nu/validator/localentities/files/presets");
    }

    public static InputStream getHtml5SpecAsStream() {
        return LOADER.getResourceAsStream("nu/validator/localentities/files/html5spec");
    }
    
    private EntityResolver delegate;

    private boolean allowRnc = false;

    /**
     * @param delegate
     */
    public LocalCacheEntityResolver(EntityResolver delegate) {
        this.delegate = delegate;
    }

    /**
     * @see org.xml.sax.EntityResolver#resolveEntity(java.lang.String,
     *      java.lang.String)
     */
    @Override
    public InputSource resolveEntity(String publicId, String systemId)
            throws SAXException, IOException {
        String path = PATH_MAP.get(systemId);
        if (path != null) {
            @SuppressWarnings("resource")
            InputStream stream = LOADER.getResourceAsStream(path);
            if (stream != null) {
                TypedInputSource is = new TypedInputSource();
                if (systemId.endsWith(".rnc")) {
                    is.setType("application/relax-ng-compact-syntax");
                    if (!allowRnc) {
                        stream.close();
                        throw new IOException("Not an XML resource: "
                                + systemId);
                    }
                } else if (systemId.endsWith(".dtd")) {
                    is.setType("application/xml-dtd");
                } else if (systemId.endsWith(".ent")) {
                    is.setType("application/xml-external-parsed-entity");
                } else {
                    is.setType("application/xml");
                }
                is.setByteStream(stream);
                is.setSystemId(systemId);
                is.setPublicId(publicId);
                return is;
            }
        }
        return delegate.resolveEntity(publicId, systemId);
    }

    /**
     * @return Returns the allowRnc.
     */
    public boolean isAllowRnc() {
        return allowRnc;
    }

    /**
     * @param allowRnc
     *            The allowRnc to set.
     */
    public void setAllowRnc(boolean allowRnc) {
        this.allowRnc = allowRnc;
    }
}
