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

org.daisy.common.transform.XMLTransformer Maven / Gradle / Ivy

The newest version!
package org.daisy.common.transform;

import java.util.function.Function;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.NoSuchElementException;

import javax.xml.namespace.QName;

import com.google.common.collect.ImmutableMap;

/**
 * Transform a number of input values to output values, at least one input or output being XML or
 * convertable to/from XML.
 *
 * Note that this interface does not extend javax.xml.transform.Transformer.
 */
public interface XMLTransformer {

	/**
	 * @param input Supplier of XDM values as a map from port name ({@link QName}) to {@link
	 *         InputValue} object. If the map returns null for a certain key it means that port is
	 *         not connected. If the map itself is null it means no input ports are connected.
	 * @param output Consumer of XDM values as a map from port name ({@link QName}) to {@link
	 *         OutputValue} object. If the map returns null for a certain key it means that port is
	 *         not connected. If the map itself is null it means no output ports are connected.
	 * @return a {@link Runnable} that executes the transformation. Transformations can be run
	 *         inside threads to keep the buffers between transformers small. If not run inside
	 *         threads, the transformations must be executed in the correct order. {@link
	 *         Runnable#run()} may throw a {@link TransformerException} if an input could not be
	 *         read, the transformation failed, or an output could not be written.
	 * @throws IllegalArgumentException if an invalid input or output value was provided or if an
	 *                                  input or output value is missing.
	 */
	public Runnable transform(Map> input, Map> output);

	public default Runnable transform(InputValue input, OutputValue output) {
		return transform(
			ImmutableMap.of(new QName("source"), input),
			ImmutableMap.of(new QName("result"), output)
		);
	}

	/**
	 * Check that the expected inputs are present and are of the expected type.
	 */
	public static Map> validateInput(Map> input,
	                                                     Map types)
			throws IllegalArgumentException {
		input = new HashMap<>(input);
		ImmutableMap.Builder> map = ImmutableMap.builder();
		if (types != null)
			for (QName expectedPort : types.keySet()) {
				InputType expectedType = types.get(expectedPort);
				try {
					Optional> value = Optional.ofNullable(input.remove(expectedPort));
					value = expectedType.validate(value);
					if (value.isPresent())
						map.put(expectedPort, value.get());
				} catch (NoSuchElementException e) {
					throw new IllegalArgumentException(
						"no value specified on input port '" + expectedPort + "'");
				} catch (IllegalArgumentException e) {
					throw new IllegalArgumentException(
						"invalid value on input port '" + expectedPort + ": " + e.getMessage());
				}
			}
		for (QName extraPort : input.keySet())
			throw new IllegalArgumentException("no value expected on input port '" + extraPort + "'");
		return map.build();
	};

	/**
	 * Check that the expected outputs are present and are of the expected type.
	 */
	public static Map> validateOutput(Map> output,
	                                                       Map types)
			throws IllegalArgumentException {
		output = new HashMap<>(output);
		ImmutableMap.Builder> map = ImmutableMap.builder();
		if (types != null)
			for (QName expectedPort : types.keySet()) {
				OutputType expectedType = types.get(expectedPort);
				try {
					if (output.containsKey(expectedPort))
						map.put(expectedPort, expectedType.validate(output.remove(expectedPort)));
				} catch (NoSuchElementException e) {
					throw new IllegalArgumentException(
						"no value specified on output port '" + expectedPort + "'");
				} catch (IllegalArgumentException e) {
					throw new IllegalArgumentException(
						"invalid value on output port '" + expectedPort + ": " + e.getMessage());
				}
			}
		for (QName extraPort : output.keySet())
			throw new IllegalArgumentException("no value expected on output port '" + extraPort + "'");
		return map.build();
	};

	@FunctionalInterface
	public interface InputType {

		public Optional> validate(Optional> input);

		public static InputType optional(Function,InputValue> type) {
			return new InputType() {
				public Optional> validate(Optional> input) {
					if (!input.isPresent())
						return input;
					else
						return Optional.of(type.apply(input.get()));
				}
			};
		}

		public static InputType optional(InputType type) {
			return new InputType() {
				public Optional> validate(Optional> input) {
					if (!input.isPresent())
						return input;
					else
						return type.validate(input);
				}
			};
		}

		public static InputType mandatory(Function,InputValue> type) {
			return new InputType() {
				public Optional> validate(Optional> input) {
					return Optional.of(type.apply(input.get()));
				}
			};
		}

		public static InputType mandatory(InputType type) {
			return new InputType() {
				public Optional> validate(Optional> input) {
					return type.validate(Optional.of(input.get()));
				}
			};
		}

		public default InputType and(InputType after) {
			InputType before = this;
			return new InputType() {
				public Optional> validate(Optional> input) {
					return after.validate(before.validate(input));
				}
			};
		}

		public default InputType andIfPresent(Function,InputValue> type) {
			InputType before = this;
			return new InputType() {
				public Optional> validate(Optional> input) {
					input = before.validate(input);
					if (!input.isPresent())
						return input;
					else
						return Optional.of(type.apply(input.get()));
				}
			};
		}

		public static final InputType OPTIONAL_ITEM_SEQUENCE = i -> i;

		public static final InputType MANDATORY_ITEM_SEQUENCE = mandatory(OPTIONAL_ITEM_SEQUENCE);

		public static final InputType OPTIONAL_NODE_SEQUENCE
			= OPTIONAL_ITEM_SEQUENCE.andIfPresent(
				i -> {
					if (i instanceof XMLInputValue) return i;
					else throw new IllegalArgumentException("value is not XML"); });

		public static final InputType MANDATORY_NODE_SEQUENCE = mandatory(OPTIONAL_NODE_SEQUENCE);

		public static final InputType OPTIONAL_NODE_SINGLE
			= OPTIONAL_NODE_SEQUENCE.andIfPresent(
				i -> ((XMLInputValue)i).ensureSingleItem());

		public static final InputType MANDATORY_NODE_SINGLE = mandatory(OPTIONAL_NODE_SINGLE);
	}

	@FunctionalInterface
	public interface OutputType {

		public OutputValue validate(OutputValue output);

		public static final OutputType ITEM_SEQUENCE = i -> i;

		public static final OutputType NODE_SEQUENCE = i -> {
			if (i instanceof XMLOutputValue) return i;
			else throw new IllegalArgumentException("value is not XML"); };
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy