All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.saxon.lib.CatalogResourceResolver Maven / Gradle / Ivy

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2018-2023 Saxonica Limited
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

package net.sf.saxon.lib;

import net.sf.saxon.resource.TypedStreamSource;
import net.sf.saxon.trans.XPathException;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.ext.EntityResolver2;
import org.xmlresolver.*;

import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
import java.io.IOException;
import java.util.Collections;

/**
 * The standard implementation of the {@link ResourceResolver} interface for use with catalogs.
 * @since 11
 */

public class CatalogResourceResolver implements
        ResourceResolver, ConfigurableResourceResolver,
        EntityResolver, EntityResolver2 {

    private org.xmlresolver.Resolver catalogBasedResolver;

    /**
     * Creates a new CatalogResourceResolver resolver with a default {@link org.xmlresolver.XMLResolverConfiguration}.
     * This default configuration will read a configuration file from the class path
     * and interrogate system properties to determine its initial configuration.
     *
     * The configuration can be updated by calling {@link #setFeature}. Alternatively,
     * the constructor can be passed an existing resolver.
     */
    public CatalogResourceResolver() {
        // We explicitly pass in an empty list so that in the absence of any other configuration,
        // the resolver doesn't use ./catalog.xml as a default. Historically, Saxon didn't use a
        // resolver unless a catalog was specified, so this default could have unexpected consequences.
        catalogBasedResolver = new Resolver(new XMLResolverConfiguration(null, Collections.emptyList()));
        catalogBasedResolver.getConfiguration().setFeature(ResolverFeature.THROW_URI_EXCEPTIONS, true);
    }

    /**
     * Creates a new CatlaogResourceResolver using the provided resolver as its underlying resolver.
     * @param resolver The resolver to wrap.
     */
    public CatalogResourceResolver(Resolver resolver) {
        this.catalogBasedResolver = resolver;
    }

    /**
     * Calls {@link org.xmlresolver.XMLResolverConfiguration#getFeature} on the underlying
     * resolver configuration.
     * @param feature The feature setting
     * @param  The feature type
     * @return The value for the specified feature.
     */
    public  T getFeature(ResolverFeature feature) {
        if (catalogBasedResolver == null) {
            return null;
        }
        return catalogBasedResolver.getConfiguration().getFeature(feature);
    }

    /**
     * Calls {@link org.xmlresolver.XMLResolverConfiguration#setFeature} on the underlying
     * resolver configuration.
     * @param feature The feature setting
     * @param value The desired value for that feature
     * @param  The feature type
     * @throws NullPointerException if the underlying resolver is null. Some features will
     * also throw this exception if the value provided is null and that's not a meaningful
     * feature value.
     */
    public  void setFeature(ResolverFeature feature, T value) {
        if (catalogBasedResolver == null) {
            throw new NullPointerException();
        }
        catalogBasedResolver.getConfiguration().setFeature(feature, value);
    }

    /**
     * Resolve a resource request.
     *
     * If catalog resolution fails and a fallback URI resolver has been chained, the
     * fallback resolver will be attempted.
     *
     * @param request details of the resource request
     * @return The resolved resource, or null if it could not be resolved.
     * @throws XPathException if an error occurs during the attempt to resolve the URI.
     */
    @Override
    public Source resolve(ResourceRequest request) throws XPathException {
        if (catalogBasedResolver != null) {
            CatalogResolver cr = catalogBasedResolver.getCatalogResolver();
            ResolvedResource rr;
            if (request.uriIsNamespace) {
                try {
                    rr = cr.resolveNamespace(
                            request.uri, request.baseUri, request.nature, request.purpose);
                    if (rr == null) {
                        return null;
                    }

                } catch (IllegalArgumentException e) {
                    throw new XPathException("Exception from catalog resolver resolveNamespace(): ", e);
                }
            } else if (ResourceRequest.DTD_NATURE.equals(request.nature)) {
                return null;
            } else if (ResourceRequest.EXTERNAL_ENTITY_NATURE.equals(request.nature)) {
                try {
                    rr = cr.resolveEntity(
                            request.entityName, request.publicId, request.baseUri, request.uri);
                } catch (IllegalArgumentException e) {
                    throw new XPathException("Exception from catalog resolver resolveEntity():", e);
                }
            } else {
                String href = request.relativeUri == null ? request.uri : request.relativeUri;
                String baseUri = request.baseUri == null ? request.uri : request.baseUri;
                try {
                    rr = cr.resolveURI(href, baseUri);
                } catch (IllegalArgumentException e) {
                    throw new XPathException("Exception from catalog resolver resolverURI()", e);
                }
            }
            if (rr != null) {
                TypedStreamSource result = new TypedStreamSource();
                result.setSystemId(rr.getResolvedURI().toString());
                result.setInputStream(rr.getInputStream());
                result.setContentType(rr.getContentType());
                return result;
            }
        }
        return null;
    }

    /**
     * Resolves an external subset. This method is part of the {@link org.xml.sax.ext.EntityResolver2} interface.
     * The resolver will attempt to find the external subset through the catalog resolver.
     *
     * If catalog resolution fails and a fallback EntityResolver2 resolver has been chained, the
     * fallback resolver will be attempted.
     *
     * @param name The doctype name.
     * @param baseURI The base URI.
     * @return The external subset, or null if it could not be found.
     * @throws SAXException If an error occurs during the attempt to resolve the external subset.
     * @throws IOException If it isn't possible to create the input source or if the base URI is invalid.
     */
    @Override
    public InputSource getExternalSubset(String name, String baseURI) throws SAXException, IOException {
        InputSource result = null;
        if (catalogBasedResolver != null) {
            result = catalogBasedResolver.getExternalSubset(name, baseURI);
        }
//        if (result == null && nextEntityResolver2 != null) {
//            result = nextEntityResolver2.getExternalSubset(name, baseURI);
//        }
        return result;
    }

    /**
     * Resolves an entity. This method attempts to resolve the entity with the catalog resolver.
     *
     * If catalog resolution fails and a fallback EntityResolver2 resolver has been chained, the
     * fallback resolver will be attempted. If that fails and a further EntityResolver has been chained,
     * that fallback will also be attempted.
     *
     * Depending on whether various aspects of the entity are provided (public and system identifiers,
     * the name and baseURI, etc.), different aspects of the catalog will be queried. Not all parsers
     * provide all of these parameters. It's common for the name and baseURI to be null, for example.
     * If the parser doesn't provide them, then the catalog resolver will not be able to resolve with them.
     *
     * @param name The name of the entity, often null
     * @param publicId The public identifier of the entity, often null
     * @param baseURI The base URI of the entity, often null
     * @param systemId The system identifier of the entity
     * @return The entity, or null if it could not be found.
     * @throws SAXException If an error occurs during the attempt to resolve the external subset.
     * @throws IOException If it isn't possible to create the input source or if the base URI is invalid.
     */
    @Override
    public InputSource resolveEntity(String name, String publicId, String baseURI, String systemId) throws SAXException, IOException {
        try {
            InputSource result = null;
            if (catalogBasedResolver != null) {
                result = catalogBasedResolver.resolveEntity(name, publicId, baseURI, systemId);
            }
//            if (result == null && nextEntityResolver2 != null) {
//                result = nextEntityResolver2.resolveEntity(name, publicId, baseURI, systemId);
//            }
//            if (result == null && nextEntityResolver != null && nextEntityResolver != nextEntityResolver2) {
//                result = nextEntityResolver.resolveEntity(publicId, systemId);
//            }
            return result;
        } catch (IllegalArgumentException e) {
            if (e.getCause() instanceof IOException) {
                throw (IOException)e.getCause();
            } else {
                throw new SAXException(e);
            }
        }
    }

    /**
     * Resolves an entity. This method attempts to resolve the entity with the catalog resolver.
     *
     * If catalog resolution fails and a fallback EntityResolver2 resolver has been chained, the
     * fallback resolver will be attempted. If that fails and a further EntityResolver has been chained,
     * that fallback will also be attempted.
     *
     * @param publicId The public identifier of the entity, often null
     * @param systemId The system identifier of the entity
     * @return The entity, or null if it could not be found.
     * @throws SAXException If an error occurs during the attempt to resolve the external subset.
     * @throws IOException If it isn't possible to create the input source or if the base URI is invalid.
     */
    @Override
    public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
        InputSource result = null;
        if (catalogBasedResolver != null) {
            result = catalogBasedResolver.resolveEntity(publicId, systemId);
        }
//        if (result == null && nextEntityResolver2 != null) {
//            result = nextEntityResolver2.resolveEntity(publicId, systemId);
//        }
//        if (result == null && nextEntityResolver != null && nextEntityResolver != nextEntityResolver2) {
//            result = nextEntityResolver.resolveEntity(publicId, systemId);
//        }
        return result;
    }

    /**
     * Resolves a URI that is known to be a namespace URI.
     *
     * This intereface allows a resolver to request a particular kind of resource (one with a particular nature,
     * possibly for a particular purpose) for a URI. The URI is usually the namespace URI. Namespace URIs are
     * often not usefully resolvable on the web, but a catalog resolver can still offer resolution.
     *
     * If neither a nature or a purpose are provided, or if using them produces no results, this method
     * simply attempts to lookup the URI in the catalog. If that also fails, and if a fallback namespace
     * resolver has been chained, resolution will be attempted with the fallback resolver.
     *
     * @param uri The namespace URI
     * @param nature The nature of the resource requested, for example, the URI of the media type
     * @param purpose The purpose of the request, for example "validation"
     * @return The resource or null if it could not be found.
     * @throws TransformerException if an error occurs during the attempt to resolve the URI.
     */
    //@Override
    public Source resolveNamespace(String uri, String nature, String purpose) throws TransformerException {
        Source result = null;
        if (catalogBasedResolver != null) {
            result = catalogBasedResolver.resolveNamespace(uri, nature, purpose);
        }
//        if (catalogBasedResolver == null && nextNamespaceResolver != null) {
//            result = nextNamespaceResolver.resolveNamespace(uri, nature, purpose);
//        }
        return result;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy