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

net.sf.saxon.java.JavaPlatform Maven / Gradle / Ivy

There is a newer version: 12.5
Show newest version
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2018-2022 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.java;

import net.sf.saxon.Configuration;
import net.sf.saxon.Platform;
import net.sf.saxon.dom.DOMEnvelope;
import net.sf.saxon.dom.DOMObjectModel;
import net.sf.saxon.expr.StaticContext;
import net.sf.saxon.expr.parser.RetainedStaticContext;
import net.sf.saxon.expr.sort.*;
import net.sf.saxon.functions.FunctionLibraryList;
import net.sf.saxon.lib.*;
import net.sf.saxon.om.NamespaceResolver;
import net.sf.saxon.pull.ActiveStAXSource;
import net.sf.saxon.regex.ARegularExpression;
import net.sf.saxon.regex.JavaRegularExpression;
import net.sf.saxon.regex.RegularExpression;
import net.sf.saxon.resource.ActiveSAXSource;
import net.sf.saxon.resource.ActiveStreamSource;
import net.sf.saxon.resource.StandardCollectionFinder;
import net.sf.saxon.str.UnicodeString;
import net.sf.saxon.trans.DynamicLoader;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.type.ExternalObjectType;
import net.sf.saxon.xpath.JAXPXPathStaticContext;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stax.StAXSource;
import javax.xml.transform.stream.StreamSource;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.URL;
import java.text.CollationKey;
import java.text.Collator;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Properties;

/**
 * Implementation of the Platform class containing methods specific to the Java platform
 * (as distinct from .NET). This is a singleton class with no instance data.
 */
public class JavaPlatform implements Platform {

    static boolean tryJdk9 = true;

    /**
     * The constructor is called during the static initialization of the Configuration
     */

    public JavaPlatform() {
    }

    /**
     * Get the default language for localization.
     *
     * @return the default language
     */
    public String getDefaultLanguage() {
        return Locale.getDefault().getLanguage();
    }

    /**
     * Get the default country for localization.
     *
     * @return the default country
     */
    public String getDefaultCountry() {
        return Locale.getDefault().getCountry();
    }

    /**
     * Read a resource file issued with the Saxon product
     *
     * @param filename the filename of the file to be read
     * @param messages List to be populated with messages in the event of failure
     * @return an InputStream for reading the file/resource
     */

    /*@Nullable*/
    public InputStream locateResource(String filename, List messages) {
        filename = "net/sf/saxon/data/" + filename;
        ClassLoader loader = null;
        try {
            loader = Thread.currentThread().getContextClassLoader();
        } catch (Exception err) {
            messages.add("Failed to getContextClassLoader() - continuing\n");
        }

        InputStream in = null;

        if (loader != null) {
            URL u = loader.getResource(filename);
            in = loader.getResourceAsStream(filename);
            if (in == null) {
                messages.add("Cannot read " + filename + " file located using ClassLoader " +
                                     loader + " - continuing\n");
            }
        }

        if (in == null) {
            loader = Configuration.class.getClassLoader();
            if (loader != null) {
                in = loader.getResourceAsStream(filename);
                if (in == null) {
                    messages.add("Cannot read " + filename + " file located using ClassLoader " +
                                         loader + " - continuing\n");
                }
            }
        }

        if (in == null) {
            // Means we're in a very strange class-loading environment, things are getting desperate
            URL url = ClassLoader.getSystemResource(filename);
            if (url != null) {
                try {
                    in = url.openStream();
                } catch (IOException ioe) {
                    messages.add("IO error " + ioe.getMessage() +
                                         " reading " + filename + " located using getSystemResource(): using defaults");
                    in = null;
                }
            }
        }
        //loaders.add(loader);
        return in;

    }


    /**
     * Checks if the supplied static context is an instance of the JAXP static context.
     * On Java we create namespace information from the JAXP XPath static context.
     * On the .NET platform we do nothing.
     *
     * @param retainedStaticContext the retained static context
     * @param sc                    the supplied static context
     * @return boolean
     * @since 9.7.0.5
     */

    @Override
    public boolean JAXPStaticContextCheck(RetainedStaticContext retainedStaticContext, StaticContext sc) {
        if (sc instanceof JAXPXPathStaticContext && !(((JAXPXPathStaticContext) sc).getNamespaceContext() instanceof NamespaceResolver)) {
            setNamespacesFromJAXP(retainedStaticContext, (JAXPXPathStaticContext) sc);
            return true;
        }
        return false;
    }

    /**
     * Create namespace information from the JAXP XPath static context. This
     * case needs special treatment because the JAXP static context holds namespace information
     * using a NamespaceContext object, which (absurdly) offers no way to iterate over all the
     * contained namespaces.
     *
     * @param retainedStaticContext the retained static context to be updated
     * @param sc                    JAXP static context
     */

    private void setNamespacesFromJAXP(RetainedStaticContext retainedStaticContext, JAXPXPathStaticContext sc) {
        final NamespaceContext nc = sc.getNamespaceContext();
        retainedStaticContext.setNamespaces(new NamespaceResolver() {

            @Override
            public String getURIForPrefix(String prefix, boolean useDefault) {
                return nc.getNamespaceURI(prefix);
            }

            @Override
            public Iterator iteratePrefixes() {
                throw new UnsupportedOperationException();
            }
        });

    }

    /**
     * Perform platform-specific initialization of the configuration
     */

    @Override
    public void initialize(Configuration config) {
        config.registerExternalObjectModel(DOMEnvelope.getInstance());
        config.registerExternalObjectModel(DOMObjectModel.getInstance());
        config.setCollectionFinder(new StandardCollectionFinder());
    }

    /**
     * Return true if this is the Java platform
     */

    @Override
    public boolean isJava() {
        return true;
    }

    /**
     * Return true if this is the .NET platform
     */

    @Override
    public boolean isDotNet() {
        return false;
    }

    /**
     * Get the platform version
     */

    @Override
    public String getPlatformVersion() {
        return "Java version " + System.getProperty("java.version");
    }

    /**
     * Get a suffix letter to add to the Saxon version number to identify the platform
     */

    @Override
    public String getPlatformSuffix() {
        return "J";
    }

    /**
     * Get the default DynamicLoader for the platform
     *
     * @return the default DynamicLoader
     */

    public IDynamicLoader getDefaultDynamicLoader() {
        return new DynamicLoader();
    }

    /**
     * Get a parser by instantiating the SAXParserFactory
     *
     * @return the parser (XMLReader)
     */

    @Override
    public XMLReader loadParser() {
        XMLReader parser;
        try {
            parser = SAXParserFactory.newInstance().newSAXParser().getXMLReader();
        } catch (ParserConfigurationException | SAXException err) {
            throw new TransformerFactoryConfigurationError(err);
        }
        return parser;
    }

    /**
     * Get a parser suitable for parsing XML fragments
     *
     * 

For background, see bugs 4127 and 4253. The problem is that to implement parse-xml-fragment(), * we need to set an EntityResolver on the returned parser. But if an Apache catalog resolver is * in use, the JAXP system properties may be set so that JAXP returns a custom XMLReader supplied * by the catalog resolver, and that XMLReader ignores any attempt to set an EntityResolver. So * we bypass JAXP and try to load the built-in parser within the JDK, which we know we can trust; * only if this fails (presumably because this is not the Oracle JDK) do we fall back to using a * JAXP-supplied parser. And if this turns out to ignore {@code setEntityResolver()} calls, we're * hosed. *

* * @return the parser (XMLReader) */ @Override public XMLReader loadParserForXmlFragments() { SAXParserFactory factory = null; if (tryJdk9) { try { // Try the JDK 9 approach (unless we know it ain't gonna work) @SuppressWarnings("JavaReflectionMemberAccess") Method method = SAXParserFactory.class.getMethod("newDefaultInstance"); Object result = method.invoke(null); factory = (SAXParserFactory)result; } catch (Exception e) { tryJdk9 = false; // keep trying } } if (factory == null) { // Try the JDK 8 approach (causes problems in JDK 9 with illegal access warnings) try { Class factoryClass = Class.forName("com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl"); factory = (SAXParserFactory) factoryClass.newInstance(); } catch (Exception e2) { // keep trying } } if (factory != null) { try { return factory.newSAXParser().getXMLReader(); } catch (Exception e) { // keep trying } } // Fallback - use whatever parser we can find return loadParser(); } /** * Convert a Source to an ActiveSource. This method is present in the Platform * because different Platforms accept different kinds of Source object. * * @param source A source object, typically the source supplied as the first * argument to {@link Transformer#transform(Source, Result)} * or similar methods. * @param config The Configuration. This provides the SourceResolver with access to * configuration information; it also allows the SourceResolver to invoke the * resolveSource() method on the Configuration object as a fallback implementation. * @return a source object that Saxon knows how to process. Return null if the Source object is not * recognized * @throws XPathException if the Source object is recognized but cannot be processed */ @Override public ActiveSource resolveSource(Source source, Configuration config) throws XPathException { if (source instanceof ActiveSource) { return (ActiveSource)source; } if (source instanceof SAXSource) { if (((SAXSource)source).getXMLReader() == null) { final XMLReader sourceParser = config.getSourceParser(); if (sourceParser.getEntityResolver() == null) { sourceParser.setEntityResolver(new StandardEntityResolver(config)); } ((SAXSource) source).setXMLReader(sourceParser); ActiveSAXSource activeSource = new ActiveSAXSource((SAXSource) source); activeSource.setParserPool(config::reuseSourceParser); return activeSource; } return new ActiveSAXSource((SAXSource)source); } if (source instanceof StreamSource) { return new ActiveStreamSource((StreamSource)source); } if (source instanceof StAXSource) { try { return ActiveStAXSource.fromStAXSource((StAXSource) source); } catch (XMLStreamException e) { throw new XPathException(e); } } return null; } /** * Obtain a collation with a given set of properties. The set of properties is extensible * and variable across platforms. Common properties with example values include lang=ed-GB, * strength=primary, case-order=upper-first, ignore-modifiers=yes, alphanumeric=yes. * Properties that are not supported are generally ignored; however some errors, such as * failing to load a requested class, are fatal. * * @param config the configuration object * @param props the desired properties of the collation * @param uri the collation URI * @return a collation with these properties * @throws XPathException if a fatal error occurs */ /*@Nullable*/ @Override public StringCollator makeCollation(Configuration config, Properties props, String uri) throws XPathException { return JavaCollationFactory.makeCollation(config, uri, props); } /** * Given a collation, determine whether it is capable of returning collation keys. * The essential property of collation keys * is that if two values are equal under the collation, then the collation keys are * equal under the equals() method. * * @param collation the collation, provided as a Comparator * @return true if this collation can supply collation keys */ @Override public boolean canReturnCollationKeys(StringCollator collation) { return !(collation instanceof SimpleCollation) || ((SimpleCollation) collation).getComparator() instanceof Collator; } /** * Given a collation, get a collation key. The essential property of collation keys * is that if two values are equal under the collation, then the collation keys are * compare correctly under the equals() method. * * @throws ClassCastException if the collation is not one that is capable of supplying * collation keys (this should have been checked in advance) */ @Override public AtomicMatchKey getCollationKey(SimpleCollation namedCollation, String value) { CollationKey ck = ((Collator) namedCollation.getComparator()).getCollationKey(value); return new CollationMatchKey(ck); } /** * No ICU features */ @Override public boolean hasICUCollator() { return false; } @Override public boolean hasICUNumberer() { return false; } /** * If available, make a collation using the ICU-J Library * * @param uri the collation URI (which will always be a UCA collation URI as defined in XSLT 3.0) * @param config the Saxon configuration * @return the collation, or null if not available * @throws XPathException if the URI is malformed in some way */ @Override public StringCollator makeUcaCollator(String uri, Configuration config) throws XPathException { UcaCollatorUsingJava collator = new UcaCollatorUsingJava(uri); if ("yes".equals(collator.getProperties().getProperty("numeric"))) { return new AlphanumericCollator(collator); } else { return collator; } } /** * Compile a regular expression * * @param config the Saxon configuration * @param regex the regular expression as a string * @param flags the value of the flags attribute * @param hostLanguage one of "XSD10", "XSD11", "XP20" or "XP30". Also allow combinations, e.g. "XP20/XSD11". * @param warnings if supplied, may capture warnings from the regular expression compiler * @return the compiled regular expression * @throws net.sf.saxon.trans.XPathException if the regular expression or the flags are invalid */ @Override public RegularExpression compileRegularExpression(Configuration config, UnicodeString regex, String flags, String hostLanguage, List warnings) throws XPathException { // recognize "!" as a flag to mean: use native Java regex syntax if (flags.contains("!")) { // undocumented return new JavaRegularExpression(regex, flags.replace("!", "")); } else { // recognize implementation-defined flags following a semicolon in the flags string boolean useJava = false; boolean useSaxon = false; int semi = flags.indexOf(';'); if (semi >= 0) { useJava = flags.indexOf('j', semi) >= 0; useSaxon = flags.indexOf('s', semi) >= 0; } if ("J".equals(config.getDefaultRegexEngine()) && !useSaxon) { useJava = true; } if (useJava) { flags = flags.substring(0, semi); return new JavaRegularExpression(regex, flags); } else { return new ARegularExpression(regex, flags, hostLanguage, warnings, config); } } } /** * Add the platform-specific function libraries to a function library list. This version * of the method does nothing * * @param list the function library list that is to be extended * @param config the Configuration * @param hostLanguage the host language, for example Configuration.XQUERY */ public void addFunctionLibraries(FunctionLibraryList list, Configuration config, int hostLanguage) { // do nothing } @Override public ExternalObjectType getExternalObjectType(Configuration config, String uri, String localName) { throw new UnsupportedOperationException("getExternalObjectType for Java"); } /** * Return the name of the directory in which the software is installed (if available) * * @param edition The edition of the software that is loaded ("HE", "PE", or "EE") * @param config the Saxon configuration * @return the name of the directory in which Saxon is installed, if available, or null otherwise */ @Override public String getInstallationDirectory(String edition, Configuration config) { try { return System.getenv("SAXON_HOME"); } catch (SecurityException e) { return null; } } /** * Register all the external object models that are provided as standard * with the relevant edition of Saxon for this Configuration * * @since 9.3 */ @Override public void registerAllBuiltInObjectModels(Configuration config) { // No action for Saxon-HE } /** * Set the default XML parser to be loaded by the SAXParserFactory on this platform. * Needed because the Apache catalog resolver uses the SAXParserFactory to instantiate * a parser, and if not customized this causes a failure on the .NET platform. * * @since 9.4 */ @Override public void setDefaultSAXParserFactory(Configuration config) { // No action for Saxon on Java } @Override public ModuleURIResolver makeStandardModuleURIResolver(Configuration config) { return new StandardModuleURIResolver(config); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy