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

com.xmlcalabash.core.XProcRuntime Maven / Gradle / Ivy

There is a newer version: 1.1.20-p16-98
Show newest version
/*
 * XProcRuntime.java
 *
 * Copyright 2008 Mark Logic Corporation.
 * All rights reserved.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */

package com.xmlcalabash.core;

import com.nwalsh.annotations.SaxonExtensionFunction;
import com.xmlcalabash.config.XProcConfigurer;
import com.xmlcalabash.functions.BaseURI;
import com.xmlcalabash.functions.Cwd;
import com.xmlcalabash.functions.IterationPosition;
import com.xmlcalabash.functions.IterationSize;
import com.xmlcalabash.functions.ResolveURI;
import com.xmlcalabash.functions.StepAvailable;
import com.xmlcalabash.functions.SystemProperty;
import com.xmlcalabash.functions.ValueAvailable;
import com.xmlcalabash.functions.VersionAvailable;
import com.xmlcalabash.functions.XPathVersionAvailable;
import com.xmlcalabash.functions.XProcExtensionFunctionDefinition;
import com.xmlcalabash.io.DataStore;
import com.xmlcalabash.io.FallbackDataStore;
import com.xmlcalabash.io.FileDataStore;
import com.xmlcalabash.io.HttpClientDataStore;
import com.xmlcalabash.io.ReadableData;
import com.xmlcalabash.io.ReadablePipe;
import com.xmlcalabash.io.URLDataStore;
import com.xmlcalabash.model.DeclareStep;
import com.xmlcalabash.model.DeclarationScope;
import com.xmlcalabash.model.Parser;
import com.xmlcalabash.model.PipelineLibrary;
import com.xmlcalabash.runtime.XLibrary;
import com.xmlcalabash.runtime.XPipeline;
import com.xmlcalabash.runtime.XRootStep;
import com.xmlcalabash.runtime.XStep;
import com.xmlcalabash.util.DefaultXProcConfigurer;
import com.xmlcalabash.util.DefaultXProcMessageListener;
import com.xmlcalabash.util.Input;
import com.xmlcalabash.util.JSONtoXML;
import com.xmlcalabash.util.Output;
import com.xmlcalabash.util.S9apiUtils;
import com.xmlcalabash.util.StepErrorListener;
import com.xmlcalabash.util.TreeWriter;
import com.xmlcalabash.util.URIUtils;
import com.xmlcalabash.util.XProcSystemPropertySet;
import com.xmlcalabash.util.XProcURIResolver;
import com.xmlcalabash.util.XProcURIResolverX;
import net.sf.saxon.Configuration;
import net.sf.saxon.lib.ExtensionFunctionDefinition;
import net.sf.saxon.lib.FeatureKeys;
import net.sf.saxon.s9api.ExtensionFunction;
import net.sf.saxon.s9api.Processor;
import net.sf.saxon.s9api.QName;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.Serializer;
import net.sf.saxon.s9api.XdmDestination;
import net.sf.saxon.s9api.XdmNode;
import net.sf.saxon.s9api.XsltCompiler;
import net.sf.saxon.s9api.XsltExecutable;
import net.sf.saxon.s9api.XsltTransformer;
import org.apache.http.client.CookieStore;
import org.apache.http.client.HttpClient;
import org.apache.http.client.utils.HttpClientUtils;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.StandardHttpRequestRetryHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;

import javax.xml.transform.URIResolver;
import javax.xml.transform.sax.SAXSource;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.Vector;

import static java.lang.String.format;

/**
 *
 * @author ndw
 */
public class XProcRuntime implements DeclarationScope {
    protected Logger logger = LoggerFactory.getLogger(XProcRuntime.class);
    private Processor processor = null;
    private Parser parser = null;
    private XProcURIResolver uriResolver = null;
    private XProcConfiguration config = null;
    private QName errorCode = null;
    private XdmNode errorNode = null;
    private String errorMessage = null;
    private Hashtable declaredSteps = new Hashtable ();
    private DeclareStep pipeline = null;
    private XPipeline xpipeline = null;
    private static String episode = null;
    private Hashtable> collections = null;
    private URI staticBaseURI = null;
    private URI baseURI = null;
    private boolean allowGeneralExpressions = true;
    private boolean allowXPointerOnText = true;
    private boolean allowTextResults = true;
    private boolean transparentJSON = false;
    private String jsonFlavor = JSONtoXML.MARKLOGIC;
    private boolean useXslt10 = false;
    private boolean htmlSerializer = false;
    private XProcData xprocData = null;
    private XProcMessageListener msgListener = null;
    private PipelineLibrary standardLibrary = null;
    private HttpClient httpClient;
    private Map cookieStores;
    private DataStore dataStore;
    private XProcConfigurer configurer = null;
    private String htmlParser = null;
    private Vector exFuncs = new Vector();
    private Vector systemPropertySets = new Vector();

    private Output profile = null;
    private Hashtable profileHash = null;
    private TreeWriter profileWriter = null;
    private QName profileProfile = new QName("http://xmlcalabash.com/ns/profile", "profile");
    private QName profileType = new QName("", "type");
    private QName profileName = new QName("", "name");
    private QName profileHref = new QName("", "href");
    private QName profileLine = new QName("", "line");
    private QName profileTime = new QName("http://xmlcalabash.com/ns/profile", "time");
    private String p_declare_step_clark = XProcConstants.p_declare_step.getClarkName();
    private String p_pipeline_clark = XProcConstants.p_pipeline.getClarkName();

    public XProcRuntime(XProcConfiguration config) {
        this.config = config;
        processor = config.getProcessor();

        if (config.xprocConfigurer != null) {
            try {
                String className = config.xprocConfigurer;
                Constructor constructor = Class.forName(className).asSubclass(XProcConfigurer.class).getConstructor(XProcRuntime.class);
                configurer = constructor.newInstance(this);
            } catch (Exception e) {
                throw new XProcException(e);
            }
        } else {
            configurer = new DefaultXProcConfigurer(this);
        }

        xprocData = new XProcData(this);

        exFuncs.add(new Cwd(this));
        exFuncs.add(new BaseURI(this));
        exFuncs.add(new ResolveURI(this));
        exFuncs.add(new SystemProperty(this));
        exFuncs.add(new StepAvailable(this));
        exFuncs.add(new IterationSize(this));
        exFuncs.add(new IterationPosition(this));
        exFuncs.add(new ValueAvailable(this));
        exFuncs.add(new VersionAvailable(this));
        exFuncs.add(new XPathVersionAvailable(this));

        for (XProcExtensionFunctionDefinition xf : exFuncs) {
            processor.registerExtensionFunction(xf);
        }

        Configuration saxonConfig = processor.getUnderlyingConfiguration();
        uriResolver = new XProcURIResolver(this);

        // Make sure that the Saxon processor uses *our* resolver for everything.
        // Unless the user has already provided a class of their own, of course.
        XProcURIResolverX saxonFakeStaticResolver = new XProcURIResolverX();
        String saxonFakeClassName = saxonFakeStaticResolver.getClass().getName();
        saxonFakeStaticResolver.setRealResolver(uriResolver);
        if (!config.setSaxonProperties.contains(FeatureKeys.ENTITY_RESOLVER_CLASS)) {
            saxonConfig.setConfigurationProperty(FeatureKeys.ENTITY_RESOLVER_CLASS, saxonFakeClassName);
        }
        if (!config.setSaxonProperties.contains(FeatureKeys.URI_RESOLVER_CLASS)) {
            saxonConfig.setConfigurationProperty(FeatureKeys.URI_RESOLVER_CLASS, saxonFakeClassName);
        }

        saxonConfig.setURIResolver(uriResolver);
        staticBaseURI = URIUtils.cwdAsURI();

        try {
            if (config.uriResolver != null) {
                uriResolver.setUnderlyingURIResolver(Class.forName(config.uriResolver).asSubclass(URIResolver.class).newInstance());
            }
            if (config.entityResolver != null) {
                uriResolver.setUnderlyingEntityResolver(Class.forName(config.entityResolver).asSubclass(EntityResolver.class).newInstance());
            }

            if (config.errorListener != null) {
                msgListener = Class.forName(config.errorListener).asSubclass(XProcMessageListener.class).newInstance();
            } else {
                msgListener = new DefaultXProcMessageListener();
            }
        } catch (Exception e) {
            throw new XProcException(e);
        }

        if (config.catalogs.size() > 0) {
            uriResolver.addCatalogs(config.catalogs);
        }

        StepErrorListener errListener = new StepErrorListener(this);
        saxonConfig.setErrorListener(errListener);

        allowGeneralExpressions = config.extensionValues;
        allowXPointerOnText = config.xpointerOnText;
        allowTextResults = config.allowTextResults;
        transparentJSON = config.transparentJSON;
        jsonFlavor = config.jsonFlavor;
        useXslt10 = config.useXslt10;
        htmlSerializer = config.htmlSerializer;

        if (config.profile != null) {
            profile = config.profile;
            profileHash = new Hashtable ();
            profileWriter = new TreeWriter(this);
            profileWriter.startDocument(URI.create("http://xmlcalabash.com/output/profile.xml"));
        }

        String warnLevel = "INFO";
        for (String className : config.extensionFunctions.keySet()) {
            try {
                SaxonExtensionFunction annotation = config.extensionFunctions.get(className);
                if (annotation != null) {
                    warnLevel = annotation.warnLevel().toUpperCase();
                }

                Object def = Class.forName(className).newInstance();
                logger.trace("Instantiated: " + className);
                if (def instanceof ExtensionFunctionDefinition)
                    processor.registerExtensionFunction((ExtensionFunctionDefinition) def);
                else if (def instanceof ExtensionFunction)
                    processor.registerExtensionFunction((ExtensionFunction) def);
                else
                    logger.info("Failed to instantiate extension function " + className + " because that class implements neither ExtensionFunction nor ExtensionFunctionDefinition.");
            } catch (Throwable e) {
                if ("INFO".equals(warnLevel)) {
                    logger.info("Failed to instantiate extension function: " + className);
                } else if ("DEBUG".equals(warnLevel)) {
                    logger.debug("Failed to instantiate extension function: " + className);
                } else if ("TRACE".equals(warnLevel)) {
                    logger.trace("Failed to instantiate extension function: " + className);
                } else {
                    logger.error("Failed to instantiate extension function: " + className);
                }
            }
        }

        htmlParser = config.htmlParser;
        addSystemPropertySet(XProcSystemPropertySet.BUILTIN);

        reset();

        initializeSteps();
    }

    public XProcRuntime(XProcRuntime runtime) {
        processor = runtime.processor;
        uriResolver = runtime.uriResolver;
        config = runtime.config;
        staticBaseURI = runtime.staticBaseURI;
        useXslt10 = runtime.useXslt10;
        htmlSerializer = runtime.htmlSerializer;
        msgListener = runtime.msgListener;
        standardLibrary = runtime.standardLibrary;
        httpClient = runtime.httpClient;
        cookieStores = runtime.cookieStores;
        configurer = runtime.configurer;
        allowGeneralExpressions = runtime.allowGeneralExpressions;
        allowXPointerOnText = runtime.allowXPointerOnText;
        transparentJSON = runtime.transparentJSON;
        jsonFlavor = runtime.jsonFlavor;
        profile = runtime.profile;

        exFuncs.add(new Cwd(this));
        exFuncs.add(new BaseURI(this));
        exFuncs.add(new ResolveURI(this));
        exFuncs.add(new SystemProperty(this));
        exFuncs.add(new StepAvailable(this));
        exFuncs.add(new IterationSize(this));
        exFuncs.add(new IterationPosition(this));
        exFuncs.add(new ValueAvailable(this));
        exFuncs.add(new VersionAvailable(this));
        exFuncs.add(new XPathVersionAvailable(this));

        for (XProcExtensionFunctionDefinition xf : exFuncs) {
            processor.registerExtensionFunction(xf);
        }

        reset();

        initializeSteps();
    }

    public void resetExtensionFunctions() {
        for (XProcExtensionFunctionDefinition xf : exFuncs) {
            processor.registerExtensionFunction(xf);
        }
    }

    private void initializeSteps() {
        for (Class klass : config.implementations.values()) {
            try {
                Method config = klass.getMethod("configureStep", XProcRuntime.class);
                config.invoke(null, this);
            } catch (NoSuchMethodException e) {
                // nevermind
            } catch (IllegalAccessException e) {
                // nevermind
            } catch (InvocationTargetException e) {
                // nevermind
            } catch (Exception e) {
                System.err.println("Caught: " + e);
            }
        }
    }

    public void close() {
        HttpClientUtils.closeQuietly(httpClient);
        httpClient = null;

        if (exFuncs != null) {
            for (XProcExtensionFunctionDefinition xf : exFuncs) {
                xf.close();
            }
        }
        exFuncs = null;
    }

    public XProcConfigurer getConfigurer() {
        return configurer;
    }

    public void setConfigurer(XProcConfigurer configurer) {
        this.configurer = configurer;
    }

    public XProcData getXProcData() {
        return xprocData;
    }

    public boolean getDebug() {
        return config.debug;
    }

    public boolean getShowMessages() {
        return config.showMessages;
    }

    public Output getProfile() {
        return profile;
    }

    public void setProfile(Output profile) {
        this.profile = profile;
    }

    public URI getStaticBaseURI() {
        return staticBaseURI;
    }

    public void setStaticBaseURI(URI staticBaseURI) {
        this.staticBaseURI = staticBaseURI;
    }

    public URI getBaseURI() {
        return baseURI;
    }

    public void setBaseURI(URI baseURI) {
        this.baseURI = baseURI;
    }

    public String getSendmailHost() {
        return config.mailHost;
    }

    public String getSendmailPort() {
        return config.mailPort;
    }

    public String getSendmailUsername() {
        return config.mailUser;
    }

    public String getSendmailPassword() {
        return config.mailPass;
    }

    public void setURIResolver(URIResolver resolver) {
        uriResolver.setUnderlyingURIResolver(resolver);
    }

    public void setEntityResolver(EntityResolver resolver) {
        uriResolver.setUnderlyingEntityResolver(resolver);
    }

    public synchronized DataStore getDataStore() {
        if (dataStore == null) {
            DataStore fallback = new URLDataStore(new FallbackDataStore());
            if (!getSafeMode()) {
                fallback = new FileDataStore(fallback);
            }
            dataStore = new HttpClientDataStore(getHttpClient(), fallback);
        }
        return dataStore;
    }

    public synchronized void setDataStore(DataStore dataStore) {
        this.dataStore = dataStore;
    }

    public XProcURIResolver getResolver() {
        return uriResolver;
    }

    public XProcMessageListener getMessageListener() {
      return msgListener;
    }

    public void setMessageListener(XProcMessageListener listener) {
      msgListener = listener;
    }

    public void setCollection(URI href, Vector docs) {
        if (collections == null) {
            collections = new Hashtable> ();
        }
        collections.put(href.toASCIIString(), docs);
    }

    public Vector getCollection(URI href) {
        if (collections == null) {
            return null;
        }
        if (collections.containsKey(href.toASCIIString())) {
            return collections.get(href.toASCIIString());
        }
        return null;
    }

    public boolean getSafeMode() {
        return config.safeMode;
    }

    public boolean getAllowGeneralExpressions() {
        return allowGeneralExpressions;
    }

    public boolean getAllowXPointerOnText() {
        return allowXPointerOnText;
    }

    public boolean getAllowTextResults() {
        return allowTextResults;
    }

    public boolean transparentJSON() {
        return transparentJSON;
    }

    public String jsonFlavor() {
        return jsonFlavor;
    }

    public String htmlParser() {
        return htmlParser;
    }

    public boolean getUseXslt10Processor() {
        return useXslt10;
    }

    public boolean getHtmlSerializer() {
        return htmlSerializer;
    }

    public void cache(XdmNode doc, URI baseURI) {
        uriResolver.cache(doc, baseURI);
    }

    public XProcConfiguration getConfiguration() {
        return config;
    }

    public Parser getParser() {
        return parser;
    }

    public String getEpisode() {
        if (episode == null) {
            MessageDigest digest = null;
            GregorianCalendar calendar = new GregorianCalendar();
            try {
                digest = MessageDigest.getInstance("MD5");
            } catch (NoSuchAlgorithmException nsae) {
                throw XProcException.dynamicError(36);
            }

            byte[] hash = digest.digest(calendar.toString().getBytes());
            episode = "CB";
            for (byte b : hash) {
                episode = episode + Integer.toHexString(b & 0xff);
            }
        }

        return episode;
    }

    public String getLanguage() {
        // Translate _ to - for compatibility with xml:lang
        return Locale.getDefault().toString().replace('_', '-');

    }

    public String getProductName() {
        return "XML Calabash";
    }

    public String getProductVersion() {
        String sver = processor.getSaxonProductVersion();
        String sed = processor.getUnderlyingConfiguration().getEditionCode();
        return XProcConstants.XPROC_VERSION + " (for Saxon " + sver + "/" + sed + ")";
    }

    public String getVendor() {
        return "Norman Walsh";
    }

    public String getVendorURI() {
        return "http://xmlcalabash.com/";
    }

    public String getXProcVersion() {
        return "1.0";
    }

    public String getXPathVersion() {
        return "2.0";
    }

    public boolean getPSVISupported() {
        return config.schemaAware;
    }

    private synchronized void reset() {
        errorCode = null;
        errorMessage = null;
        declaredSteps = new Hashtable ();
        //explicitDeclarations = false;
        pipeline = null;
        xpipeline = null;
        episode = null;
        collections = null;
        cookieStores = new HashMap();

        xprocData = new XProcData(this);

        parser = new Parser(this);
        try {
            standardLibrary = parser.loadStandardLibrary();
            if (errorCode != null) {
                throw new XProcException(errorCode, errorMessage);
            }
        } catch (FileNotFoundException ex) {
            throw new XProcException(XProcConstants.dynamicError(9), ex);
        } catch (URISyntaxException ex) {
            throw new XProcException(XProcConstants.dynamicError(9), ex);
        } catch (SaxonApiException ex) {
            throw new XProcException(XProcConstants.dynamicError(9), ex);
        }

        if (profile != null) {
            profileHash = new Hashtable();
            profileWriter = new TreeWriter(this);
            profileWriter.startDocument(URI.create("http://xmlcalabash.com/output/profile.xml"));
        }
    }

    public PipelineLibrary getStandardLibrary() {
        return standardLibrary;
    }

    // FIXME: This design sucks
    public XPipeline load(Input pipeline) throws SaxonApiException {
        String uri;
        switch (pipeline.getKind()) {
            case URI:
                uri = pipeline.getUri();
                break;

            case INPUT_STREAM:
                uri = pipeline.getInputStreamUri();
                break;

            default:
                throw new UnsupportedOperationException(format("Unsupported pipeline kind '%s'", pipeline.getKind()));
        }

        for (String map : config.loaders.keySet()) {
            boolean data = map.startsWith("data:");
            String pattern = map.substring(5);
            if (uri.matches(pattern)) {
                return runPipelineLoader(pipeline, config.loaders.get(map), data);
            }
        }

        try {
            return _load(pipeline);
        } catch (SaxonApiException sae) {
            error(sae);
            throw sae;
        } catch (XProcException xe) {
            error(xe);
            throw xe;
        } catch (IOException ioe) {
            error(ioe);
            throw new XProcException(ioe);
        }
    }

    private XPipeline _load(Input pipelineInput) throws SaxonApiException, IOException {
        reset();
        configurer.getXMLCalabashConfigurer().configRuntime(this);
        switch (pipelineInput.getKind()) {
            case URI:
                if (baseURI == null) {
                    pipeline = parser.loadPipeline(pipelineInput.getUri());
                } else {
                    pipeline = parser.loadPipeline(pipelineInput.getUri(), baseURI.toASCIIString());
                }
                break;

            case INPUT_STREAM:
                pipeline = parser.loadPipeline(pipelineInput.getInputStream(), pipelineInput.getInputStreamUri());
                break;

            default:
                throw new UnsupportedOperationException(format("Unsupported pipeline kind '%s'", pipelineInput.getKind()));
        }
        if (errorCode != null) {
            throw new XProcException(errorCode, errorMessage);
        }

        XRootStep root = new XRootStep(this);
        DeclareStep decl = pipeline.getDeclaration();
        decl.setup();

        if (errorCode != null) {
            throw new XProcException(errorCode, errorNode, errorMessage);
        }

        xpipeline = new XPipeline(this, pipeline, root);
        xpipeline.instantiate(decl);

        if (errorCode != null) {
            throw new XProcException(errorCode, errorMessage);
        }

        return xpipeline;
    }

    // FIXME: This design sucks
    public XPipeline use(XdmNode p_pipeline) throws SaxonApiException {
        try {
            return _use(p_pipeline);
        } catch (SaxonApiException sae) {
            error(sae);
            throw sae;
        } catch (XProcException xe) {
            error(xe);
            throw xe;
        }
    }
    private XPipeline _use(XdmNode p_pipeline) throws SaxonApiException {
        reset();
        configurer.getXMLCalabashConfigurer().configRuntime(this);
        pipeline = parser.usePipeline(p_pipeline);
        if (errorCode != null) {
            throw new XProcException(errorCode, errorMessage);
        }

        XRootStep root = new XRootStep(this);
        DeclareStep decl = pipeline.getDeclaration();
        decl.setup();

        if (errorCode != null) {
            throw new XProcException(errorCode, errorMessage);
        }

        xpipeline = new XPipeline(this, pipeline, root);
        xpipeline.instantiate(decl);

        if (errorCode != null) {
            throw new XProcException(errorCode, errorMessage);
        }

        return xpipeline;
    }

    // FIXME: This design sucks
    public XLibrary loadLibrary(Input library) throws SaxonApiException {
        String libraryURI;
        switch (library.getKind()) {
            case URI:
                libraryURI = library.getUri();
                break;

            case INPUT_STREAM:
                libraryURI = library.getInputStreamUri();
                break;

            default:
                throw new UnsupportedOperationException(format("Unsupported library kind '%s'", library.getKind()));
        }

        for (String map : config.loaders.keySet()) {
            boolean data = map.startsWith("data:");
            String pattern = map.substring(5);
            if (libraryURI.matches(pattern)) {
                return runLibraryLoader(library, config.loaders.get(map), data);
            }
        }

        try {
            return _loadLibrary(library);
        } catch (SaxonApiException sae) {
            error(sae);
            throw sae;
        } catch (XProcException xe) {
            error(xe);
            throw xe;
        } catch (IOException ioe) {
            error(ioe);
            throw new XProcException(ioe);
        }
    }

    private XLibrary _loadLibrary(Input library) throws SaxonApiException, IOException {
        PipelineLibrary plibrary;
        switch (library.getKind()) {
            case URI:
                plibrary = parser.loadLibrary(library.getUri());
                break;

            case INPUT_STREAM:
                plibrary = parser.loadLibrary(library.getInputStream(), library.getInputStreamUri());
                break;

            default:
                throw new UnsupportedOperationException(format("Unsupported library kind '%s'", library.getKind()));
        }

        if (errorCode != null) {
            throw new XProcException(errorCode, errorMessage);
        }

        XLibrary xlibrary = new XLibrary(this, plibrary);

        if (errorCode != null) {
            throw new XProcException(errorCode, errorMessage);
        }

        return xlibrary;
    }

    // FIXME: This design sucks
    public XLibrary useLibrary(XdmNode library) throws SaxonApiException {
        try {
            return _useLibrary(library);
        } catch (SaxonApiException sae) {
            error(sae);
            throw sae;
        } catch (XProcException xe) {
            error(xe);
            throw xe;
        }
    }

    private XLibrary _useLibrary(XdmNode library) throws SaxonApiException {
        PipelineLibrary plibrary = parser.useLibrary(library);
        if (errorCode != null) {
            throw new XProcException(errorCode, errorMessage);
        }

        XLibrary xlibrary = new XLibrary(this, plibrary);

        if (errorCode != null) {
            throw new XProcException(errorCode, errorMessage);
        }

        return xlibrary;
    }

    private XPipeline runPipelineLoader(Input pipeline, String loaderURI, boolean data) throws SaxonApiException {
        XdmNode pipeDoc = runLoader(pipeline, loaderURI, data);
        return use(pipeDoc);
    }

    private XLibrary runLibraryLoader(Input library, String loaderURI, boolean data) throws SaxonApiException {
        XdmNode libDoc = runLoader(library, loaderURI, data);
        return useLibrary(libDoc);
    }

    private XdmNode runLoader(Input pipeline, String loaderURI, boolean data) throws SaxonApiException {
        XPipeline loader = null;

        try {
            loader = _load(new Input(loaderURI));
        } catch (SaxonApiException sae) {
            error(sae);
            throw sae;
        } catch (XProcException xe) {
            error(xe);
            throw xe;
        } catch (IOException ioe) {
            error(ioe);
            throw new XProcException(ioe);
        }

        XdmNode pipeDoc = null;
        switch (pipeline.getKind()) {
            case URI:
                if (data) {
                    ReadableData rdata = new ReadableData(this, XProcConstants.c_result, getStaticBaseURI().resolve(pipeline.getUri()).toASCIIString(), "text/plain");
                    pipeDoc = rdata.read();
                } else {
                    pipeDoc = parse(pipeline.getUri(), getStaticBaseURI().toASCIIString());
                }
                break;

            case INPUT_STREAM:
                if (data) {
                    ReadableData rdata = new ReadableData(this, XProcConstants.c_result, pipeline.getInputStream(), "text/plain");
                    pipeDoc = rdata.read();
                } else {
                    pipeDoc = parse(new InputSource(pipeline.getInputStream()));
                }
                break;

            default:
                throw new UnsupportedOperationException(format("Unsupported pipeline kind '%s'", pipeline.getKind()));
        }

        loader.clearInputs("source");
        loader.writeTo("source", pipeDoc);
        loader.run();
        ReadablePipe xformed = loader.readFrom("result");
        pipeDoc = xformed.read();

        reset();
        return pipeDoc;
    }

    public Processor getProcessor() {
        return processor;
    }

    public XdmNode parse(String uri, String base) {
        return parse(uri, base, false);
    }

    public XdmNode parse(String uri, String base, boolean validate) {
        return uriResolver.parse(uri, base, validate);
    }

    public XdmNode parse(InputSource isource) {
        return uriResolver.parse(isource);
    }

    public void declareStep(QName name, DeclareStep step) {
        DeclareStep d = getDeclaration(name);
        if (d != null) {
            if (!d.equals(step))
                throw new XProcException(step, "Duplicate step type: " + name);
        } else {
            declaredSteps.put(name, step);
        }
    }

    public DeclareStep getDeclaration(QName type) {
        DeclareStep decl = null;
        if (standardLibrary != null)
            decl = standardLibrary.getDeclaration(type);
        DeclareStep d = declaredSteps.get(type);
        if (d != null) {
            if (decl == null)
                decl = d;
            else
                throw new XProcException(d, "Duplicate step type: " + type);
        }
        return decl;
    }

    public Set getInScopeTypes() {
        Set decls = new HashSet<>();
        decls.addAll(declaredSteps.keySet());
        if (standardLibrary != null)
            decls.addAll(standardLibrary.getInScopeTypes());
        return decls;
    }

    public synchronized CookieStore getCookieStore(String key) {
        if (cookieStores.containsKey(key))
            return cookieStores.get(key);
        BasicCookieStore cookieStore = new BasicCookieStore();
        cookieStores.put(key, cookieStore);
        return cookieStore;
    }

    public synchronized void setCookieStore(String key, CookieStore cookieStore) {
        if (cookieStore == null) {
            removeCookieStore(key);
        } else {
            this.cookieStores.put(key, cookieStore);
        }
    }

    public synchronized void removeCookieStore(String key) {
        this.cookieStores.remove(key);
    }

    public synchronized HttpClient getHttpClient() {
    	if (this.httpClient == null) {
    	    HttpClientBuilder builder = HttpClientBuilder.create();
            // Provide custom retry handler is necessary
            builder.setRetryHandler(new StandardHttpRequestRetryHandler(3, false));
            return this.httpClient = builder.build();
    	} else {
    		return httpClient;
    	}
    }

    public synchronized void setHttpClient(HttpClient client) {
        this.httpClient = client;
    }

    public QName getErrorCode() {
        return errorCode;
    }

    public String getErrorMessage() {
        return errorMessage;
    }

    // ===========================================================
    // This logging stuff is still accessed through XProcRuntime
    // so that messages can be formatted in a common way and so
    // that errors can be trapped.

    public void error(XProcRunnable step, XdmNode node, String message, QName code) {
        if (errorCode == null) {
            errorCode = code;
            errorNode = node;
            errorMessage = message;
        }

        msgListener.error(step, node, message, code);
    }

    public void error(Throwable error) {
        msgListener.error(error);
    }

    public void warning(XProcRunnable step, XdmNode node, String message) {
        msgListener.warning(step, node, message);
    }

    public void warning(Throwable error) {
        msgListener.warning(error);
    }

    public void info(XProcRunnable step, XdmNode node, String message) {
        msgListener.info(step, node, message);
    }

    // ===========================================================

    private Stack runningSteps = new Stack();

    public void start(XStep step) {
        runningSteps.push(step);

        if (profile == null) {
            return;
        }

        boolean first = profileHash.isEmpty();

        Calendar start = GregorianCalendar.getInstance();
        profileHash.put(step, start);
        profileWriter.addStartElement(profileProfile);

        if (first) {
            DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
            profileWriter.addAttribute(new QName("", "timestamp"), df.format(new Date()));
            profileWriter.addAttribute(new QName("", "episode"), getEpisode());
            profileWriter.addAttribute(new QName("", "language"), getLanguage());
            profileWriter.addAttribute(new QName("", "product-name"), getProductName());
            profileWriter.addAttribute(new QName("", "product-version"), getProductVersion());
            profileWriter.addAttribute(new QName("", "product-vendor"), getVendor());
            profileWriter.addAttribute(new QName("", "product-vendor-uri"), getVendorURI());
            profileWriter.addAttribute(new QName("", "xproc-version"), getXProcVersion());
            profileWriter.addAttribute(new QName("", "xpath-version"), getXPathVersion());
            profileWriter.addAttribute(new QName("", "psvi-supported"), ""+getPSVISupported());
        }

        String name = step.getType().getClarkName();
        if ((p_declare_step_clark.equals(name) || p_pipeline_clark.equals(name))
                && step.getType() != null
                && step.getStep().getDeclaredType() != null) {
            profileWriter.addAttribute(profileType, step.getStep().getDeclaredType().getClarkName());
        } else {
            profileWriter.addAttribute(profileType, name);
        }

        profileWriter.addAttribute(profileName, step.getStep().getName());
        if (step.getStep().getNode() != null) {
            profileWriter.addAttribute(profileHref, step.getStep().xplFile());
            profileWriter.addAttribute(profileLine, ""+step.getStep().xplLine());
        }
        profileWriter.startContent();
    }

    public XStep runningStep() {
        return runningSteps.peek();
    }

    public void finish(XStep step) {
        runningSteps.pop();

        if (profile == null) {
            return;
        }

        Calendar start = profileHash.get(step);
        long time = GregorianCalendar.getInstance().getTimeInMillis() - start.getTimeInMillis();
        profileHash.remove(step);

        profileWriter.addStartElement(profileTime);
        profileWriter.startContent();
        profileWriter.addText("" + time);
        profileWriter.addEndElement();
        profileWriter.addEndElement();

        if (profileHash.isEmpty()) {
            profileWriter.endDocument();
            XdmNode profile = profileWriter.getResult();

            InputStream xsl = getClass().getResourceAsStream("/etc/patch-profile.xsl");
            if (xsl == null) {
                throw new UnsupportedOperationException("Failed to load profile_patch.xsl from JAR file.");
            }

            try {
                XsltCompiler compiler = getProcessor().newXsltCompiler();
                compiler.setSchemaAware(false);
                XsltExecutable exec = compiler.compile(new SAXSource(new InputSource(xsl)));
                XsltTransformer transformer = exec.load();
                transformer.setInitialContextNode(profile);
                XdmDestination result = new XdmDestination();
                transformer.setDestination(result);
                transformer.transform();

                Serializer serializer = getProcessor().newSerializer();
                serializer.setOutputProperty(Serializer.Property.INDENT, "yes");

                OutputStream outstr = null;
                try {
                    switch (this.profile.getKind()) {
                    case URI:
                        URI furi = URI.create(this.profile.getUri());
                        outstr = new FileOutputStream(new File(furi));
                        break;

                    case OUTPUT_STREAM:
                        outstr = this.profile.getOutputStream();
                        break;

                    default:
                        throw new UnsupportedOperationException(format("Unsupported profile kind '%s'", this.profile.getKind()));
                    }

                    serializer.setOutputStream(outstr);
                    S9apiUtils.serialize(this, result.getXdmNode(), serializer);
                } finally {
                    if (!System.out.equals(outstr) && !System.err.equals(outstr)) {
                        outstr.close();
                    }
                }

                profileWriter = new TreeWriter(this);
                profileWriter.startDocument(URI.create("http://xmlcalabash.com/output/profile.xml"));
            } catch (SaxonApiException sae) {
                throw new XProcException(sae);
            } catch (FileNotFoundException fnfe) {
                throw new XProcException(fnfe);
            } catch (IOException ioe) {
                throw new XProcException(ioe);
            }
        }
    }

    /**
     * Registers an {@code XProcSystemPropertySet}.
     * It will be consulted whenever the
     * {@code p:system-property} function is evaluated.
     *
     * 

The {@linkplain com.xmlcalabash.util.XProcSystemPropertySet#BUILTIN built-in} * {@code XProcSystemPropertySet} is added automatically. * Other property sets may be added with this method by applications using XML Calabash.

* * @see #getSystemProperty * @param systemPropertySet The set of values to add. */ public void addSystemPropertySet(XProcSystemPropertySet systemPropertySet) { systemPropertySets.add(systemPropertySet); } /** * Looks up a system property by the given name. * If no such system property is found, this method returns {@code null}. * *

This method consults {@linkplain com.xmlcalabash.util.XProcSystemPropertySet#BUILTIN the built-in} * {@link com.xmlcalabash.util.XProcSystemPropertySet}, and any other * {@code XProcSystemPropertySet}s that have been {@linkplain #addSystemPropertySet registered}.

* * @see #addSystemPropertySet * @see com.xmlcalabash.util.XProcSystemPropertySet#systemProperty * @param propertyName the name of the system property to look up * @return the string value of that system property, or {@code null} * @throws XProcException if any error occurs */ public String getSystemProperty(QName propertyName) throws XProcException { synchronized (systemPropertySets) { for (XProcSystemPropertySet propSet : systemPropertySets) { String value = propSet.systemProperty(this, propertyName); if (value != null) return value; } } return null; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy