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

net.sf.saxon.dotnet.DotNetPlatform Maven / Gradle / Ivy

Go to download

Saxon a complete and conformant implementation of the XSLT 2.0, XQuery 1.0, and XPath 2.0 Recommendations published on 23 January 2007 by W3C

The newest version!
package net.sf.saxon.dotnet;

import cli.System.Environment;
import cli.System.Reflection.Assembly;
import cli.System.Uri;
import cli.System.Xml.*;
import net.sf.saxon.*;
import net.sf.saxon.event.PipelineConfiguration;
import net.sf.saxon.functions.*;
import net.sf.saxon.om.NamespaceConstant;
import net.sf.saxon.om.Validation;
import net.sf.saxon.pull.PullProvider;
import net.sf.saxon.pull.PullSource;
import net.sf.saxon.regex.RegularExpression;
import net.sf.saxon.sort.CodepointCollator;
import net.sf.saxon.sort.NamedCollation;
import net.sf.saxon.sort.StringCollator;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.trans.Err;
import net.sf.saxon.type.SchemaType;

import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
import javax.xml.transform.stream.StreamSource;
import java.io.InputStream;
import java.io.Reader;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.MalformedURLException;
import java.util.Properties;
import java.util.StringTokenizer;

/**
 * Implementation of the Platform interface containing methods appropriate to the .NET platform
 */

public class DotNetPlatform implements Platform {

    private static DotNetPlatform theInstance = new DotNetPlatform();

    /**
     * Get the singular instance of this class
     * @return the singular DotNetPlatform
     */

    public static DotNetPlatform getInstance() {
        return theInstance;
    }

    /**
     * Create a link to the extended character sets in charsets.jar. This makes these accessible for
     * loading.
     */

    //private static CharsetProvider provider = new sun.nio.cs.ext.ExtendedCharsets();    

    private DotNetPlatform(){}

    /**
     * Get the fully qualified class name of the Saxon-SA Configuration class
     * @return the fully-qualified class name
     */

    public static String getSaxonSaFullyQualifiedClassName() {
//        return "com.saxonica.validate.SchemaAwareConfiguration";
        return "com.saxonica.validate.SchemaAwareConfiguration, " +
                "saxon9sa, " +
                "Version=" + Version.getProductVersion() + ", " +
                "Culture=neutral, " +
                "PublicKeyToken=e1fdd002d5083fe6";
    }

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

    public void initialize(Configuration config) {
        config.setURIResolver(new DotNetURIResolver(new XmlUrlResolver()));
        config.setModuleURIResolver(new DotNetStandardModuleURIResolver(new XmlUrlResolver()));
        config.setExtensionFunctionFactory("clitype", new DotNetExtensionFunctionFactory(config));
        config.setExtensionFunctionFactory("java", new JavaExtensionFunctionFactory(config));
    }

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

    public boolean isJava() {
        return false;
    }

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

    public boolean isDotNet() {
        return true;
    }

    /**
     * Construct an absolute URI from a relative URI and a base URI
     *
     * @param relativeURI the relative URI
     * @param base        the base URI
     * @return the absolutized URI
     * @throws java.net.URISyntaxException
     */

    public URI makeAbsoluteOLD(String relativeURI, String base) throws URISyntaxException {

        // It's not entirely clear why the .NET product needs a different version of this method.
        // Possibly because of bugs in GNU classpath.

        try {
            //noinspection ConstantIfStatement
            if (false) {
                // dummy code to allow the exception to be caught
                throw new cli.System.UriFormatException();
            }
            XmlUrlResolver resolver = new XmlUrlResolver();
            Uri fulluri;
            if (base != null) {
                Uri baseUri = new Uri(base);
                fulluri = resolver.ResolveUri(baseUri, relativeURI);
            }
            else {
                fulluri = resolver.ResolveUri(null, relativeURI.replaceAll("file:", ""));
            }
            return new URI(fulluri.get_AbsoluteUri());
        } catch (cli.System.UriFormatException e) {
            throw new URISyntaxException(base + " + " + relativeURI, e.getMessage());
        }
    }

    public URI makeAbsolute(String relativeURI, String base) throws URISyntaxException {
            URI absoluteURI;
            // System.err.println("makeAbsolute " + relativeURI + " against base " + base);
            if (relativeURI == null) {
                absoluteURI = new URI(ResolveURI.escapeSpaces(base));
                if (!absoluteURI.isAbsolute()) {
                    throw new URISyntaxException(base, "Relative URI not supplied, so base URI must be absolute");
                } else {
                    return absoluteURI;
                }
            }
            relativeURI = ResolveURI.escapeSpaces(relativeURI);
            base = ResolveURI.escapeSpaces(base);
            try {
                if (base==null || base.length() == 0) {
                    absoluteURI = new URI(relativeURI);
                    if (!absoluteURI.isAbsolute()) {
                        String expandedBase = ResolveURI.tryToExpand(base);
                        if (!expandedBase.equals(base)) { // prevent infinite recursion
                            return makeAbsolute(relativeURI, expandedBase);
                        }
                    }
                } else if (base != null && (base.startsWith("jar:") || base.startsWith("file:////"))) {

                    // jar: URIs can't be resolved by the java.net.URI class, because they don't actually
                    // conform with the RFC standards for hierarchic URI schemes (quite apart from not being
                    // a registered URI scheme). But they seem to be widely used.

                    // URIs starting file://// are accepted by the java.net.URI class, they are used to
                    // represent Windows UNC filenames. However, the java.net.URI algorithm for resolving
                    // a relative URI against such a base URI fails to produce a usable UNC filename (it's not
                    // clear whether Java is implementing RFC 3986 correctly here, it depends on interpretation).
                    // So we use the java.net.URL algorithm for this case too, because it works.

                    try {
                        URL baseURL = new URL(base);
                        URL absoluteURL = new URL(baseURL, relativeURI);
                        absoluteURI = new URI(absoluteURL.toString());
                            // TODO: JDK1.5: use absoluteURL.toURI()
                    } catch (MalformedURLException err) {
                        throw new URISyntaxException(base + " " + relativeURI, err.getMessage());
                    }
                } else {
                    URI baseURI;
                    try {
                        baseURI = new URI(base);
                    } catch (URISyntaxException e) {
                        throw new URISyntaxException(base, "Invalid base URI: " + e.getMessage());
                    }
                    try {
                        new URI(relativeURI);   // for validation only
                    } catch (URISyntaxException e) {
                        throw new URISyntaxException(base, "Invalid relative URI: " + e.getMessage());
                    }
                    absoluteURI = (relativeURI.length()==0 ?
                                     baseURI :
                                     baseURI.resolve(relativeURI)
                                 );
                }
            } catch (IllegalArgumentException err0) {
                // can be thrown by resolve() when given a bad URI
                throw new URISyntaxException(relativeURI, "Cannot resolve URI against base " + Err.wrap(base));
            }

            return absoluteURI;
        }
    

    /**
     * Get the platform version
     */

    public String getPlatformVersion() {
        return ".NET " + Environment.get_Version().ToString() +
                " on " + Environment.get_OSVersion().ToString();
    }

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

    public String getPlatformSuffix() {
        return "N";
    }

    /**
     * Convert a StreamSource to either a SAXSource or a PullSource, depending on the native
     * parser of the selected platform
     *
     * @param pipe the pipeline configuration
     * @param input the supplied StreamSource
     * @param validation indicates whether schema validation is required, adn in what mode
     * @param dtdValidation true if DTD validation is required
     * @param stripspace defines the requird whitespace handling
     * @return the PullSource or SAXSource, initialized with a suitable parser, or the original
     * input Source, if now special handling is required or possible. May also return an AugmentedSource
     * that wraps one of these.
     */

    public Source getParserSource(PipelineConfiguration pipe, StreamSource input, int validation, boolean dtdValidation,
                                  int stripspace) {
        Configuration config = pipe.getConfiguration();
        boolean preferJaxp = ((Boolean)config.getConfigurationProperty(FeatureKeys.PREFER_JAXP_PARSER)).booleanValue();
        InputStream is = input.getInputStream();
        if (is != null) {
            if (is instanceof DotNetInputStream && !preferJaxp) {
                XmlReader parser = new XmlTextReader(input.getSystemId(),
                        ((DotNetInputStream)is).getUnderlyingStream());
                ((XmlTextReader)parser).set_WhitespaceHandling(WhitespaceHandling.wrap(WhitespaceHandling.All));
                ((XmlTextReader)parser).set_Normalization(true);
                if (pipe.getURIResolver() instanceof DotNetURIResolver) {
                    ((XmlTextReader)parser).set_XmlResolver(
                            ((DotNetURIResolver)pipe.getURIResolver()).getXmlResolver());
                }
                
                // Always need a validating parser, because that's the only way to get entity references expanded
                parser = new XmlValidatingReader(parser);
                if (dtdValidation) {
                    ((XmlValidatingReader)parser).set_ValidationType(ValidationType.wrap(ValidationType.DTD));
                } else {
                    ((XmlValidatingReader)parser).set_ValidationType(ValidationType.wrap(ValidationType.None));
                }
                PullProvider provider = new DotNetPullProvider(parser);
                //provider = new PullTracer(provider);
                PullSource ps = new PullSource(provider);
                //System.err.println("Using PullSource(stream)");
                ps.setSystemId(input.getSystemId());
                if (validation == Validation.DEFAULT) {
                    return ps;
                } else {
                    AugmentedSource as = AugmentedSource.makeAugmentedSource(ps);
                    as.setSchemaValidationMode(validation);
                    return as;
                }
            } else {
                return input;
            }
        }
        Reader reader = input.getReader();
        if (reader != null) {
            if (reader instanceof DotNetReader && !preferJaxp) {
                XmlReader parser = new XmlTextReader(input.getSystemId(),
                        ((DotNetReader)reader).getUnderlyingTextReader());
                ((XmlTextReader)parser).set_Normalization(true);
                ((XmlTextReader)parser).set_WhitespaceHandling(WhitespaceHandling.wrap(WhitespaceHandling.All));
                if (pipe.getURIResolver() instanceof DotNetURIResolver) {
                    ((XmlTextReader)parser).set_XmlResolver(
                            ((DotNetURIResolver)pipe.getURIResolver()).getXmlResolver());
                }

                // Always need a validating parser, because that's the only way to get entity references expanded
                parser = new XmlValidatingReader(parser);
                if (dtdValidation) {
                    ((XmlValidatingReader)parser).set_ValidationType(ValidationType.wrap(ValidationType.DTD));
                } else {
                    ((XmlValidatingReader)parser).set_ValidationType(ValidationType.wrap(ValidationType.None));
                }
                PullSource ps = new PullSource(new DotNetPullProvider(parser));
                //System.err.println("Using PullSource(reader)");
                ps.setSystemId(input.getSystemId());
                if (validation == Validation.DEFAULT) {
                    return ps;
                } else {
                    AugmentedSource as = AugmentedSource.makeAugmentedSource(ps);
                    as.setSchemaValidationMode(validation);
                    return as;
                }
            } else {
                return input;
            }
        }
        String uri = input.getSystemId();
        if (uri != null) {
            try {
                Source r = pipe.getURIResolver().resolve(uri, null);
                if (r == null) {
                    return input;
                } else if (r instanceof AugmentedSource) {
                    Source r2 = ((AugmentedSource)r).getContainedSource();
                    if (r2 instanceof StreamSource) {
                        r2 = getParserSource(pipe, (StreamSource)r2, validation, dtdValidation, stripspace);
                        return r2;
                    } else {
                        return r2;
                    }
                } else if (r instanceof StreamSource && r != input) {
                    Source r2 = getParserSource(pipe, (StreamSource)r, validation, dtdValidation, stripspace);
                    AugmentedSource as = AugmentedSource.makeAugmentedSource(r2);
                    as.setPleaseCloseAfterUse(true);
                    return as;
                } else {
                    return r;
                }
            } catch (TransformerException err) {
                return input;
            }
        }
        return input;
    }

    /**
     * Create a compiled regular expression
     * @param regex the source text of the regular expression, in XML Schema or XPath syntax
     * @param xmlVersion One of {@link net.sf.saxon.Configuration#XML10} or {@link net.sf.saxon.Configuration#XML11}
     * @param syntax one of XPath, XMLSchema, or native, indicating the regular expression syntax used
     * @param flags the flags argument as supplied to functions such as fn:matches(), in string form @throws XPathException if the syntax of the regular expression or flags is incorrect @return the compiled regular expression
     */

    public RegularExpression compileRegularExpression(
            CharSequence regex, int xmlVersion, int syntax, CharSequence flags)
    throws XPathException {
        return new DotNetRegularExpression(regex, xmlVersion, syntax, flags);
    }

    /**
     * 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
     */

    public StringCollator makeCollation(Configuration config, Properties props, String uri) throws XPathException {
        return DotNetCollationFactory.makeCollation(config, props, uri);
    }

    /**
     * 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
     */

    public boolean canReturnCollationKeys(StringCollator collation) {
        return collation instanceof DotNetComparator ||
                collation instanceof CodepointCollator;
    }

    /**
     * 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
     * equal 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)
     */

    public Object getCollationKey(NamedCollation namedCollation, String value) {
        DotNetComparator c = (DotNetComparator)((NamedCollation)namedCollation).getCollation();
        return c.getCollationKey(value);
    }

    /**
     * Make the default extension function factory appropriate to the platform
     */

    public void makeExtensionLibrary(Configuration config) {
        config.setExtensionBinder("clitype", new DotNetExtensionLibrary(config));
        config.setExtensionBinder("java", new JavaExtensionLibrary(config));
    }

    /**
     * Add the platform-specific function libraries to a function library list. The libraries
     * that are added are those registered with the Configuration using
     *  {@link Configuration#setExtensionBinder(String, net.sf.saxon.functions.FunctionLibrary)}
     * @param list the function library list that is to be extended
     * @param config the Configuration
     */

    public void addFunctionLibraries(FunctionLibraryList list, Configuration config, int hostLanguage) {
        list.addFunctionLibrary(config.getExtensionBinder("java"));
        list.addFunctionLibrary(config.getExtensionBinder("clitype"));
    }

    public void declareJavaClass(FunctionLibrary library, String uri, Class theClass) {
        throw new IllegalStateException("saxon:script cannot be used on .NET");
    }

    /**
     * Dynamically load a .NET class with a given name, starting with a URI that contains information
     * about the type and the assembly
     * @param uri A URI in the form
     * clitype:Full.Type.Name?param=value;
     * 

Query parameters in the URL may be separated by semicolons or ampersands. The recognized parameters * are:

* *
    *
  • asm - the name of the assembly
  • *
  • ver - the version of the assembly
  • *
  • loc - the culture
  • *
  • sn - the strong name
  • *
  • from - the location to load from
  • *
  • partialname - the partial name of the assembly
  • *
* @param baseURI base URI of the expression containing an extension function call (null if not known). This * is used to resolve any relative URI appearing in the "from" parameter of the query part of the namespace URI * @param debug true if diagnostic tracing is enabled @return the cli.System.Type object representing the * dynamically loaded class * @return the successfully loaded Type */ public cli.System.Type dynamicLoad(String uri, String baseURI, boolean debug) throws XPathException { if (uri.startsWith("clitype:")) { uri = uri.substring(8); } else { if (debug) { System.err.println("Unrecognized .NET external URI: " + uri); } throw new XPathException("Unrecognized .NET external URI: " + uri); } String typeName; String queryParams; int q = uri.indexOf('?'); if (q == 0 || q == uri.length()-1) { if (debug) { System.err.println("Misplaced '?' in " + uri); } throw new XPathException("Misplaced '?' in " + uri); } if (q > 0) { typeName = uri.substring(0, q); queryParams = uri.substring(q+1); } else { typeName = uri; queryParams = ""; } if ("".equals(queryParams)) { cli.System.Type type = cli.System.Type.GetType(typeName); if (type == null && debug) { try { //noinspection ConstantIfStatement if (false) throw new cli.System.TypeLoadException(); //noinspection UnusedDeclaration cli.System.Type type2 = cli.System.Type.GetType(typeName, true); } catch (Exception err) { System.err.println("Failed to load type " + typeName + ": " + err.getMessage()); return null; } catch (cli.System.TypeLoadException err) { System.err.println("Failed to load type " + typeName + ": " + err.getMessage()); return null; } System.err.println("Failed to load type " + typeName); } return type; } else { // AssemblyName aname = new AssemblyName(); String loadFrom = null; String href = null; String partialName = null; String asmName = null; String loc = null; String ver = null; String sn = null; StringTokenizer tok = new StringTokenizer(queryParams, ";&"); while (tok.hasMoreTokens()) { String kv = tok.nextToken(); int eq = kv.indexOf('='); if (eq <= 0) { if (debug) { System.err.println("Bad keyword=value pair in " + kv); } throw new XPathException("Bad keyword=value pair in " + kv); } String keyword = kv.substring(0, eq); String value = kv.substring(eq+1); if (keyword.equals("asm")) { asmName = value; } else if (keyword.equals("ver")) { ver = value; } else if (keyword.equals("loc")) { loc = value; } else if (keyword.equals("sn")) { sn = value; } else if (keyword.equals("from")) { loadFrom = value; } else if (keyword.equals("href")) { href = value; } else if (keyword.equals("partialname")) { partialName = value; } else if (debug) { System.err.println("Unrecognized keyword in URI: " + keyword + " (ignored)"); } } Assembly asm; try { //noinspection ConstantIfStatement if (false) throw new cli.System.IO.FileNotFoundException(); if (partialName != null) { asm = Assembly.LoadWithPartialName(partialName); } else if (loadFrom != null) { String abs = loadFrom; if (baseURI != null) { abs = makeAbsolute(loadFrom, baseURI).toString(); if (debug) { System.err.println("Absolute location URI: " + abs); } } asm = Assembly.LoadFrom(abs); } else if (href != null) { asm = Assembly.LoadFrom(href); } else { String longName = asmName; if (ver != null) { longName += ", Version=" + ver; } if (loc != null) { longName += ", Culture=" + loc; } if (sn != null) { longName += ", PublicKeyToken=" + sn; } asm = Assembly.Load(longName); // asm = Assembly.Load(aname); } if (debug) { System.err.println("Assembly " + asm.get_FullName() + " successfully loaded"); System.err.println("Assembly codebase (" + (asm.get_GlobalAssemblyCache() ? "GAC" : "local") + "): " + asm.get_CodeBase()); } } catch (cli.System.IO.FileNotFoundException err) { if (debug) { System.err.println("Failed to load assembly " + uri + ": " + err.getMessage() + " (FileNotFoundException)"); } throw new XPathException("Failed to load assembly " + uri + ": " + err.getMessage()); } catch (Throwable err) { if (debug) { System.err.println("Failed to load assembly " + uri + ": " + err.getMessage() + " (" + err.getClass().getName() + ")"); } throw new XPathException("Failed to load assembly " + uri + ": " + err.getMessage()); } cli.System.Type type = asm.GetType(typeName); if (type == null) { if (debug) { System.err.println("Type " + typeName + " not found in assembly"); } throw new XPathException("Type " + typeName + " not found in assembly"); } return type; } } public SchemaType getExternalObjectType(Configuration config, String uri, String localName) { if (uri.equals(NamespaceConstant.DOT_NET_TYPE)) { return new DotNetExternalObjectType(cli.System.Type.GetType(localName), config); } else { throw new IllegalArgumentException("Type is not in .NET namespace"); } } } // // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License"); // you may not use this file except in compliance with the License. You may obtain a copy of the // License at http://www.mozilla.org/MPL/ // // Software distributed under the License is distributed on an "AS IS" basis, // WITHOUT WARRANTY OF ANY KIND, either express or implied. // See the License for the specific language governing rights and limitations under the License. // // The Original Code is: all this file. // // The Initial Developer of the Original Code is Michael H. Kay // // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved. // // Contributor(s): none. //




© 2015 - 2024 Weber Informatics LLC | Privacy Policy