eu.delving.x3ml.X3MLEngine.java Source code

Java tutorial

Introduction

Here is the source code for eu.delving.x3ml.X3MLEngine.java

Source

//===========================================================================
//    Copyright 2014 Delving B.V.
//
//    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 eu.delving.x3ml;

import eu.delving.x3ml.engine.Generator;
import eu.delving.x3ml.engine.Root;

import org.apache.commons.io.IOUtils;
import org.w3c.dom.Element;
import org.w3c.dom.ls.LSInput;
import org.w3c.dom.ls.LSResourceResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

import com.hp.hpl.jena.rdf.model.Model;

import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import static eu.delving.x3ml.engine.X3ML.Helper.x3mlStream;
import static eu.delving.x3ml.engine.X3ML.MappingNamespace;
import static eu.delving.x3ml.engine.X3ML.RootElement;

/**
 * The engine is created from an X3ML file which is loaded from an input stream.
 *
 * It has an execute method which takes a DOM root node and a value generator
 * and produces a graph in its output.
 *
 * @author Gerald de Jong <gerald@delving.eu>
 */

public class X3MLEngine {
    private static final String VERSION = "1.0";
    private RootElement rootElement;
    private NamespaceContext namespaceContext = new XPathContext();
    private List<String> prefixes = new ArrayList<String>();

    public static List<String> validate(InputStream inputStream) {
        try {
            return validateStream(inputStream);
        } catch (SAXException e) {
            throw new X3MLException("Unable to validate: SAX", e);
        } catch (IOException e) {
            throw new X3MLException("Unable to validate: IO", e);
        }
    }

    public static X3MLEngine load(InputStream inputStream) throws X3MLException {
        RootElement rootElement = (RootElement) x3mlStream().fromXML(inputStream);
        if (!VERSION.equals(rootElement.version)) {
            throw exception("Incorrect X3ML Version " + rootElement.version + ", expected " + VERSION);
        }
        return new X3MLEngine(rootElement);
    }

    public static void save(X3MLEngine engine, OutputStream outputStream) throws X3MLException {
        x3mlStream().toXML(engine.rootElement, outputStream);
    }

    public static X3MLException exception(String message) {
        return new X3MLException(message);
    }

    public static X3MLException exception(String message, Throwable throwable) {
        return new X3MLException(message, throwable);
    }

    public Output execute(Element sourceRoot, Generator generator) throws X3MLException {
        Root rootContext = new Root(sourceRoot, generator, namespaceContext, prefixes);
        generator.setDefaultArgType(rootElement.sourceType);
        generator.setLanguageFromMapping(rootElement.language);
        if (rootElement.namespaces != null) {
            for (MappingNamespace mn : rootElement.namespaces) {
                generator.setNamespace(mn.prefix, mn.uri);
            }
        }
        rootElement.apply(rootContext);
        return rootContext.getModelOutput();
    }

    public String toString() {
        return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + x3mlStream().toXML(rootElement);
    }

    public interface Output {

        void write(PrintStream printStream, String rdfFormat);

        void writeXML(PrintStream printStream);

        Model getModel();

        String[] toStringArray();

    }

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

    private X3MLEngine(RootElement rootElement) {
        this.rootElement = rootElement;
        if (this.rootElement.namespaces != null) {
            for (MappingNamespace namespace : this.rootElement.namespaces) {
                ((XPathContext) namespaceContext).addNamespace(namespace.prefix, namespace.uri);
                prefixes.add(namespace.prefix);
            }
        }
    }

    private class XPathContext implements NamespaceContext {
        private Map<String, String> prefixUri = new TreeMap<String, String>();
        private Map<String, String> uriPrefix = new TreeMap<String, String>();

        void addNamespace(String prefix, String uri) {
            prefixUri.put(prefix, uri);
            uriPrefix.put(uri, prefix);
        }

        @Override
        public String getNamespaceURI(String prefix) {
            if (prefix == null) {
                throw new X3MLException("Null prefix!");
            }
            if (prefixUri.size() == 1) {
                return prefixUri.values().iterator().next();
            }
            return prefixUri.get(prefix);
        }

        @Override
        public String getPrefix(String namespaceURI) {
            return uriPrefix.get(namespaceURI);
        }

        @Override
        public Iterator getPrefixes(String namespaceURI) {
            String prefix = getPrefix(namespaceURI);
            if (prefix == null)
                return null;
            List<String> list = new ArrayList<String>();
            list.add(prefix);
            return list.iterator();
        }
    }

    public static List<String> validateStream(InputStream inputStream) throws SAXException, IOException {
        Schema schema = schemaFactory().newSchema(new StreamSource(inputStream("x3ml_v1.0.xsd")));
        Validator validator = schema.newValidator();
        final List<String> errors = new ArrayList<String>();
        validator.setErrorHandler(new ErrorHandler() {
            @Override
            public void warning(SAXParseException exception) throws SAXException {
                errors.add(errorMessage(exception));
            }

            @Override
            public void error(SAXParseException exception) throws SAXException {
                errors.add(errorMessage(exception));
            }

            @Override
            public void fatalError(SAXParseException exception) throws SAXException {
                errors.add(errorMessage(exception));
            }
        });
        StreamSource source = new StreamSource(inputStream);
        validator.validate(source);
        return errors;
    }

    private static SchemaFactory schemaFactory() {
        SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
        try {
            //            schemaFactory.setFeature(Constants.XERCES_FEATURE_PREFIX + Constants.CTA_FULL_XPATH_CHECKING_FEATURE, true);
            schemaFactory.setResourceResolver(new ResourceResolver());
        } catch (Exception e) {
            throw new RuntimeException("Configuring schema factory", e);
        }
        return schemaFactory;
    }

    private static String errorMessage(SAXParseException e) {
        return String.format("%d:%d - %s", e.getLineNumber(), e.getColumnNumber(), e.getMessage());
    }

    public static class ResourceResolver implements LSResourceResolver {

        @Override
        public LSInput resolveResource(String type, final String namespaceUri, final String publicId,
                final String systemId, final String baseUri) {
            return new ResourceInput(publicId, systemId, baseUri);
        }

        private static class ResourceInput implements LSInput {

            private String publicId, systemId, baseUri;

            private ResourceInput(String publicId, String systemId, String baseUri) {
                this.publicId = publicId;
                this.systemId = systemId;
                this.baseUri = baseUri;
            }

            @Override
            public Reader getCharacterStream() {
                try {
                    return new InputStreamReader(getByteStream(), "UTF-8");
                } catch (UnsupportedEncodingException e) {
                    throw new RuntimeException(e);
                }
            }

            @Override
            public void setCharacterStream(Reader reader) {
                throw new RuntimeException();
            }

            @Override
            public InputStream getByteStream() {
                return inputStream(systemId);
            }

            @Override
            public void setByteStream(InputStream inputStream) {
                throw new RuntimeException();
            }

            @Override
            public String getStringData() {
                try {
                    return IOUtils.toString(getByteStream());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }

            @Override
            public void setStringData(String s) {
                throw new RuntimeException();
            }

            @Override
            public String getSystemId() {
                return systemId;
            }

            @Override
            public void setSystemId(String s) {
                this.systemId = s;
            }

            @Override
            public String getPublicId() {
                return publicId;
            }

            @Override
            public void setPublicId(String s) {
                this.publicId = s;
            }

            @Override
            public String getBaseURI() {
                return baseUri;
            }

            @Override
            public void setBaseURI(String s) {
                throw new RuntimeException();
            }

            @Override
            public String getEncoding() {
                return "UTF-8";
            }

            @Override
            public void setEncoding(String s) {
                throw new RuntimeException();
            }

            @Override
            public boolean getCertifiedText() {
                return false;
            }

            @Override
            public void setCertifiedText(boolean b) {
                throw new RuntimeException();
            }
        }
    }

    private static InputStream inputStream(String fileName) {
        return X3MLEngine.class.getResourceAsStream("/validation/" + fileName);
    }

    public static class X3MLException extends RuntimeException {

        public X3MLException(String s) {
            super(s);
        }

        public X3MLException(String s, Throwable throwable) {
            super(s, throwable);
        }
    }
}