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

org.daisy.common.xproc.calabash.XProcStep Maven / Gradle / Ivy

The newest version!
package org.daisy.common.xproc.calabash;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import javax.xml.namespace.QName;

import com.xmlcalabash.core.XProcException;
import com.xmlcalabash.core.XProcRuntime;
import com.xmlcalabash.io.ReadablePipe;
import com.xmlcalabash.io.WritablePipe;
import com.xmlcalabash.model.RuntimeValue;
import com.xmlcalabash.runtime.XAtomicStep;

import net.sf.saxon.s9api.SaxonApiException;

import org.daisy.common.saxon.SaxonHelper;
import org.daisy.common.saxon.SaxonInputValue;
import org.daisy.common.transform.InputValue;
import org.daisy.common.transform.Mult;
import org.daisy.common.transform.OutputValue;
import org.daisy.common.transform.StringWithNamespaceContext;
import org.daisy.common.transform.TransformerException;
import org.daisy.common.transform.XMLTransformer;
import org.daisy.common.xproc.XProcErrorException;

/**
 * {@link com.xmlcalabash.core.XProcStep} that also implements the {@link XMLTransformer} API.
 */
public interface XProcStep extends com.xmlcalabash.core.XProcStep, XMLTransformer {

	@Override
	public default Runnable transform(Map> input, Map> output) {
		return () -> {
			try {
				for (QName n : input.keySet()) {
					InputValue i = input.get(n);
					if (i instanceof SaxonInputValue) {
						if (n.getNamespaceURI() != null && !"".equals(n.getNamespaceURI()))
							throw new IllegalArgumentException("unexpected value on input port '" + n + "'");
						XProcStep.this.setInput(n.getLocalPart(), XMLCalabashInputValue.of(i).asReadablePipe());
					} else if (i instanceof StringWithNamespaceContext) {
						XProcStep.this.setOption(new net.sf.saxon.s9api.QName(n),
						                         XMLCalabashOptionValue.of(i).asRuntimeValue());
					} else {
						Mult> m = i.mult(2);
						i = m.get();
						try {
							Map params
								= XMLCalabashParameterInputValue.of(i).asRuntimeValueMap();
							if (n.getNamespaceURI() != null && !"".equals(n.getNamespaceURI()))
								throw new IllegalArgumentException("unexpected value on input port '" + n + "'");
							for (net.sf.saxon.s9api.QName p : params.keySet())
								XProcStep.this.setParameter(n.getLocalPart(), p, params.get(p));
						} catch (IllegalArgumentException e) {
							i = m.get();
							try {
								XProcStep.this.setOption(new net.sf.saxon.s9api.QName(n),
								                         XMLCalabashOptionValue.of(i).asRuntimeValue());
							} catch (IllegalArgumentException ee) {
								throw new IllegalArgumentException("unexpected value on input port '" + n + "'");
							}
						}
					}
				}
				for (QName n : output.keySet()) {
					OutputValue o = output.get(n);
					if (n.getNamespaceURI() != null && !"".equals(n.getNamespaceURI()))
						throw new IllegalArgumentException("unexpected value on output port '" + n + "'");
					try {
						XProcStep.this.setOutput(n.getLocalPart(), XMLCalabashOutputValue.of(o).asWritablePipe());
					} catch (IllegalArgumentException e) {
						throw new IllegalArgumentException("unexpected value on output port '" + n + "'");
					}
				}
			} catch (IllegalArgumentException e) {
				throw new TransformerException(e);
			}
			try {
				XProcStep.this.run();
			} catch (SaxonApiException|XProcException e) {
				throw new TransformerException(e);
			}
		};
	}

	/**
	 * Port names of regular and parameter inputs are expected to not collide.
	 */
	public static XProcStep of(XMLTransformer transformer, XProcRuntime runtime, XAtomicStep step) {
		return new XProcStep() {

			private Map inputs = null;
			private Map outputs = null;
			private Map options = null;
			private Map> parameters = null;

			public void setInput(String port, ReadablePipe input) {
				if (inputs == null)
					inputs = new HashMap<>();
				inputs.put(port, input);
			}

			public void setOutput(String port, WritablePipe output) {
				if (outputs == null)
					outputs = new HashMap<>();
				outputs.put(port, output);
			}

			public void setOption(net.sf.saxon.s9api.QName name, RuntimeValue value) {
				if (options == null)
					options = new HashMap<>();
				options.put(SaxonHelper.jaxpQName(name), value);
			}

			public void setParameter(net.sf.saxon.s9api.QName name, RuntimeValue value) {
				throw new XProcException(step, "A port should be specified for a parameter.");
			}

			public void setParameter(String port, net.sf.saxon.s9api.QName name, RuntimeValue value) {
				if (parameters == null)
					parameters = new HashMap<>();
				Map params = parameters.get(port);
				if (params == null) {
					params = new HashMap<>();
					parameters.put(port, params);
				}
				params.put(name, value);
			}

			public void reset() {
				if (inputs != null) inputs.clear();
				if (outputs != null) outputs.clear();
				if (options != null) options.clear();
				if (parameters != null) parameters.clear();
			}

			public void run() {
				try {
					Map> input = new HashMap<>();
					if (inputs != null)
						for (String port : inputs.keySet())
							input.put(new QName(port), new XMLCalabashInputValue(inputs.get(port)));
					if (options != null)
						for (QName name : options.keySet())
							input.put(name, new XMLCalabashOptionValue(options.get(name)));
					if (parameters != null)
						for (String port : parameters.keySet())
							input.put(new QName(port), new XMLCalabashParameterInputValue(parameters.get(port)));
					Map> output = new HashMap<>();
					if (outputs != null)
						for (String port : outputs.keySet())
							output.put(new QName(port), new XMLCalabashOutputValue(outputs.get(port), runtime));
					transformer.transform(input, output).run();
				} catch (Throwable e) {
					throw raiseError(e, step);
				}
			}

			@Override
			public Runnable transform(Map> input, Map> output) {
				return transformer.transform(input, output);
			}
		};
	}

	public static XProcException raiseError(Throwable e, XAtomicStep step) {
		if (e instanceof XProcException)
			throw (XProcException)e;
		else if (e instanceof XProcErrorException) {
			throw CalabashExceptionFromXProcError.from(((XProcErrorException)e).getXProcError());
		} else {
			StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
			stackTrace = Arrays.copyOfRange(stackTrace, 1, stackTrace.length);
			if (e instanceof TransformerException) {
				XProcException cause = e.getCause() instanceof XProcException
					? (XProcException)e.getCause()
					: e.getCause() instanceof XProcErrorException
						? CalabashExceptionFromXProcError.from(((XProcErrorException)e.getCause()).getXProcError())
						: e.getCause() != null
							? XProcException.fromException(e.getCause())
							                .rebase(step.getLocation(), stackTrace)
							: null;
				QName code = ((TransformerException)e).getCode();
				return new XProcException(
					code != null
						? new net.sf.saxon.s9api.QName(code.getPrefix(), code.getNamespaceURI(), code.getLocalPart())
						: null,
					step, e, cause);
			} else {
				/* If the exception is a TransformerException we assume it comes from the {@link
				 * XMLTransformer.transform()} method used to implement the step. An
				 * IllegalArgumentException should not happen (indicates a bug in the XProc
				 * step). Because no other exceptions are mentioned in the contract of this method
				 * we treat them as unexpected exceptions as well. */
				return new XProcException(step,
				                          "Unexpected error in " + step.getType(),
				                          XProcException.fromException(e)
				                                        .rebase(step.getLocation(), stackTrace));
			}
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy