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

org.coode.owlapi.obo.renderer.OBOFlatFileRenderer Maven / Gradle / Ivy

/*
 * This file is part of the OWL API.
 *
 * The contents of this file are subject to the LGPL License, Version 3.0.
 *
 * Copyright (C) 2011, The University of Manchester
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see http://www.gnu.org/licenses/.
 *
 *
 * Alternatively, the contents of this file may be used under the terms of the Apache License, Version 2.0
 * in which case, the provisions of the Apache License Version 2.0 are applicable instead of those above.
 *
 * Copyright 2011, University of Manchester
 *
 * 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.coode.owlapi.obo.renderer;

import java.io.IOException;
import java.io.Writer;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.coode.owlapi.obo.parser.OBOVocabulary;
import org.semanticweb.owlapi.io.AbstractOWLRenderer;
import org.semanticweb.owlapi.io.OWLRendererException;
import org.semanticweb.owlapi.io.XMLUtils;
import org.semanticweb.owlapi.model.AxiomType;
import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.model.OWLAnnotation;
import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLClassAxiom;
import org.semanticweb.owlapi.model.OWLClassExpression;
import org.semanticweb.owlapi.model.OWLDataFactory;
import org.semanticweb.owlapi.model.OWLDataProperty;
import org.semanticweb.owlapi.model.OWLDataPropertyExpression;
import org.semanticweb.owlapi.model.OWLDataRange;
import org.semanticweb.owlapi.model.OWLDisjointClassesAxiom;
import org.semanticweb.owlapi.model.OWLEntity;
import org.semanticweb.owlapi.model.OWLEquivalentClassesAxiom;
import org.semanticweb.owlapi.model.OWLImportsDeclaration;
import org.semanticweb.owlapi.model.OWLIndividual;
import org.semanticweb.owlapi.model.OWLLiteral;
import org.semanticweb.owlapi.model.OWLNamedIndividual;
import org.semanticweb.owlapi.model.OWLObjectIntersectionOf;
import org.semanticweb.owlapi.model.OWLObjectProperty;
import org.semanticweb.owlapi.model.OWLObjectPropertyExpression;
import org.semanticweb.owlapi.model.OWLObjectUnionOf;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyManager;
import org.semanticweb.owlapi.model.OWLProperty;
import org.semanticweb.owlapi.model.OWLPropertyExpression;
import org.semanticweb.owlapi.model.OWLRestriction;
import org.semanticweb.owlapi.model.OWLSubClassOfAxiom;
import org.semanticweb.owlapi.model.OWLSubPropertyChainOfAxiom;
import org.semanticweb.owlapi.model.SWRLRule;
import org.semanticweb.owlapi.util.NamespaceUtil;
import org.semanticweb.owlapi.util.SimpleShortFormProvider;
import org.semanticweb.owlapi.util.VersionInfo;
import org.semanticweb.owlapi.vocab.OWLRDFVocabulary;

/**
 * Author: Nick Drummond
* The University Of Manchester
* Bio Health Informatics Group
* Date: Dec 17, 2008

* Renders OBO 1.2 flat file format. * OBO 1.2 is a subset of OWL, so a rendering of an arbitrary OWL ontology may be incomplete in OBO. * Several features are currently not implemented: * - Exception handling for unsupported constructs - these are currently reported in stderr * - axiom annotations (these might be serialisable as inline comments - although OBO parsers provide no * guarantees these will be roundtripped) * - anonymous classes/properties - it is not clear what this means in OBO * - datatype restrictions * - namespace and derived tags for relationships etc - it is not clear what this means in OBO * - preservation of ordering on roundtripping exercises * - Stanzas are ordered (classes, obj/data props then instances) * - Entities are ordered by ID in each Stanza type * - OBO tags are ordered WRT the OBO specifications * Additional points: * - cardinality is expressed as an '=' separated tag value pair in [1] and underspecified in [2]. OBOEdit 2 * does not parse this (at the time of writing) so this has been changed to standard TVP syntax * - OBO 1.2 currently specifies pairwise disjointness which this renderer follows so files may get large * - Exceptions are caught along the way and then wrapped in an OBOStorageIncompleteException which is thrown * after serialisation ends * References: * [1] http://www.cs.man.ac.uk/~horrocks/obo/ * [2] http://www.geneontology.org/GO.format.obo-1_2.shtml */ public class OBOFlatFileRenderer extends AbstractOWLRenderer implements OBOExceptionHandler { private OBORelationshipGenerator relationshipHandler; private SimpleShortFormProvider sfp; private String defaultPrefix; private List exceptions = new ArrayList(); private NamespaceUtil nsUtil; private String defaultNamespace; private OWLDataFactory factory; @Deprecated protected OBOFlatFileRenderer(OWLOntologyManager owlOntologyManager) { this(); } protected OBOFlatFileRenderer() { relationshipHandler = new OBORelationshipGenerator(this); sfp = new SimpleShortFormProvider(); } @Override public void render(OWLOntology ontology, Writer writer) throws OWLRendererException { factory = ontology.getOWLOntologyManager().getOWLDataFactory(); exceptions.clear(); IRI ontologyIRI = ontology.getOntologyID().getOntologyIRI(); if (ontologyIRI != null) { final String ontURIStr = ontologyIRI.toString(); if (ontURIStr.endsWith("/")) { defaultNamespace = ontURIStr; } else { defaultNamespace = ontURIStr + "#"; } } else { defaultNamespace = "urn:defaultOBONamespace:ontology" + System.currentTimeMillis() + "#"; System.err.println("WARNING: anonymous ontology saved in OBO format. Default namespace created for it."); } nsUtil = new NamespaceUtil(); defaultPrefix = nsUtil.getPrefix(defaultNamespace); writeHeader(ontology, writer); writeStanzas(ontology, writer); if (!exceptions.isEmpty()) { throw new OBOStorageIncompleteException(exceptions); } } @Override public void addException(OBOStorageException exception) { exceptions.add(exception); } @Override public List getExceptions() { return exceptions; } private void writeHeader(OWLOntology ontology, Writer writer) { OBOTagValuePairList tvpList = new OBOTagValuePairList(OBOVocabulary.getHeaderTags()); tvpList.setDefault(OBOVocabulary.DEFAULT_NAMESPACE, defaultPrefix); for (OWLAnnotation ax : ontology.getAnnotations()) { if (ax.getProperty().isComment()) { tvpList.addPair(OBOVocabulary.REMARK, ((OWLLiteral) ax.getValue()).getLiteral()); } else { tvpList.visit(ax); } } for (OWLImportsDeclaration importDecl : ontology.getImportsDeclarations()) { tvpList.addPair(OBOVocabulary.IMPORT, importDecl.getIRI().toString()); } Map namespace2PrefixMap = loadUsedNamespaces(ontology); for (String namespace : namespace2PrefixMap.keySet()) { String mapping = namespace2PrefixMap.get(namespace) + " " + namespace; tvpList.addPair(OBOVocabulary.ID_SPACE, mapping); } // overwrite the existing values for below tvpList.setPair(OBOVocabulary.FORMAT_VERSION, "1.2"); tvpList.setPair(OBOVocabulary.DATE, getTimestampFormatter().format(new Date(System.currentTimeMillis()))); tvpList.setPair(OBOVocabulary.SAVED_BY, System.getProperty("user.name")); tvpList.setPair(OBOVocabulary.AUTO_GENERATED_BY, VersionInfo.getVersionInfo().toString()); tvpList.write(writer); } private Map loadUsedNamespaces(OWLOntology ontology) { for (OWLEntity entity : ontology.getSignature()) { final IRI base = IRI.create(XMLUtils.getNCNamePrefix(entity.getIRI() .toString())); nsUtil.getPrefix(base.toString()); } return nsUtil.getNamespace2PrefixMap(); } private void writeStanzas(OWLOntology ontology, Writer writer) { write("\n\n! ---------------------- CLASSES -------------------------\n", writer); final List sortedClasses = new ArrayList(ontology.getClassesInSignature()); Collections.sort(sortedClasses); for (OWLClass cls : sortedClasses) { writeTermStanza(cls, ontology, writer); } write("\n\n! ---------------------- PROPERTIES -------------------------\n", writer); final List objProps = new ArrayList(ontology.getObjectPropertiesInSignature()); Collections.sort(objProps); for (OWLObjectProperty property : objProps) { writeTypeDefStanza(property, ontology, writer); } final List dataProps = new ArrayList(ontology.getDataPropertiesInSignature()); Collections.sort(dataProps); for (OWLDataProperty property : dataProps) { writeTypeDefStanza(property, ontology, writer); } write("\n\n! ---------------------- INSTANCES -------------------------\n", writer); final List individuals = new ArrayList(ontology.getIndividualsInSignature()); Collections.sort(individuals); for (OWLNamedIndividual individual : individuals) { writeInstanceStanza(individual, ontology, writer); } // scan ontology for other constructs that cannot be translated for (OWLClassAxiom ax : ontology.getGeneralClassAxioms()) { if (ax instanceof OWLSubClassOfAxiom) { exceptions.add(new OBOStorageException(ax, null, "Superclass GCI found in ontology cannot be translated to OBO")); } if (ax instanceof OWLEquivalentClassesAxiom) { exceptions.add(new OBOStorageException(ax, null, "Equivalent class GCI found in ontology cannot be translated to OBO")); } if (ax instanceof OWLDisjointClassesAxiom) { exceptions.add(new OBOStorageException(ax, null, "Disjoint axiom contains anonymous classes - cannot be translated to OBO")); } } for (SWRLRule r : ontology.getAxioms(AxiomType.SWRL_RULE)) { exceptions.add(new OBOStorageException(r, null, "SWRL rules cannot be translated to OBO")); } } private void writeTermStanza(OWLClass cls, OWLOntology ontology, Writer writer) { write("\n[", writer); write(OBOVocabulary.TERM.getName(), writer); write("]\n", writer); // TODO is_anonymous?? OBOTagValuePairList tvpList = new OBOTagValuePairList(OBOVocabulary.getTermStanzaTags()); handleEntityBase(cls, ontology, tvpList); relationshipHandler.setClass(cls); for (OWLClassExpression superCls : cls.getSuperClasses(ontology)) { if (!superCls.isAnonymous()) { String superclassID = getID(superCls.asOWLClass()); tvpList.addPair(OBOVocabulary.IS_A, superclassID); // TODO handle namespace and derived? } else if (superCls instanceof OWLRestriction) { // TODO handle datatype restrictions superCls.accept(relationshipHandler); } else { exceptions.add(new OBOStorageException(cls, superCls, "OBO format only supports named superclass or someValuesFrom restrictions")); } } final OWLClass owlThing = factory.getOWLThing(); // if no named superclass is specified, then this must be asserted to be a subclass of owlapi:Thing if (!cls.equals(owlThing) && tvpList.getValues(OBOVocabulary.IS_A).isEmpty()) { tvpList.addPair(OBOVocabulary.IS_A, getID(owlThing)); } for (OWLClassExpression equiv : cls.getEquivalentClasses(ontology)) { if (equiv instanceof OWLObjectIntersectionOf) { handleIntersection(cls, (OWLObjectIntersectionOf) equiv, tvpList); } else if (equiv instanceof OWLObjectUnionOf) { handleUnion(cls, (OWLObjectUnionOf) equiv, tvpList); } else if (equiv instanceof OWLRestriction) { /* OBO equivalence must be of the form "A and p some B and ..." * if this class is equiv to a restriction, put this into an intersection with owlapi:Thing as the named class */ OWLObjectIntersectionOf intersection = factory .getOWLObjectIntersectionOf(owlThing, equiv); handleIntersection(cls, intersection, tvpList); } else { // TODO handle datatype restrictions exceptions.add(new OBOStorageException(cls, equiv, "Cannot answer equivalent class that is not intersection or union")); } } for (OWLClassExpression disjoint : cls.getDisjointClasses(ontology)) { if (!disjoint.isAnonymous()) { tvpList.addPair(OBOVocabulary.DISJOINT_FROM, getID(disjoint.asOWLClass())); // TODO handle namespace and derived } else { exceptions.add(new OBOStorageException(cls, disjoint, "Found anonymous disjoint class that cannot be represented in OBO")); } } for (OBORelationship relationship : relationshipHandler.getOBORelationships()) { // TODO handle datatype restrictions handleRelationship(relationship, tvpList); } tvpList.write(writer); } private void handleIntersection(OWLClass cls, OWLObjectIntersectionOf intersectionOf, OBOTagValuePairList tvpList) { for (OWLClassExpression op : intersectionOf.getOperands()) { if (!op.isAnonymous()) { tvpList.addPair(OBOVocabulary.INTERSECTION_OF, getID(op.asOWLClass())); } else { relationshipHandler.setClass(cls); op.accept(relationshipHandler); Set relations = relationshipHandler.getOBORelationships(); if (!relations.isEmpty()) { OBORelationship rel = relations.iterator().next(); tvpList.addPair(OBOVocabulary.INTERSECTION_OF, renderRestriction(rel)); } else { exceptions.add(new OBOStorageException(cls, op, "Found operand in intersection that cannot be represented")); } } } // TODO handle namespace } private String renderRestriction(OBORelationship rel) { StringBuilder sb = new StringBuilder(getID(rel.getProperty())); sb.append(" "); sb.append(getID(rel.getFiller())); return sb.toString(); } private void handleUnion(OWLClass cls, OWLObjectUnionOf union, OBOTagValuePairList tvpList) { for (OWLClassExpression op : union.getOperands()) { if (!op.isAnonymous()) { tvpList.addPair(OBOVocabulary.UNION_OF, getID(op.asOWLClass())); } else { relationshipHandler.setClass(cls); op.accept(relationshipHandler); Set relations = relationshipHandler.getOBORelationships(); if (!relations.isEmpty()) { OBORelationship rel = relations.iterator().next(); tvpList.addPair(OBOVocabulary.UNION_OF, renderRestriction(rel)); } else { exceptions.add(new OBOStorageException(cls, op, "Found operand in union that cannot be represented")); } } } } private void handleRelationship(OBORelationship relationship, OBOTagValuePairList tvpList) { StringBuilder sb = new StringBuilder(); sb.append(renderRestriction(relationship)); sb.append("\n"); if (relationship.getCardinality() >= 0) { sb.append(OBOVocabulary.CARDINALITY.getName()); sb.append(":"); sb.append(Integer.toString(relationship.getCardinality())); sb.append("\n"); } if (relationship.getMaxCardinality() >= 0) { sb.append(OBOVocabulary.MAX_CARDINALITY.getName()); sb.append(":"); sb.append(Integer.toString(relationship.getMaxCardinality())); sb.append("\n"); } if (relationship.getMinCardinality() >= 0) { sb.append(OBOVocabulary.MIN_CARDINALITY.getName()); sb.append(":"); sb.append(Integer.toString(relationship.getMinCardinality())); sb.append("\n"); } tvpList.addPair(OBOVocabulary.RELATIONSHIP, sb.toString()); } private

OBOTagValuePairList handleCommonTypeDefStanza(P property, OWLOntology ontology, Writer writer) { write("\n[", writer); write(OBOVocabulary.TYPEDEF.getName(), writer); write("]\n", writer); OBOTagValuePairList tvpList = new OBOTagValuePairList(OBOVocabulary.getTypeDefStanzaTags()); handleEntityBase(property, ontology, tvpList); Set domains = property.getDomains(ontology); for (OWLClassExpression domain : domains) { if (!domain.isAnonymous()) { tvpList.addPair(OBOVocabulary.DOMAIN, getID(domain.asOWLClass())); } else { exceptions.add(new OBOStorageException(property, domain, "Anonymous domain that cannot be represented in OBO")); } } final Set> sp = property.getSuperProperties(ontology); for (OWLPropertyExpression superProp : sp) { if (!superProp.isAnonymous()) { tvpList.addPair(OBOVocabulary.IS_A, getID((OWLProperty) superProp)); } else { exceptions.add(new OBOStorageException(property, superProp, "Anonymous property in superProperty is not supported in OBO")); } } tvpList.setDefault(OBOVocabulary.IS_METADATA_TAG, "false"); // annotation properties only return tvpList; } private void writeTypeDefStanza(OWLObjectProperty property, OWLOntology ontology, Writer writer) { OBOTagValuePairList tvpList = handleCommonTypeDefStanza(property, ontology, writer); for (OWLClassExpression range : property.getRanges(ontology)) { if (!range.isAnonymous()) { tvpList.addPair(OBOVocabulary.RANGE, getID(range.asOWLClass())); } else { exceptions.add(new OBOStorageException(property, range, "Anonymous range that cannot be represented in OBO")); } } if (property.isAsymmetric(ontology)) { tvpList.addPair(OBOVocabulary.IS_ASYMMETRIC, "true"); } if (property.isReflexive(ontology)) { tvpList.addPair(OBOVocabulary.IS_REFLEXIVE, "true"); } if (property.isSymmetric(ontology)) { tvpList.addPair(OBOVocabulary.IS_SYMMETRIC, "true"); } if (property.isTransitive(ontology)) { tvpList.addPair(OBOVocabulary.IS_TRANSITIVE, "true"); } for (OWLObjectPropertyExpression inv : property.getInverses(ontology)) { if (!inv.isAnonymous()) { tvpList.addPair(OBOVocabulary.INVERSE, getID(inv.asOWLObjectProperty())); } else { exceptions.add(new OBOStorageException(property, inv, "Anonymous property in inverse is not supported in OBO")); } } // transitive over for (OWLSubPropertyChainOfAxiom ax : ontology.getAxioms(AxiomType.SUB_PROPERTY_CHAIN_OF)) { if (ax.getSuperProperty().equals(property)) { final List chain = ax.getPropertyChain(); if (chain.size() == 2 && chain.get(0).equals(property) && !chain.get(1).isAnonymous()) { tvpList.addPair(OBOVocabulary.TRANSITIVE_OVER, getID(chain.get(1).asOWLObjectProperty())); } else { exceptions.add(new OBOStorageException(property, ax, "Only property chains of form 'p o q -> p' supported")); } } } // TODO is RELATIONSHIP really a part of the typedef stanza? tvpList.write(writer); } private void writeTypeDefStanza(OWLDataProperty property, OWLOntology ontology, Writer writer) { OBOTagValuePairList tvpList = handleCommonTypeDefStanza(property, ontology, writer); for (OWLDataRange range : property.getRanges(ontology)) { if (range.isDatatype()) { tvpList.addPair(OBOVocabulary.RANGE, range.asOWLDatatype().getIRI().toString()); } else { exceptions.add(new OBOStorageException(property, range, "Complex data range cannot be represented in OBO")); } } // TODO is RELATIONSHIP really a part of the typedef stanza? tvpList.write(writer); } private void writeInstanceStanza(OWLNamedIndividual individual, OWLOntology ontology, Writer writer) { write("\n[", writer); write(OBOVocabulary.INSTANCE.getName(), writer); write("]\n", writer); OBOTagValuePairList tvpList = new OBOTagValuePairList(OBOVocabulary.getInstanceStanzaTags()); if (individual.isAnonymous()) { tvpList.setDefault(OBOVocabulary.IS_ANONYMOUS, "true"); } handleEntityBase(individual, ontology, tvpList); for (OWLClassExpression type : individual.getTypes(ontology)) { if (!type.isAnonymous()) { tvpList.addPair(OBOVocabulary.INSTANCE_OF, getID(type.asOWLClass())); } else { exceptions.add(new OBOStorageException(individual, type, "Complex types cannot be represented in OBO")); } } Map> objPropAssertions = individual.getObjectPropertyValues(ontology); for (OWLObjectPropertyExpression p : objPropAssertions.keySet()) { if (!p.isAnonymous()) { for (OWLIndividual ind : objPropAssertions.get(p)) { if (!ind.isAnonymous()) { final String rel = renderRestriction(new OBORelationship(p.asOWLObjectProperty(), ind.asOWLNamedIndividual())); tvpList.addPair(OBOVocabulary.PROPERTY_VALUE, rel); } } } else { exceptions.add(new OBOStorageException(individual, p, "Anonymous property in assertion is not supported in OBO")); } } Map> dataPropAssertions = individual.getDataPropertyValues(ontology); for (OWLDataPropertyExpression p : dataPropAssertions.keySet()) { if (!p.isAnonymous()) { for (OWLLiteral literal : dataPropAssertions.get(p)) { final String rel = renderPropertyAssertion(p.asOWLDataProperty(), literal); tvpList.addPair(OBOVocabulary.PROPERTY_VALUE, rel); } } else { // no anonymous data properties so this should never occur exceptions.add(new OBOStorageException(individual, p, "Anonymous property in assertion is not supported in OBO")); } } tvpList.write(writer); } private void handleEntityBase(OWLEntity entity, OWLOntology ontology, OBOTagValuePairList tvpList) { tvpList.addPair(OBOVocabulary.ID, getID(entity)); Set potentialNames = new HashSet(); for (OWLAnnotation annotation : entity.getAnnotations(ontology)) { if (annotation.getProperty().getIRI().equals(OWLRDFVocabulary.RDFS_LABEL.getIRI())) { potentialNames.add(annotation); } else if (annotation.getProperty().isComment()) { tvpList.addPair(OBOVocabulary.COMMENT, ((OWLLiteral) annotation.getValue()).getLiteral()); } else { tvpList.visit(annotation); } } if (tvpList.getValues(OBOVocabulary.NAME).isEmpty()) { // one name required!! if (!potentialNames.isEmpty()) { OWLAnnotation firstLabel = potentialNames.iterator().next(); tvpList.addPair(OBOVocabulary.NAME, ((OWLLiteral) firstLabel.getValue()).getLiteral()); potentialNames.remove(firstLabel); } else { tvpList.addPair(OBOVocabulary.NAME, getID(entity)); } } // other labels just get rendered as label for (OWLAnnotation anno : potentialNames) { tvpList.visit(anno); } final String uri = entity.getIRI().toString(); if (!uri.startsWith(defaultNamespace)) { final IRI base = IRI.create(XMLUtils.getNCNamePrefix(uri)); String prefix = nsUtil.getPrefix(base.toString()); tvpList.setDefault(OBOVocabulary.NAMESPACE, prefix); } } private String renderPropertyAssertion(OWLDataProperty property, OWLLiteral literal) { StringBuilder sb = new StringBuilder(getID(property)); sb.append(" \""); sb.append(literal.getLiteral()); sb.append("\" "); if (!literal.isRDFPlainLiteral()) { sb.append(literal.getDatatype().getIRI()); } return sb.toString(); } private String getID(OWLEntity entity) { return sfp.getShortForm(entity); } private void write(String s, Writer writer) { try { writer.write(s); } catch (IOException e) { e.printStackTrace(); } } private DateFormat getTimestampFormatter() { SimpleDateFormat sdf = new SimpleDateFormat(); sdf.applyPattern("dd:MM:yyyy HH:mm"); return sdf; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy