fr.lirmm.graphik.graal.io.owl.OWL2Parser.java Source code

Java tutorial

Introduction

Here is the source code for fr.lirmm.graphik.graal.io.owl.OWL2Parser.java

Source

/*
 * Copyright (C) Inria Sophia Antipolis - Mditerrane / LIRMM
 * (Universit de Montpellier & CNRS) (2014 - 2015)
 *
 * Contributors :
 *
 * Clment SIPIETER <clement.sipieter@inria.fr>
 * Mlanie KNIG
 * Swan ROCHER
 * Jean-Franois BAGET
 * Michel LECLRE
 * Marie-Laure MUGNIER <mugnier@lirmm.fr>
 *
 *
 * This file is part of Graal <https://graphik-team.github.io/graal/>.
 *
 * This software is governed by the CeCILL  license under French law and
 * abiding by the rules of distribution of free software.  You can  use,
 * modify and/ or redistribute the software under the terms of the CeCILL
 * license as circulated by CEA, CNRS and INRIA at the following URL
 * "http://www.cecill.info".
 *
 * As a counterpart to the access to the source code and  rights to copy,
 * modify and redistribute granted by the license, users are provided only
 * with a limited warranty  and the software's author,  the holder of the
 * economic rights,  and the successive licensors  have only  limited
 * liability.
 *
 * In this respect, the user's attention is drawn to the risks associated
 * with loading,  using,  modifying and/or developing or reproducing the
 * software by the user in light of its specific status of free software,
 * that may mean  that it is complicated to manipulate,  and  that  also
 * therefore means  that it is reserved for developers  and  experienced
 * professionals having in-depth computer knowledge. Users are therefore
 * encouraged to load and test the software's suitability as regards their
 * requirements in conditions enabling the security of their systems and/or
 * data to be ensured and,  more generally, to use and operate it in the
 * same conditions as regards security.
 *
 * The fact that you are presently reading this means that you have had
 * knowledge of the CeCILL license and that you accept its terms.
 */
/**
 * 
 */
package fr.lirmm.graphik.graal.io.owl;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.apache.commons.io.input.ReaderInputStream;
import org.semanticweb.owlapi.apibinding.OWLManager;
import org.semanticweb.owlapi.formats.PrefixDocumentFormat;
import org.semanticweb.owlapi.model.OWLAnonymousIndividual;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLDocumentFormat;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyCreationException;
import org.semanticweb.owlapi.model.OWLOntologyManager;
import org.semanticweb.owlapi.model.parameters.Imports;
import org.semanticweb.owlapi.util.DefaultPrefixManager;
import org.semanticweb.owlapi.util.ShortFormProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import fr.lirmm.graphik.graal.api.core.Atom;
import fr.lirmm.graphik.graal.api.core.AtomSet;
import fr.lirmm.graphik.graal.api.core.InMemoryAtomSet;
import fr.lirmm.graphik.graal.api.core.Predicate;
import fr.lirmm.graphik.graal.api.core.Rule;
import fr.lirmm.graphik.graal.api.core.Substitution;
import fr.lirmm.graphik.graal.api.core.Term;
import fr.lirmm.graphik.graal.api.io.Parser;
import fr.lirmm.graphik.graal.core.DefaultNegativeConstraint;
import fr.lirmm.graphik.graal.core.TreeMapSubstitution;
import fr.lirmm.graphik.graal.core.atomset.LinkedListAtomSet;
import fr.lirmm.graphik.graal.core.atomset.graph.DefaultInMemoryGraphAtomSet;
import fr.lirmm.graphik.graal.core.factory.DefaultAtomFactory;
import fr.lirmm.graphik.graal.core.factory.RuleFactory;
import fr.lirmm.graphik.util.Prefix;
import fr.lirmm.graphik.util.stream.AbstractCloseableIterator;
import fr.lirmm.graphik.util.stream.ArrayBlockingStream;
import fr.lirmm.graphik.util.stream.transformator.Transformator;

/**
 * This class parses OWL2 ontologies and converts them into Rule, Facts and
 * Constraints.
 * 
 * @author Clment Sipieter (INRIA) {@literal <clement@6pi.fr>}
 *
 */
public class OWL2Parser extends AbstractCloseableIterator<Object> implements Parser<Object> {

    private static final Logger LOGGER = LoggerFactory.getLogger(OWL2Parser.class);
    private static final RuleTransformator RULE_TRANSFO = new RuleTransformator();
    private static final InMemoryAtomSet BOTTOM_ATOMSET = new LinkedListAtomSet(
            DefaultAtomFactory.instance().getBottom());

    private ArrayBlockingStream<Object> buffer = new ArrayBlockingStream<Object>(512);

    private InputStream inputStream = null;
    private OWLOntologyManager manager = OWLManager.createOWLOntologyManager();
    private OWLOntology ontology;
    private boolean prefixEnable = true;

    // /////////////////////////////////////////////////////////////////////////
    // CONSTRUCTOR
    // /////////////////////////////////////////////////////////////////////////

    /**
     * Constructor for parsing from the given reader.
     * 
     * @param inputStream
     * @throws OWL2ParserException
     */
    public OWL2Parser(InputStream stream) throws OWL2ParserException {
        this.inputStream = stream;
        try {
            this.ontology = this.manager.loadOntologyFromOntologyDocument(stream);
        } catch (OWLOntologyCreationException e) {
            throw new OWL2ParserException(e);
        }
        ShortFormProvider sfp = getShortFormProvider(this.ontology);
        new Thread(new Producer(this.ontology, sfp, buffer)).start();
    }

    /**
     * Constructor for parsing from the standard input.
     * 
     * @throws OWL2ParserException
     */
    public OWL2Parser() throws OWL2ParserException {
        this(System.in);
    }

    /**
     * Constructor for parsing from the given file.
     * 
     * @param file
     * @throws FileNotFoundException
     * @throws OWL2ParserException
     */
    public OWL2Parser(File file) throws FileNotFoundException, OWL2ParserException {
        try {
            this.ontology = this.manager.loadOntologyFromOntologyDocument(file);
        } catch (OWLOntologyCreationException e) {
            throw new OWL2ParserException(e);
        }
        ShortFormProvider sfp = getShortFormProvider(this.ontology);
        new Thread(new Producer(this.ontology, sfp, buffer)).start();
    }

    /**
     * Constructor for parsing the content of the string s as OWL content.
     * 
     * @param s
     * @throws OWL2ParserException
     */
    public OWL2Parser(String s) throws OWL2ParserException {
        this(new ByteArrayInputStream(s.getBytes()));
    }

    /**
     * Constructor for parsing the given InputStream.
     * 
     * @param in
     * @throws OWL2ParserException
     */
    public OWL2Parser(Reader in) throws OWL2ParserException {
        this(new ReaderInputStream(in));
    }

    // /////////////////////////////////////////////////////////////////////////
    // METHODS
    // /////////////////////////////////////////////////////////////////////////

    /**
     * Enable or disable prefix short form.
     * 
     * @param b
     *            Default value is true.
     */
    public void prefixEnable(boolean b) {
        this.prefixEnable = b;
    }

    @Override
    public boolean hasNext() {
        return buffer.hasNext();
    }

    @Override
    public Object next() {
        return buffer.next();
    }

    /**
     * Closes the stream and releases any system resources associated with it.
     * Closing a previously closed parser has no effect.
     * 
     * @throws IOException
     */
    @Override
    public void close() {
        if (this.inputStream != null) {
            try {
                this.inputStream.close();
            } catch (IOException e) {
                LOGGER.error("Error during closing inputStream");
            }
            this.inputStream = null;
        }
    }

    // /////////////////////////////////////////////////////////////////////////
    // PRIVATE METHODS
    // /////////////////////////////////////////////////////////////////////////

    private ShortFormProvider getShortFormProvider(OWLOntology ontology) {
        OWLDocumentFormat format = this.manager.getOntologyFormat(this.ontology);
        DefaultPrefixManager pm = new DefaultPrefixManager();
        if (prefixEnable && format.isPrefixOWLOntologyFormat()) {
            PrefixDocumentFormat prefixFormat = format.asPrefixOWLOntologyFormat();

            Map<String, String> prefixMap = prefixFormat.getPrefixName2PrefixMap();

            Set<String> forbiddenPrefix = new TreeSet<String>();
            forbiddenPrefix.add("xml:");
            forbiddenPrefix.add("rdf:");
            forbiddenPrefix.add("rdfs:");
            forbiddenPrefix.add("owl:");

            for (Map.Entry<String, String> entry : prefixMap.entrySet()) {
                String prefix = entry.getKey();
                if (!forbiddenPrefix.contains(prefix)) {
                    pm.setPrefix(prefix, entry.getValue());
                    prefix = prefix.substring(0, prefix.length() - 1);
                    buffer.write(new Prefix(prefix, entry.getValue()));
                }
            }
        }
        return pm;
    }

    // /////////////////////////////////////////////////////////////////////////
    // PRIVATE CLASSES
    // /////////////////////////////////////////////////////////////////////////

    private static class Producer implements Runnable {

        private OWLOntology onto;
        private ArrayBlockingStream<Object> buffer;
        private ShortFormProvider shortForm;

        Producer(OWLOntology onto, ShortFormProvider shortForm, ArrayBlockingStream<Object> buffer) {
            this.onto = onto;
            this.buffer = buffer;
            this.shortForm = shortForm;
        }

        @Override
        public void run() {
            try {

                OWLAxiomParser visitor = new OWLAxiomParser(shortForm);

                // process axioms containing anonymous individuals
                Set<OWLAnonymousIndividual> anonymousIndividuals = onto.getAnonymousIndividuals();

                while (!anonymousIndividuals.isEmpty()) {
                    Iterator<OWLAnonymousIndividual> it = anonymousIndividuals.iterator();
                    TreeSet<OWLAnonymousIndividual> localAnonymousIndividuals = new TreeSet<>();
                    localAnonymousIndividuals.add(it.next());
                    it.remove();

                    InMemoryAtomSet fact = new LinkedListAtomSet();
                    AnonymousProcessor processor = new AnonymousProcessor(fact);

                    // keep an anonymous individu and process it
                    while (!localAnonymousIndividuals.isEmpty()) {
                        OWLAnonymousIndividual i = localAnonymousIndividuals.pollFirst();

                        for (OWLAxiom a : onto.getReferencingAxioms(i, Imports.EXCLUDED)) {
                            // add individu referenced by this axiom to the
                            // local set and remove it from the global set
                            for (OWLAnonymousIndividual neestedIndividu : a.getAnonymousIndividuals()) {
                                if (anonymousIndividuals.contains(neestedIndividu)) {
                                    anonymousIndividuals.remove(neestedIndividu);
                                    localAnonymousIndividuals.add(neestedIndividu);
                                }
                            }

                            // process current axiom
                            processAxiom(a, visitor, processor);
                        }
                    }
                    buffer.write(fact);
                }

                // process other axioms
                for (OWLAxiom a : onto.getAxioms()) {
                    if (!a.getAnonymousIndividuals().isEmpty()) {
                        continue;
                    }
                    processAxiom(a, visitor, noAnonymousProcessor);
                }

            } finally {
                buffer.close();
            }
        }

        /**
         * @param a
         * @return
         */
        private static void processAxiom(OWLAxiom a, OWLAxiomParser visitor, Processor p) {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("### OWLAxiom: " + a.toString());
            }

            Iterable<? extends Object> iterable = a.accept(visitor);
            if (iterable != null) {
                for (Object o : iterable) {
                    if (o != null) {
                        if (o instanceof Rule) {
                            o = RULE_TRANSFO.transform((Rule) o);
                        }
                        if (o != null) {
                            if (LOGGER.isDebugEnabled()) {
                                LOGGER.debug(" => " + o.toString());
                            }
                            p.exec(o);
                        }
                    }
                }
            }
        }

        private static interface Processor {
            void exec(Object o);
        }

        private static class AnonymousProcessor implements Processor {

            private InMemoryAtomSet fact;

            public AnonymousProcessor(InMemoryAtomSet fact) {
                this.fact = fact;
            }

            @Override
            public void exec(Object o) {
                if (!(o instanceof AtomSet)) {
                    LOGGER.error("AnonymousIndividuals not allowed here: " + o);
                } else {
                    fact.addAll((AtomSet) o);
                }
            }

        }

        private NoAnonymousProcessor noAnonymousProcessor = new NoAnonymousProcessor();

        private class NoAnonymousProcessor implements Processor {
            @Override
            public void exec(Object o) {
                buffer.write(o);
            }
        }

    }

    /**
     * This filter some rule into a fact or a negativeConstraint or delete it.
     * 
     * @param r
     * @return
     */
    private static class RuleTransformator implements Transformator<Rule, Object> {

        @Override
        public Object transform(Rule r) {
            InMemoryAtomSet body = r.getBody();
            InMemoryAtomSet head = r.getHead();

            Iterator<Atom> bodyIt = body.iterator();

            // Remove equality in body
            Substitution s = new TreeMapSubstitution();
            while (bodyIt.hasNext()) {
                Atom a = bodyIt.next();
                if (a.getPredicate().equals(Predicate.EQUALITY)
                        && (!a.getTerm(0).isConstant() || !a.getTerm(1).isConstant())) {
                    bodyIt.remove();
                    if (a.getTerm(0).isConstant()) {
                        s.put(a.getTerm(1), a.getTerm(0));
                    } else {
                        s.put(a.getTerm(0), a.getTerm(1));
                    }
                }
            }

            body = removeUselessTopInBody(removeUselessBottom(s.createImageOf(body)));
            bodyIt = body.iterator();

            head = removeUselessTopInHead(removeUselessBottom(s.createImageOf(head)));
            Iterator<Atom> headIt = head.iterator();

            // USELESS STATEMENT
            if (!headIt.hasNext()) {
                return null;
                // CONSTRAINTS
            } else if (headIt.next().getPredicate().equals(Predicate.BOTTOM)) {
                return new DefaultNegativeConstraint(body);
                // ASSERTIONS
            } else if (!bodyIt.hasNext()) {
                return head;
                // USELESS STATEMENT
            } else if (bodyIt.next().getPredicate().equals(Predicate.BOTTOM)) {
                return null;
            } else {
                return RuleFactory.instance().create(body, head);
            }
        }
    }

    /**
     * bottom and A => bottom
     * 
     * @param atomset
     * @return
     */
    private static InMemoryAtomSet removeUselessBottom(InMemoryAtomSet atomset) {
        Iterator<Atom> it = atomset.iterator();
        Atom a;
        while (it.hasNext()) {
            a = it.next();
            if (a.getPredicate().equals(Predicate.BOTTOM)) {
                return BOTTOM_ATOMSET;
            }
        }

        return atomset;
    }

    private static InMemoryAtomSet removeUselessTopInHead(InMemoryAtomSet atomset) {
        InMemoryAtomSet newAtomset = new LinkedListAtomSet();
        Iterator<Atom> it = atomset.iterator();
        Atom a;
        while (it.hasNext()) {
            a = it.next();
            if (!a.getPredicate().equals(Predicate.TOP)) {
                newAtomset.add(a);
            }
        }

        return newAtomset;
    }

    private static InMemoryAtomSet removeUselessTopInBody(InMemoryAtomSet atomset) {
        InMemoryAtomSet newAtomset = new DefaultInMemoryGraphAtomSet();
        Iterator<Atom> it = atomset.iterator();
        Atom a;
        while (it.hasNext()) {
            a = it.next();
            if (!a.getPredicate().equals(Predicate.TOP)) {
                newAtomset.add(a);
                it.remove();
            } else {

            }
        }

        // for each top predicate
        Set<Term> terms = newAtomset.getTerms();
        it = atomset.iterator();
        while (it.hasNext()) {
            a = it.next();
            if (!terms.contains(a.getTerm(0))) {
                newAtomset.add(a);
            }
        }

        return newAtomset;
    }

}