org.blip.workflowengine.functions.PropertyNodeXmlFunctions.java Source code

Java tutorial

Introduction

Here is the source code for org.blip.workflowengine.functions.PropertyNodeXmlFunctions.java

Source

/**
 *     Copyright 2017 Open Source Osijek Community
 *
 * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.blip.workflowengine.functions;

import com.google.common.collect.ImmutableList;

import net.sf.saxon.s9api.DocumentBuilder;
import net.sf.saxon.s9api.Processor;
import net.sf.saxon.s9api.XPathCompiler;
import net.sf.saxon.s9api.XPathSelector;
import net.sf.saxon.s9api.XdmValue;

import org.blip.workflowengine.transferobject.Attribute;
import org.blip.workflowengine.transferobject.ImmutableAttribute;
import org.blip.workflowengine.transferobject.ImmutableProperty;
import org.blip.workflowengine.transferobject.ImmutableValue;
import org.blip.workflowengine.transferobject.ModifiablePropertyNode;
import org.blip.workflowengine.transferobject.Property;
import org.blip.workflowengine.transferobject.PropertyNode;
import org.blip.workflowengine.transferobject.Value;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.input.SAXBuilder;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;
import org.xml.sax.InputSource;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.time.Instant;
import java.util.AbstractMap;
import java.util.Base64;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

import javax.xml.transform.sax.SAXSource;
import javax.xml.xpath.XPathException;

/**
 * Created by root on 11.12.16..
 */
public interface PropertyNodeXmlFunctions {

    default Function<Element, PropertyNode> xmlToPropertyNode() {
        return element -> {
            Objects.requireNonNull(element, "element is null");

            final ModifiablePropertyNode node = ModifiablePropertyNode.create();
            for (final Element element1 : element.getChildren()) {
                final Property property = xmlToProperty().apply(element1);
                node.add(property);
            }

            return node;
        };
    }

    default Function<Element, Property> xmlToProperty() {
        return element -> {
            Objects.requireNonNull(element, "element is null");

            final String name = element.getName();
            final Collection<Attribute> collection = element.getAttributes().stream()
                    .map(p -> ImmutableAttribute.of(p.getName(), p.getValue())).collect(Collectors.toList());

            return ImmutableProperty.of(name, xmlToValue().apply(element.getChildren().get(0)))
                    .withAttributes(collection);
        };
    }

    default Function<Element, Value> xmlToValue() {
        return element -> {
            Objects.requireNonNull(element, "element is null");

            final String name = element.getName();

            if (name.contentEquals("null")) {
                return ImmutableValue.of(null);
            } else if (name.contentEquals("boolean")) {
                return ImmutableValue.of(Boolean.valueOf(element.getValue()));
            } else if (name.contentEquals("instant")) {
                return ImmutableValue.of(Instant.parse(element.getValue()));
            } else if (name.contentEquals(("byte-array"))) {
                final String base64Encoded = element.getValue();
                final byte[] binary = Base64.getDecoder().decode(base64Encoded.getBytes(Charset.forName("UTF-8")));
                return ImmutableValue.of(binary);
            } else if (name.contentEquals("string")) {
                return ImmutableValue.of(element.getValue());
            } else if (name.contentEquals("byte")) {
                return ImmutableValue.of(Byte.valueOf(element.getValue()));
            } else if (name.contentEquals("short")) {
                return ImmutableValue.of(Short.valueOf(element.getValue()));
            } else if (name.contentEquals("integer")) {
                return ImmutableValue.of(Integer.valueOf(element.getValue()));
            } else if (name.contentEquals("long")) {
                return ImmutableValue.of(Long.valueOf(element.getValue()));
            } else if (name.contentEquals("float")) {
                return ImmutableValue.of(Float.valueOf(element.getValue()));
            } else if (name.contentEquals("double")) {
                return ImmutableValue.of(Double.valueOf(element.getValue()));
            } else if (name.contentEquals("collection")) {
                final ImmutableList.Builder<Object> objectBuilder = ImmutableList.builder();
                for (final Element element1 : element.getChildren()) {
                    if (element1.getName().contentEquals("entry")) {
                        final List<Element> children = element1.getChildren();
                        if (children.size() == 2) {
                            final Element keyElement = children.get(0);
                            final Element valueElement = children.get(1);

                            final Value key = xmlToValue().apply(keyElement);
                            final Value value = xmlToValue().apply(valueElement);
                            final Map.Entry entry = new AbstractMap.SimpleEntry(key.value(), value.value());
                            objectBuilder.add(entry);
                        }
                    } else {
                        final Object rawValue = xmlToValue().apply(element1).value();
                        if (Objects.nonNull(rawValue)) {
                            objectBuilder.add(rawValue);
                        }
                    }
                }

                return ImmutableValue.of(objectBuilder.build());
            } else if (name.contentEquals("property-node")) {
                return ImmutableValue.of((xmlToPropertyNode().apply(element)));
            } else {
                throw new IllegalArgumentException(
                        new XMLOutputter(Format.getPrettyFormat()).outputString(element));
            }
        };
    }

    default Function<Property, Element> propertyToXml() {
        return property -> {
            Objects.requireNonNull(property, "property is null");

            final Element element = new Element(property.key());
            for (final Attribute attribute : property.attributes()) {
                element.setAttribute(attribute.key(), attribute.value());
            }

            element.addContent(valueToXml().apply(property.value()));

            return element;
        };
    }

    default Function<PropertyNode, Element> propertyNodeToXml() {
        return propertyNode -> {
            Objects.requireNonNull(propertyNode, "propertyNode is null");

            final Element element = new Element("property-node");
            for (final Property property : propertyNode) {
                element.addContent(propertyToXml().apply(property));
            }

            return element;
        };
    }

    default Function<Value, Element> valueToXml() {
        return value -> {
            Objects.requireNonNull(value, "value is null");

            if (value.isNull()) {
                return new Element("null");
            } else if (value.isBoolean()) {
                return new Element("boolean").setText(value.asBoolean().toString());
            } else if (value.isInstant()) {
                return new Element("instant").setText(value.asString());
            } else if (value.isBinary()) {
                return new Element("byte-array").setText(value.asString());
            } else if (value.isString()) {
                return new Element("string").setText(value.asString());
            } else if (value.isByte()) {
                return new Element("byte").setText(value.asByte().toString());
            } else if (value.isShort()) {
                return new Element("short").setText(value.asShort().toString());
            } else if (value.isInteger()) {
                return new Element("integer").setText(value.asInteger().toString());
            } else if (value.isLong()) {
                return new Element("long").setText(value.asLong().toString());
            } else if (value.isFloat()) {
                return new Element("float").setText(value.asFloat().toString());
            } else if (value.isDouble()) {
                return new Element("double").setText(value.asDouble().toString());
            } else if (value.isCollection()) {
                final Element rootElement = new Element("collection");
                for (final Object object : value.asCollection()) {
                    if (object instanceof Map.Entry) {
                        final Map.Entry entry = (Map.Entry) object;
                        final Element entryElement = new Element("entry");
                        final Element keyElement = valueToXml().apply(ImmutableValue.of(entry.getKey()));
                        final Element valueElement = valueToXml().apply(ImmutableValue.of(entry.getValue()));
                        entryElement.addContent(keyElement).addContent(valueElement);
                        rootElement.addContent(entryElement);
                    } else {
                        final Element element = valueToXml().apply(ImmutableValue.of(object));
                        rootElement.addContent(element);
                    }
                }

                return rootElement;

            } else if (value.isPropertyNode()) {
                return propertyNodeToXml().apply(value.asPropertyNode());
            } else {
                throw new IllegalArgumentException(value.toString());
            }

        };
    }

    default Element xpath(final PropertyNode propertyNode, final String expression) throws XPathException {
        Objects.requireNonNull(propertyNode, "propertyNode is null");
        Objects.requireNonNull(expression, "XPath expression is null");
        if (expression.trim().isEmpty()) {
            throw new IllegalArgumentException("XPath expression is empty");
        }

        try {
            final Processor processor = new Processor(false);
            final DocumentBuilder builder = processor.newDocumentBuilder();
            final XPathCompiler xpc = processor.newXPathCompiler();

            final XPathSelector selector = xpc.compile(expression).load();
            final Element element = propertyNodeToXml().apply(propertyNode);
            final XMLOutputter xmlOutputter = new XMLOutputter(Format.getPrettyFormat());

            final ByteArrayOutputStream baos = new ByteArrayOutputStream();
            xmlOutputter.output(element, baos);
            final Reader reader = new InputStreamReader(new ByteArrayInputStream(baos.toByteArray()),
                    String.valueOf(Charset.forName("UTF-8")));
            final InputSource inputSource = new InputSource(reader);
            inputSource.setEncoding(String.valueOf(Charset.forName("UTF-8")));

            selector.setContextItem(builder.build(new SAXSource(inputSource)));
            final XdmValue xdmValue = selector.evaluate();

            final String content = String.format("<%s>%s</%s>", "XPathResult", xdmValue.toString(), "XPathResult");
            final Document doc = new SAXBuilder().build(new StringReader(content));

            return doc.getRootElement();

        } catch (Exception exception) {
            throw new XPathException(exception);
        }

    }

}