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

org.obolibrary.obo2owl.OWLAPIOwl2Obo Maven / Gradle / Ivy

There is a newer version: 5.5.1
Show newest version
package org.obolibrary.obo2owl;

import static org.semanticweb.owlapi.search.EntitySearcher.getAnnotationObjects;
import static org.semanticweb.owlapi.util.OWLAPIPreconditions.checkNotNull;
import static org.semanticweb.owlapi.util.OWLAPIPreconditions.emptyOptional;
import static org.semanticweb.owlapi.util.OWLAPIPreconditions.optional;
import static org.semanticweb.owlapi.util.OWLAPIPreconditions.verifyNotNull;
import static org.semanticweb.owlapi.util.OWLAPIStreamUtils.asList;
import static org.semanticweb.owlapi.util.OWLAPIStreamUtils.asSet;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;

import javax.annotation.Nullable;

import org.obolibrary.obo2owl.Obo2OWLConstants.Obo2OWLVocabulary;
import org.obolibrary.oboformat.model.Clause;
import org.obolibrary.oboformat.model.Frame;
import org.obolibrary.oboformat.model.Frame.FrameType;
import org.obolibrary.oboformat.model.OBODoc;
import org.obolibrary.oboformat.model.QualifierValue;
import org.obolibrary.oboformat.model.Xref;
import org.obolibrary.oboformat.parser.OBOFormatConstants;
import org.obolibrary.oboformat.parser.OBOFormatConstants.OboFormatTag;
import org.semanticweb.owlapi.model.AxiomType;
import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.model.OWLAnnotation;
import org.semanticweb.owlapi.model.OWLAnnotationAssertionAxiom;
import org.semanticweb.owlapi.model.OWLAnnotationProperty;
import org.semanticweb.owlapi.model.OWLAnnotationValue;
import org.semanticweb.owlapi.model.OWLAsymmetricObjectPropertyAxiom;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLAxiomVisitor;
import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLClassAssertionAxiom;
import org.semanticweb.owlapi.model.OWLClassExpression;
import org.semanticweb.owlapi.model.OWLDataFactory;
import org.semanticweb.owlapi.model.OWLDatatype;
import org.semanticweb.owlapi.model.OWLDeclarationAxiom;
import org.semanticweb.owlapi.model.OWLDisjointClassesAxiom;
import org.semanticweb.owlapi.model.OWLDisjointObjectPropertiesAxiom;
import org.semanticweb.owlapi.model.OWLEntity;
import org.semanticweb.owlapi.model.OWLEquivalentClassesAxiom;
import org.semanticweb.owlapi.model.OWLEquivalentObjectPropertiesAxiom;
import org.semanticweb.owlapi.model.OWLFunctionalObjectPropertyAxiom;
import org.semanticweb.owlapi.model.OWLInverseFunctionalObjectPropertyAxiom;
import org.semanticweb.owlapi.model.OWLInverseObjectPropertiesAxiom;
import org.semanticweb.owlapi.model.OWLLiteral;
import org.semanticweb.owlapi.model.OWLNamedIndividual;
import org.semanticweb.owlapi.model.OWLNamedObject;
import org.semanticweb.owlapi.model.OWLNaryPropertyAxiom;
import org.semanticweb.owlapi.model.OWLObject;
import org.semanticweb.owlapi.model.OWLObjectAllValuesFrom;
import org.semanticweb.owlapi.model.OWLObjectCardinalityRestriction;
import org.semanticweb.owlapi.model.OWLObjectComplementOf;
import org.semanticweb.owlapi.model.OWLObjectExactCardinality;
import org.semanticweb.owlapi.model.OWLObjectIntersectionOf;
import org.semanticweb.owlapi.model.OWLObjectMaxCardinality;
import org.semanticweb.owlapi.model.OWLObjectMinCardinality;
import org.semanticweb.owlapi.model.OWLObjectProperty;
import org.semanticweb.owlapi.model.OWLObjectPropertyDomainAxiom;
import org.semanticweb.owlapi.model.OWLObjectPropertyExpression;
import org.semanticweb.owlapi.model.OWLObjectPropertyRangeAxiom;
import org.semanticweb.owlapi.model.OWLObjectSomeValuesFrom;
import org.semanticweb.owlapi.model.OWLObjectUnionOf;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyManager;
import org.semanticweb.owlapi.model.OWLQuantifiedObjectRestriction;
import org.semanticweb.owlapi.model.OWLReflexiveObjectPropertyAxiom;
import org.semanticweb.owlapi.model.OWLRuntimeException;
import org.semanticweb.owlapi.model.OWLSubAnnotationPropertyOfAxiom;
import org.semanticweb.owlapi.model.OWLSubClassOfAxiom;
import org.semanticweb.owlapi.model.OWLSubObjectPropertyOfAxiom;
import org.semanticweb.owlapi.model.OWLSubPropertyChainOfAxiom;
import org.semanticweb.owlapi.model.OWLSymmetricObjectPropertyAxiom;
import org.semanticweb.owlapi.model.OWLTransitiveObjectPropertyAxiom;
import org.semanticweb.owlapi.vocab.Namespaces;
import org.semanticweb.owlapi.vocab.OWL2Datatype;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Sets;

/**
 * The Class OWLAPIOwl2Obo.
 */
public class OWLAPIOwl2Obo {

    /**
     * The annotation property map.
     */
    public static final Map ANNOTATIONPROPERTYMAP = initAnnotationPropertyMap();
    private static final String TOP_BOTTOM_NONTRANSLATEABLE =
        "Assertions using owl:Thing or owl:Nothing are not translateable OBO";
    /**
     * The log.
     */
    private static final Logger LOG = LoggerFactory.getLogger(OWLAPIOwl2Obo.class);
    private static final String IRI_CLASS_SYNONYMTYPEDEF =
        Obo2OWLConstants.DEFAULT_IRI_PREFIX + "IAO_synonymtypedef";
    private static final String IRI_CLASS_SUBSETDEF =
        Obo2OWLConstants.DEFAULT_IRI_PREFIX + "IAO_subsetdef";
    private static final Set SKIPPED_QUALIFIERS = Sets.newHashSet("gci_relation",
        "gci_filler", "cardinality", "minCardinality", "maxCardinality", "all_some", "all_only");
    protected final Pattern absoluteURLPattern = Pattern.compile("<\\s*http.*?>");
    protected final Set untranslatableAxioms = new HashSet<>();
    protected final Map idSpaceMap = new HashMap<>();
    protected final Set apToDeclare = new HashSet<>();
    private final OWLDataFactory df;
    protected OWLOntologyManager manager;
    protected OWLOntology owlOntology;
    protected OBODoc obodoc;
    protected String ontologyId;
    protected boolean strictConversion;
    protected boolean discardUntranslatable = false;
    /**
     * Mute untranslatable axiom warnings.
     */
    private boolean muteUntranslatableAxioms = false;

    /**
     * Instantiates a new oWLAPI owl2 obo.
     *
     * @param translationManager the translation manager
     */
    @SuppressWarnings("null")
    public OWLAPIOwl2Obo(OWLOntologyManager translationManager) {
        manager = translationManager;
        df = manager.getOWLDataFactory();
        init();
    }

    /**
     * Inits the annotation property map.
     *
     * @return the hash map
     */
    protected static Map initAnnotationPropertyMap() {
        Map map = new HashMap<>();
        for (String key : OWLAPIObo2Owl.ANNOTATIONPROPERTYMAP.keySet()) {
            IRI propIRI = OWLAPIObo2Owl.ANNOTATIONPROPERTYMAP.get(key);
            map.put(propIRI.toString(), key);
        }
        return map;
    }

    protected static boolean shorthand(@Nullable String propId) {
        return "shorthand".equals(propId);
    }

    /**
     * Adds the qualifiers.
     *
     * @param c the c
     * @param qualifiers the qualifiers
     */
    protected static void addQualifiers(Clause c, Stream qualifiers) {
        qualifiers.forEach(a -> addQualifiers(c, a));
    }

    /**
     * Adds the qualifiers.
     *
     * @param c the c
     * @param qualifier the qualifier
     */
    protected static void addQualifiers(Clause c, OWLAnnotation qualifier) {
        String prop = owlObjectToTag(qualifier.getProperty());
        if (prop == null) {
            prop = qualifier.getProperty().getIRI().toString();
        }
        if (SKIPPED_QUALIFIERS.contains(prop)) {
            return;
        }
        String value = qualifier.getValue().toString();
        if (qualifier.getValue() instanceof OWLLiteral) {
            value = ((OWLLiteral) qualifier.getValue()).getLiteral();
        } else if (qualifier.getValue().isIRI()) {
            value = getIdentifier((IRI) qualifier.getValue());
        }
        QualifierValue qv = new QualifierValue(prop, value);
        c.addQualifierValue(qv);
    }

    /**
     * E.g. http://purl.obolibrary.org/obo/go.owl to "go"
* if does not match this pattern, then retain original IRI * * @param ontology the ontology * @return The OBO ID of the ontology */ public static String getOntologyId(OWLOntology ontology) { Optional ontologyIRI = ontology.getOntologyID().getOntologyIRI(); if (!ontologyIRI.isPresent()) { return ""; } return getOntologyId(ontologyIRI.get()); } /** * Gets the ontology id. * * @param iriObj the iri obj * @return the ontology id */ public static String getOntologyId(IRI iriObj) { String iri = iriObj.toString(); String id; if (iri.startsWith("http://purl.obolibrary.org/obo/")) { id = iri.replace("http://purl.obolibrary.org/obo/", ""); if (id.endsWith(".owl")) { id = id.replaceFirst(".owl$", ""); } } else { id = iri; } return id; } /** * Gets the data version. * * @param ontology the ontology * @return the data version */ @Nullable public static String getDataVersion(OWLOntology ontology) { String oid = getOntologyId(ontology); Optional v = ontology.getOntologyID().getVersionIRI(); if (v.isPresent()) { String vs = v.get().toString().replace("http://purl.obolibrary.org/obo/", ""); vs = vs.replaceFirst(oid + '/', ""); vs = vs.replace('/' + oid + ".owl", ""); return vs; } return null; } /** * Check the entity annotations for axioms declaring it to be an obsolete entity, with * 'obsolescence reason' being 'term merge', and a non-empty 'replaced by' literal. This * corresponds to an OBO alternate identifier. Track non related annotations. * * @param annotations set of annotations for the entity @return replaced_by if it is an alt_id * @return alt id check result */ private static Optional checkForOboAltId( Collection annotations) { String replacedBy = null; boolean isMerged = false; boolean isDeprecated = false; final Set unrelatedAxioms = new HashSet<>(); for (OWLAnnotationAssertionAxiom axiom : annotations) { OWLAnnotationProperty prop = axiom.getProperty(); if (prop.isDeprecated()) { isDeprecated = true; } else if (Obo2OWLConstants.IRI_IAO_0000231.equals(prop.getIRI())) { isMerged = handleIAO227(isMerged, unrelatedAxioms, axiom); } else if (Obo2OWLVocabulary.IRI_IAO_0100001.iri.equals(prop.getIRI())) { replacedBy = handleIAO10001(replacedBy, unrelatedAxioms, axiom); } else { unrelatedAxioms.add(axiom); } } Optional result; if (replacedBy != null && isMerged && isDeprecated) { result = optional(new OboAltIdCheckResult(replacedBy, unrelatedAxioms)); } else { result = emptyOptional(); } return result; } protected static boolean handleIAO227(boolean isMerged, final Set unrelatedAxioms, OWLAnnotationAssertionAxiom axiom) { OWLAnnotationValue value = axiom.getValue(); Optional asIRI = value.asIRI(); if (asIRI.isPresent()) { return Obo2OWLConstants.IRI_IAO_0000227.equals(asIRI.get()); } unrelatedAxioms.add(axiom); return isMerged; } @Nullable protected static String handleIAO10001(@Nullable String replacedBy, final Set unrelatedAxioms, OWLAnnotationAssertionAxiom axiom) { OWLAnnotationValue value = axiom.getValue(); Optional asLiteral = value.asLiteral(); if (asLiteral.isPresent()) { return asLiteral.get().getLiteral(); } // fallback: also check for an IRI Optional asIRI = value.asIRI(); if (asIRI.isPresent()) { // translate IRI to OBO style ID return getIdentifier(asIRI.get()); } unrelatedAxioms.add(axiom); return replacedBy; } /** * Retrieve the identifier for a given {@link OWLObject}. This methods uses also shorthand hints * to resolve the identifier. Should the translation process encounter a problem or not find an * identifier the defaultValue is returned. * * @param obj the {@link OWLObject} to resolve * @param ont the target ontology * @param defaultValue the value to return in case of an error or no id * @return identifier or the default value */ public static String getIdentifierFromObject(OWLObject obj, OWLOntology ont, String defaultValue) { String id = defaultValue; try { id = getIdentifierFromObject(obj, ont); if (id == null) { id = defaultValue; } } catch (UntranslatableAxiomException e) { LOG.error(e.getMessage(), e); } return id; } /** * Retrieve the identifier for a given {@link OWLObject}. This methods uses also shorthand hints * to resolve the identifier. Should the translation process encounter an unexpected axiom an * * @param obj the {@link OWLObject} to resolve * @param ont the target ontology * @return identifier or null * @throws UntranslatableAxiomException the untranslatable axiom exception * {@link UntranslatableAxiomException} is thrown. */ @Nullable public static String getIdentifierFromObject(OWLObject obj, OWLOntology ont) throws UntranslatableAxiomException { if (obj instanceof OWLObjectProperty || obj instanceof OWLAnnotationProperty) { OWLEntity entity = (OWLEntity) obj; for (OWLAnnotationAssertionAxiom ax : asList( ont.annotationAssertionAxioms(entity.getIRI()))) { String propId = getIdentifierFromObject(ax.getProperty().getIRI(), ont); // see BFOROXrefTest // 5.9.3. Special Rules for Relations if (shorthand(propId)) { OWLAnnotationValue value = ax.getValue(); if (value instanceof OWLLiteral) { return ((OWLLiteral) value).getLiteral(); } throw new UntranslatableAxiomException( "Untranslatable axiom, expected literal value, but was: " + value + " in axiom: " + ax); } } } if (obj instanceof OWLEntity) { return getIdentifier(((OWLEntity) obj).getIRI()); } if (obj.isIRI()) { return getIdentifier((IRI) obj); } return null; } /** * See table 5.9.2. Translation of identifiers * * @param iriId the iri id * @return obo identifier */ public static String getIdentifier(IRI iriId) { String iri = iriId.toString(); // canonical IRIs String id = getId(iri); String[] s = id.split("#_"); // table 5.9.2 row 2 - NonCanonical-Prefixed-ID if (s.length > 1) { return s[0] + ':' + s[1]; } // row 3 - Unprefixed-ID s = id.split("#"); if (s.length > 1) { String prefix = ""; if ("owl".equals(s[0]) || "rdf".equals(s[0]) || "rdfs".equals(s[0])) { prefix = s[0] + ':'; } return prefix + s[1]; } // row 1 - Canonical-Prefixed-ID s = id.split("_"); if (s.length == 2 && !id.contains("#") && !s[1].contains("_")) { String localId; try { localId = URLDecoder.decode(s[1], "UTF-8"); return s[0] + ':' + localId; } catch (UnsupportedEncodingException e) { throw new OWLRuntimeException("UTF-8 not supported, JRE corrupted?", e); } } if (s.length > 2 && !id.contains("#") && s[s.length - 1].replaceAll("[0-9]", "").isEmpty()) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < s.length; i++) { if (i > 0) { if (i == s.length - 1) { sb.append(':'); } else { sb.append('_'); } } sb.append(s[i]); } return sb.toString(); } return iri; } protected static String getId(String iri) { int indexSlash = iri.lastIndexOf('/'); if (indexSlash > -1) { return iri.substring(indexSlash + 1); } return iri; } /** * Owl object to tag. * * @param obj the obj * @return the string */ @Nullable public static String owlObjectToTag(OWLObject obj) { IRI iriObj = null; if (obj instanceof OWLNamedObject) { iriObj = ((OWLNamedObject) obj).getIRI(); } else if (obj.isIRI()) { iriObj = (IRI) obj; } if (iriObj == null) { return null; } String iri = iriObj.toString(); String tag = ANNOTATIONPROPERTYMAP.get(iri); if (tag == null) { // hardcoded values for legacy annotation properties: (TEMPORARY) if (iri.startsWith(Obo2OWLConstants.DEFAULT_IRI_PREFIX + "IAO_")) { String legacyId = iri.replace(Obo2OWLConstants.DEFAULT_IRI_PREFIX, ""); if ("IAO_xref".equals(legacyId)) { return OboFormatTag.TAG_XREF.getTag(); } if ("IAO_id".equals(legacyId)) { return OboFormatTag.TAG_ID.getTag(); } if ("IAO_namespace".equals(legacyId)) { return OboFormatTag.TAG_NAMESPACE.getTag(); } } String prefix = Obo2OWLConstants.OIOVOCAB_IRI_PREFIX; if (iri.startsWith(prefix)) { tag = iri.substring(prefix.length()); } } return tag; } /** * Join clauses and its {@link QualifierValue} which have the same relationship type and target. * Try to resolve conflicts for multiple statements. E.g., min=2 and min=3 is resolved to min=2, * or max=2 and max=4 is resolved to max=4. It will not merge conflicting exact cardinality * statements. TODO How to merge "all_some", and "all_only"? * * @param clauses the clauses * @return normalized list of {@link Clause} */ public static List normalizeRelationshipClauses(List clauses) { List normalized = new ArrayList<>(); while (!clauses.isEmpty()) { Clause target = clauses.remove(0); List similar = findSimilarClauses(clauses, target); normalized.add(target); mergeSimilarIntoTarget(target, similar); } return normalized; } /** * Find similar clauses. * * @param clauses the clauses * @param target the target * @return the list */ static List findSimilarClauses(List clauses, Clause target) { String targetTag = target.getTag(); List similar = new ArrayList<>(); if (targetTag == null) { return similar; } int size = target.getValues().size(); Object targetValue = target.getValue(); Object targetValue2 = null; if (size > 1) { targetValue2 = target.getValue2(); } Iterator iterator = clauses.iterator(); while (iterator.hasNext()) { Clause current = iterator.next(); int sizeCurrent = current.getValues().size(); Object currentValue = current.getValue(); Object currentValue2 = null; if (sizeCurrent > 1) { currentValue2 = current.getValue2(); } if (targetTag.equals(current.getTag()) && targetValue.equals(currentValue) && Objects.equals(targetValue2, currentValue2)) { similar.add(current); iterator.remove(); } } return similar; } /** * Merge similar into target. * * @param target the target * @param similar the similar */ static void mergeSimilarIntoTarget(Clause target, List similar) { if (similar.isEmpty()) { return; } Collection targetQVs = target.getQualifierValues(); for (Clause current : similar) { Collection newQVs = current.getQualifierValues(); for (QualifierValue newQV : newQVs) { String newQualifier = newQV.getQualifier(); // if min or max cardinality check for possible merges if ("minCardinality".equals(newQualifier) || "maxCardinality".equals(newQualifier)) { QualifierValue match = findMatchingQualifierValue(newQV, targetQVs); if (match != null) { mergeQualifierValues(match, newQV); } else { target.addQualifierValue(newQV); } } else { target.addQualifierValue(newQV); } } } } /** * Find matching qualifier value. * * @param query the query * @param list the list * @return the qualifier value */ @Nullable static QualifierValue findMatchingQualifierValue(QualifierValue query, Collection list) { String queryQualifier = query.getQualifier(); for (QualifierValue qv : list) { if (queryQualifier.equals(qv.getQualifier())) { return qv; } } return null; } /** * Merge qualifier values. * * @param target the target * @param newQV the new qv */ static void mergeQualifierValues(QualifierValue target, QualifierValue newQV) { // do nothing, if they are equal if (!target.getValue().equals(newQV.getValue())) { if ("minCardinality".equals(target.getQualifier())) { // try to merge, parse as integers int currentValue = Integer.parseInt(target.getValue()); int newValue = Integer.parseInt(newQV.getValue()); int mergedValue = Math.min(currentValue, newValue); target.setValue(Integer.toString(mergedValue)); } else if ("maxCardinality".equals(target.getQualifier())) { // try to merge, parse as integers int currentValue = Integer.parseInt(target.getValue()); int newValue = Integer.parseInt(newQV.getValue()); int mergedValue = Math.max(currentValue, newValue); target.setValue(Integer.toString(mergedValue)); } } } protected final void init() { idSpaceMap.clear(); // legacy: idSpaceMap.put("http://www.obofoundry.org/ro/ro.owl#", "OBO_REL"); untranslatableAxioms.clear(); apToDeclare.clear(); } /** * Gets the strict conversion. * * @return the strict conversion */ public boolean getStrictConversion() { return strictConversion; } /** * Sets the strict conversion. * * @param b the new strict conversion */ public void setStrictConversion(boolean b) { strictConversion = b; } /** * Checks if is discard untranslatable. * * @return the discardUntranslatable */ public boolean isDiscardUntranslatable() { return discardUntranslatable; } /** * Sets the discard untranslatable. * * @param discardUntranslatable the discardUntranslatable to set */ public void setDiscardUntranslatable(boolean discardUntranslatable) { this.discardUntranslatable = discardUntranslatable; } /** * Gets the manager. * * @return the manager */ public OWLOntologyManager getManager() { return manager; } /** * Sets the manager. * * @param manager the new manager */ public void setManager(OWLOntologyManager manager) { this.manager = manager; } /** * Gets the obodoc. * * @return the obodoc */ public OBODoc getObodoc() { return verifyNotNull(obodoc); } /** * Sets the obodoc. * * @param obodoc the new obodoc */ public void setObodoc(OBODoc obodoc) { this.obodoc = obodoc; } /** * Convert. * * @param ont the ont * @return the oBO doc */ public OBODoc convert(OWLOntology ont) { owlOntology = ont; ontologyId = getOntologyId(ont); init(); return tr(); } protected OWLOntology getOWLOntology() { return verifyNotNull(owlOntology); } /** * Gets the untranslatable axioms. * * @return the untranslatableAxioms */ public Collection getUntranslatableAxioms() { return untranslatableAxioms; } /** * Tr. * * @return the oBO doc */ protected OBODoc tr() { setObodoc(new OBODoc()); preProcess(); tr(getOWLOntology()); // declarations need to be sorted - otherwise there is a risk of id being processed before // altId, which causes spurious clauses. accept(getOWLOntology().axioms(AxiomType.DECLARATION).sorted()); AxiomType.skipDeclarations().forEach(t -> accept(getOWLOntology().axioms(t))); if (!untranslatableAxioms.isEmpty() && !discardUntranslatable) { String axiomString = OwlStringTools.translate(untranslatableAxioms); if (!axiomString.isEmpty()) { Frame headerFrame = getObodoc().getHeaderFrame(); if (headerFrame == null) { headerFrame = new Frame(FrameType.HEADER); getObodoc().setHeaderFrame(headerFrame); } headerFrame.addClause(new Clause(OboFormatTag.TAG_OWL_AXIOMS, axiomString)); } } return getObodoc(); } private void accept(Stream axioms) { OWLAxiomVisitor visitor = new Translator(); axioms.forEach(ax -> ax.accept(visitor)); } /** * Pre process. */ protected void preProcess() { // converse of postProcess in obo2owl String viewRel = null; OWLAnnotationProperty logicalDef = manager.getOWLDataFactory().getOWLAnnotationProperty( Obo2OWLVocabulary.IRI_OIO_LogicalDefinitionViewRelation.getIRI()); List collect = asList(getOWLOntology().annotations(logicalDef)); for (OWLAnnotation ann : collect) { OWLAnnotationValue v = ann.getValue(); if (v instanceof OWLLiteral) { viewRel = ((OWLLiteral) v).getLiteral(); } else if (v.isIRI()) { viewRel = getIdentifier((IRI) v); } break; } if (viewRel == null) { return; } String view = viewRel; Set rmAxioms = new HashSet<>(); Set newAxioms = new HashSet<>(); getOWLOntology().axioms(AxiomType.EQUIVALENT_CLASSES) .forEach(eca -> preprocessEquivalents(view, rmAxioms, newAxioms, eca)); getOWLOntology().remove(rmAxioms); getOWLOntology().add(newAxioms); } protected void preprocessEquivalents(String view, Set rmAxioms, Set newAxioms, OWLEquivalentClassesAxiom eca) { AtomicInteger numNamed = new AtomicInteger(); Set xs = new HashSet<>(); eca.classExpressions().forEach(x -> { if (x instanceof OWLClass) { xs.add(x); numNamed.incrementAndGet(); } else if (x instanceof OWLObjectSomeValuesFrom) { OWLObjectProperty p = (OWLObjectProperty) ((OWLObjectSomeValuesFrom) x).getProperty(); if (!view.equals(getIdentifier(p))) { LOG.error("Expected: {} got: {} in {}", view, p, eca); } xs.add(((OWLObjectSomeValuesFrom) x).getFiller()); } else { LOG.error("Unexpected: {}", eca); } }); if (numNamed.get() == 1) { rmAxioms.add(eca); newAxioms.add(df.getOWLEquivalentClassesAxiom(xs)); } else { LOG.error("ECA did not fit expected pattern: {}", eca); } } protected void add(@Nullable Frame f) { if (f != null) { try { getObodoc().addFrame(f); } catch (Exception ex) { LOG.error(ex.getMessage(), ex); } } } /** * Tr object property. * * @param prop the prop * @param tag the tag * @param value the value * @param annotations the annotations * @return true, if successful */ protected boolean trObjectProperty(@Nullable OWLObjectProperty prop, @Nullable String tag, @Nullable String value, Stream annotations) { if (prop == null || value == null) { return false; } addQualifiers(clause(prop, tag, value), annotations); return true; } protected Clause clause(OWLObjectProperty prop, @Nullable String tag, String value) { Frame f = getTypedefFrame(prop); if (OboFormatTag.TAG_ID.getTag().equals(tag)) { Clause clause = f.getClause(tag); assert clause != null; clause.setValue(value); return clause; } else { Clause clause = new Clause(tag, value); f.addClause(clause); return clause; } } /** * Tr object property. * * @param prop the prop * @param tag the tag * @param value the value * @param annotations the annotations * @return true, if successful */ protected boolean trObjectProperty(@Nullable OWLObjectProperty prop, String tag, @Nullable Boolean value, Stream annotations) { if (prop == null || value == null) { return false; } Frame f = getTypedefFrame(prop); Clause clause = new Clause(tag); clause.addValue(value); f.addClause(clause); addQualifiers(clause, annotations); return true; } /** * Tr nary property axiom. * * @param ax the ax * @param tag the tag */ protected void trNaryPropertyAxiom(OWLNaryPropertyAxiom ax, String tag) { List set = asList(ax.properties()); if (set.size() > 1) { boolean first = true; OWLObjectProperty prop = null; String disjointFrom = null; for (OWLObjectPropertyExpression ex : set) { if (ex.isBottomEntity() || ex.isTopEntity()) { error(tag + " using Top or Bottom entities are not supported in OBO.", ax, false); return; } if (first) { first = false; if (ex.isOWLObjectProperty()) { prop = ex.asOWLObjectProperty(); } } else { disjointFrom = getIdentifier(ex); } } if (trObjectProperty(prop, tag, disjointFrom, ax.annotations())) { return; } } error(ax, true); } /** * Tr. * * @param ax the ax */ protected void tr(OWLSubPropertyChainOfAxiom ax) { OWLObjectPropertyExpression pEx = ax.getSuperProperty(); if (pEx.isAnonymous()) { error(ax, false); return; } OWLObjectProperty p = pEx.asOWLObjectProperty(); Frame f = getTypedefFrame(p); if (p.isBottomEntity() || p.isTopEntity()) { error("Property chains using Top or Bottom entities are not supported in OBO.", ax, false); return; } List list = ax.getPropertyChain(); if (list.size() != 2) { error(ax, false); return; } OWLObjectPropertyExpression exp1 = list.get(0); OWLObjectPropertyExpression exp2 = list.get(1); if (exp1.isBottomEntity() || exp1.isTopEntity() || exp2.isBottomEntity() || exp2.isTopEntity()) { error("Property chains using Top or Bottom entities are not supported in OBO.", ax, false); return; } String rel1 = getIdentifier(exp1); String rel2 = getIdentifier(exp2); if (rel1 == null || rel2 == null) { error(ax, false); return; } Clause clause; // set of unprocessed annotations List unprocessedAnnotations = asList(ax.annotations()); if (rel1.equals(f.getId())) { clause = new Clause(OboFormatTag.TAG_TRANSITIVE_OVER, rel2); } else { OboFormatTag tag = OboFormatTag.TAG_HOLDS_OVER_CHAIN; List collect = asList(ax.annotations()); for (OWLAnnotation ann : collect) { if (OWLAPIObo2Owl.IRI_PROP_ISREVERSIBLEPROPERTYCHAIN .equals(ann.getProperty().getIRI().toString())) { tag = OboFormatTag.TAG_EQUIVALENT_TO_CHAIN; // remove annotation from unprocessed set. unprocessedAnnotations.remove(ann); break; } } clause = new Clause(tag); clause.addValue(rel1); clause.addValue(rel2); } f.addClause(clause); addQualifiers(clause, unprocessedAnnotations.stream()); } /** * Tr. * * @param ax the ax */ protected void tr(OWLEquivalentObjectPropertiesAxiom ax) { trNaryPropertyAxiom(ax, OboFormatTag.TAG_EQUIVALENT_TO.getTag()); } /** * Tr. * * @param ax the ax */ protected void tr(OWLTransitiveObjectPropertyAxiom ax) { OWLObjectPropertyExpression prop = ax.getProperty(); if (prop.isOWLObjectProperty() && trObjectProperty(prop.asOWLObjectProperty(), OboFormatTag.TAG_IS_TRANSITIVE.getTag(), Boolean.TRUE, ax.annotations())) { return; } error(ax, true); } /** * Tr. * * @param ax the ax */ protected void tr(OWLDisjointObjectPropertiesAxiom ax) { trNaryPropertyAxiom(ax, OboFormatTag.TAG_DISJOINT_FROM.getTag()); } /** * Tr. * * @param ax the ax */ protected void tr(OWLReflexiveObjectPropertyAxiom ax) { OWLObjectPropertyExpression prop = ax.getProperty(); if (prop.isOWLObjectProperty() && trObjectProperty(prop.asOWLObjectProperty(), OboFormatTag.TAG_IS_REFLEXIVE.getTag(), Boolean.TRUE, ax.annotations())) { return; } error(ax, true); } /** * Tr. * * @param ax the ax */ protected void tr(OWLInverseFunctionalObjectPropertyAxiom ax) { OWLObjectPropertyExpression prop = ax.getProperty(); if (prop.isOWLObjectProperty() && trObjectProperty(prop.asOWLObjectProperty(), OboFormatTag.TAG_IS_INVERSE_FUNCTIONAL.getTag(), Boolean.TRUE, ax.annotations())) { return; } error(ax, true); } /** * Tr. * * @param ax the ax */ protected void tr(OWLInverseObjectPropertiesAxiom ax) { OWLObjectPropertyExpression prop1 = ax.getFirstProperty(); OWLObjectPropertyExpression prop2 = ax.getSecondProperty(); if (prop1.isOWLObjectProperty() && prop2.isOWLObjectProperty() && trObjectProperty(prop1.asOWLObjectProperty(), OboFormatTag.TAG_INVERSE_OF.getTag(), getIdentifier(prop2), ax.annotations())) { return; } error(ax, true); } /** * Tr. * * @param ax the ax */ protected void tr(OWLObjectPropertyDomainAxiom ax) { OWLClassExpression domain = ax.getDomain(); OWLObjectPropertyExpression propEx = ax.getProperty(); if (propEx.isAnonymous()) { error(ax, true); return; } OWLObjectProperty prop = propEx.asOWLObjectProperty(); if (domain.isBottomEntity() || domain.isTopEntity()) { // at least get the type def frame getTypedefFrame(prop); // now throw the error error("domains using top or bottom entities are not translatable to OBO.", ax, false); return; } String range = getIdentifier(domain); if (range != null) { if (trObjectProperty(prop, OboFormatTag.TAG_DOMAIN.getTag(), range, ax.annotations())) { return; } else { error("trObjectProperty failed for " + prop, ax, true); } } else { error("no range translatable for " + ax, false); } } /** * Tr. * * @param ax the ax */ protected void tr(OWLAsymmetricObjectPropertyAxiom ax) { OWLObjectPropertyExpression prop = ax.getProperty(); if (prop.isOWLObjectProperty() && trObjectProperty(prop.asOWLObjectProperty(), OboFormatTag.TAG_IS_ASYMMETRIC.getTag(), Boolean.TRUE, ax.annotations())) { return; } error(ax, true); } /** * Tr. * * @param ax the ax */ protected void tr(OWLSymmetricObjectPropertyAxiom ax) { OWLObjectPropertyExpression prop = ax.getProperty(); if (prop.isOWLObjectProperty() && trObjectProperty(prop.asOWLObjectProperty(), OboFormatTag.TAG_IS_SYMMETRIC.getTag(), Boolean.TRUE, ax.annotations())) { return; } error(ax, true); } /** * Tr. * * @param ax the ax */ protected void tr(OWLFunctionalObjectPropertyAxiom ax) { OWLObjectPropertyExpression prop = ax.getProperty(); if (prop.isOWLObjectProperty() && trObjectProperty(prop.asOWLObjectProperty(), OboFormatTag.TAG_IS_FUNCTIONAL.getTag(), Boolean.TRUE, ax.annotations())) { return; } error(ax, true); } /** * Tr. * * @param ax the ax */ protected void tr(OWLObjectPropertyRangeAxiom ax) { OWLClassExpression owlRange = ax.getRange(); OWLObjectPropertyExpression propEx = ax.getProperty(); if (propEx.isAnonymous()) { error(ax, false); } OWLObjectProperty prop = propEx.asOWLObjectProperty(); if (owlRange.isBottomEntity() || owlRange.isTopEntity()) { // at least create the property frame getTypedefFrame(prop); // error message error("ranges using top or bottom entities are not translatable to OBO.", ax, false); return; } String range = getIdentifier(owlRange); if (range != null && trObjectProperty(prop, OboFormatTag.TAG_RANGE.getTag(), range, ax.annotations())) { return; } error(ax, false); } protected void tr(OWLSubObjectPropertyOfAxiom ax) { OWLObjectPropertyExpression sup = ax.getSuperProperty(); OWLObjectPropertyExpression sub = ax.getSubProperty(); if (sub.isBottomEntity() || sub.isTopEntity() || sup.isBottomEntity() || sup.isTopEntity()) { error("SubProperties using Top or Bottom entites are not supported in OBO.", false); return; } if (sub.isOWLObjectProperty() && sup.isOWLObjectProperty()) { String supId = checkNotNull(getIdentifier(sup)); if (supId.startsWith("owl:")) { return; } Frame f = getTypedefFrame((OWLObjectProperty) sub); Clause clause = new Clause(OboFormatTag.TAG_IS_A, supId); f.addClause(clause); addQualifiers(clause, ax.annotations()); } else { error(ax, true); } } protected void tr(OWLSubAnnotationPropertyOfAxiom ax) { OWLAnnotationProperty sup = ax.getSuperProperty(); OWLAnnotationProperty sub = ax.getSubProperty(); if (sub.isBottomEntity() || sub.isTopEntity() || sup.isBottomEntity() || sup.isTopEntity()) { error("SubAnnotationProperties using Top or Bottom entites are not supported in OBO.", false); return; } String tagObject = owlObjectToTag(sup); if (OboFormatTag.TAG_SYNONYMTYPEDEF.getTag().equals(tagObject)) { String name = ""; String scope = null; for (OWLAnnotationAssertionAxiom axiom : asList( getOWLOntology().annotationAssertionAxioms(sub.getIRI()))) { String tg = owlObjectToTag(axiom.getProperty()); if (OboFormatTag.TAG_NAME.getTag().equals(tg)) { name = ((OWLLiteral) axiom.getValue()).getLiteral(); } else if (OboFormatTag.TAG_SCOPE.getTag().equals(tg)) { scope = owlObjectToTag(axiom.getValue()); } } Frame hf = checkNotNull(getObodoc().getHeaderFrame()); assert hf != null; Clause clause = new Clause(OboFormatTag.TAG_SYNONYMTYPEDEF); clause.addValue(getIdentifier(sub)); clause.addValue(name); if (scope != null) { clause.addValue(scope); } addQualifiers(clause, ax.annotations()); if (!hf.getClauses().contains(clause)) { hf.addClause(clause); } else { LOG.error("duplicate clause: {} in header", clause); } return; } else if (OboFormatTag.TAG_SUBSETDEF.getTag().equals(tagObject)) { String comment = ""; for (OWLAnnotationAssertionAxiom axiom : asList( getOWLOntology().annotationAssertionAxioms(sub.getIRI()))) { String tg = owlObjectToTag(axiom.getProperty()); if (OboFormatTag.TAG_COMMENT.getTag().equals(tg)) { comment = ((OWLLiteral) axiom.getValue()).getLiteral(); break; } } Frame hf = checkNotNull(getObodoc().getHeaderFrame()); assert hf != null; Clause clause = new Clause(OboFormatTag.TAG_SUBSETDEF); clause.addValue(getIdentifier(sub)); clause.addValue(comment); if (!hf.getClauses().contains(clause)) { hf.addClause(clause); } else { LOG.error("duplicate clause: {} in header", clause); } addQualifiers(clause, ax.annotations()); return; } if (sub.isOWLObjectProperty() && sup.isOWLObjectProperty()) { String supId = getIdentifier(sup); if (supId == null || supId.startsWith("owl:")) { return; } Frame f = getTypedefFrame(sub); Clause clause = new Clause(OboFormatTag.TAG_IS_A, supId); f.addClause(clause); addQualifiers(clause, ax.annotations()); } else { error(ax, true); } } /** * Tr. * * @param aanAx the aan ax * @param frame the frame */ protected void tr(OWLAnnotationAssertionAxiom aanAx, Frame frame) { boolean success = tr(aanAx.getProperty(), aanAx.getValue(), asList(aanAx.annotations()), frame); if (!success) { untranslatableAxioms.add(aanAx); } } /** * Tr. * * @param prop the prop * @param annVal the ann val * @param qualifiers the qualifiers * @param frame the frame * @return true, if successful */ protected boolean tr(OWLAnnotationProperty prop, OWLAnnotationValue annVal, Collection qualifiers, Frame frame) { String tagString = owlObjectToTag(prop); OboFormatTag tag = null; if (tagString != null) { tag = OBOFormatConstants.getTag(tagString); } if (tag == null) { if (annVal.isIRI() && FrameType.TERM.equals(frame.getType()) && isMetadataTag(prop)) { String propId = this.getIdentifier(prop); if (propId != null) { Clause clause = new Clause(OboFormatTag.TAG_RELATIONSHIP); clause.addValue(propId); clause.addValue(getIdentifier((IRI) annVal)); addQualifiers(clause, qualifiers.stream()); frame.addClause(clause); return true; } } // annotation property does not correspond to a mapping to a tag in // the OBO syntax - // use the property_value tag return trGenericPropertyValue(prop, annVal, qualifiers.stream(), frame); } Object value = getValue(annVal, tagString); if (!value.toString().trim().isEmpty()) { if (tag == OboFormatTag.TAG_ID) { if (!value.equals(frame.getId())) { warn("Conflicting id definitions: 1) " + frame.getId() + " 2)" + value); return false; } return true; } Clause clause = new Clause(tag); if (tag == OboFormatTag.TAG_DATE) { try { clause.addValue( OBOFormatConstants.headerDateFormat().parseObject(value.toString())); } catch (@SuppressWarnings("unused") ParseException e) { error("Could not parse date string: " + value, true); return false; } } else { clause.addValue(value); } Set unprocessedQualifiers = new HashSet<>(qualifiers); if (tag == OboFormatTag.TAG_DEF) { for (OWLAnnotation aan : qualifiers) { String propId = owlObjectToTag(aan.getProperty()); if ("xref".equals(propId)) { OWLAnnotationValue v = aan.getValue(); String xrefValue; if (v.isIRI()) { xrefValue = v.toString(); } else { xrefValue = ((OWLLiteral) v).getLiteral(); } Xref xref = new Xref(xrefValue); clause.addXref(xref); unprocessedQualifiers.remove(aan); } } } else if (tag == OboFormatTag.TAG_XREF) { Xref xref = new Xref(value.toString()); for (OWLAnnotation annotation : qualifiers) { if (df.getRDFSLabel().equals(annotation.getProperty())) { OWLAnnotationValue owlAnnotationValue = annotation.getValue(); if (owlAnnotationValue instanceof OWLLiteral) { unprocessedQualifiers.remove(annotation); String xrefAnnotation = ((OWLLiteral) owlAnnotationValue).getLiteral(); xrefAnnotation = xrefAnnotation.trim(); if (!xrefAnnotation.isEmpty()) { xref.setAnnotation(xrefAnnotation); } } } } clause.setValue(xref); } else if (tag == OboFormatTag.TAG_EXACT || tag == OboFormatTag.TAG_NARROW || tag == OboFormatTag.TAG_BROAD || tag == OboFormatTag.TAG_RELATED) { handleSynonym(qualifiers, tag.getTag(), clause, unprocessedQualifiers); } else if (tag == OboFormatTag.TAG_SYNONYM) { // This should never happen. // All synonyms need to be qualified with a type. String synonymType = null; handleSynonym(qualifiers, synonymType, clause, unprocessedQualifiers); } addQualifiers(clause, unprocessedQualifiers.stream()); // before adding the clause check for redundant clauses boolean redundant = false; for (Clause frameClause : frame.getClauses()) { if (clause.equals(frameClause)) { redundant = handleDuplicateClause(frame, frameClause); } } if (!redundant) { frame.addClause(clause); } } else { return false; } return true; } private boolean isMetadataTag(OWLAnnotationProperty p) { final IRI metadataTagIRI = IRI.create(Obo2OWLConstants.OIOVOCAB_IRI_PREFIX, OboFormatTag.TAG_IS_METADATA_TAG.getTag()); return owlOntology.annotationAssertionAxioms(p.getIRI()) .anyMatch(ax -> metadataTagIRI.equals(ax.getProperty().getIRI())); } /** * Handle synonym. * * @param qualifiers the qualifiers * @param scope the scope * @param clause the clause * @param unprocessedQualifiers the unprocessed qualifiers */ protected void handleSynonym(Collection qualifiers, @Nullable String scope, Clause clause, Set unprocessedQualifiers) { clause.setTag(OboFormatTag.TAG_SYNONYM.getTag()); String type = null; clause.setXrefs(new ArrayList()); for (OWLAnnotation aan : qualifiers) { String propId = owlObjectToTag(aan.getProperty()); if (OboFormatTag.TAG_XREF.getTag().equals(propId)) { OWLAnnotationValue v = aan.getValue(); String xrefValue; if (v.isIRI()) { xrefValue = v.toString(); } else { xrefValue = ((OWLLiteral) v).getLiteral(); } Xref xref = new Xref(xrefValue); clause.addXref(xref); unprocessedQualifiers.remove(aan); } else if (OboFormatTag.TAG_HAS_SYNONYM_TYPE.getTag().equals(propId)) { type = getIdentifier(aan.getValue()); unprocessedQualifiers.remove(aan); } } if (scope != null) { clause.addValue(scope); if (type != null) { clause.addValue(type); } } } /** * Handle a duplicate clause in a frame during translation. * * @param frame the frame * @param clause the clause * @return true if the clause is to be marked as redundant and will not be added to the */ protected boolean handleDuplicateClause(Frame frame, Clause clause) { // default is to report it via the logger and remove it. LOG.error("Duplicate clause '{}' generated in frame: {}", clause, frame.getId()); return true; } /** * Tr generic property value. * * @param prop the prop * @param annVal the ann val * @param qualifiers the qualifiers * @param frame the frame * @return true, if successful */ protected boolean trGenericPropertyValue(OWLAnnotationProperty prop, OWLAnnotationValue annVal, Stream qualifiers, Frame frame) { // no built-in obo tag for this: use the generic property_value tag Clause clause = new Clause(OboFormatTag.TAG_PROPERTY_VALUE.getTag()); String propId = getIdentifier(prop); addQualifiers(clause, qualifiers); if (!shorthand(propId)) { clause.addValue(propId); if (annVal instanceof OWLLiteral) { OWLLiteral owlLiteral = (OWLLiteral) annVal; clause.addValue(owlLiteral.getLiteral()); OWLDatatype datatype = owlLiteral.getDatatype(); IRI dataTypeIri = datatype.getIRI(); if (!OWL2Datatype.isBuiltIn(dataTypeIri)) { error("Untranslatable axiom due to unknown data type: " + annVal, true); return false; } if (Namespaces.XSD.inNamespace(dataTypeIri)) { clause.addValue(dataTypeIri.prefixedBy("xsd:")); } else if (dataTypeIri.isPlainLiteral()) { clause.addValue("xsd:string"); } else { clause.addValue(dataTypeIri.toString()); } } else if (annVal.isIRI()) { clause.addValue(getIdentifier((IRI) annVal)); } frame.addClause(clause); } return true; } /** * Gets the value. * * @param annVal the ann val * @param tag the tag * @return the value */ protected Object getValue(OWLAnnotationValue annVal, @Nullable String tag) { Object value = annVal.toString(); if (annVal instanceof OWLLiteral) { OWLLiteral l = (OWLLiteral) annVal; value = l.isBoolean() ? Boolean.valueOf(l.parseBoolean()) : l.getLiteral(); } else if (annVal.isIRI()) { value = getIdentifier((IRI) annVal); } if (OboFormatTag.TAG_EXPAND_EXPRESSION_TO.getTag().equals(tag)) { String s = value.toString(); Matcher matcher = absoluteURLPattern.matcher(s); while (matcher.find()) { String m = matcher.group(); m = m.replace("<", ""); m = m.replace(">", ""); int i = m.lastIndexOf('/'); m = m.substring(i + 1); s = s.replace(matcher.group(), m); } value = s; } return value; } /** * Tr. * * @param ontology the ontology */ protected void tr(OWLOntology ontology) { Frame f = new Frame(FrameType.HEADER); getObodoc().setHeaderFrame(f); ontology.directImportsDocuments().forEach(iri -> f .addClause(new Clause(OboFormatTag.TAG_IMPORT.getTag()).withValue(iri.toString()))); String id = getOntologyId(ontology); Clause c = new Clause(OboFormatTag.TAG_ONTOLOGY.getTag()); c.setValue(id); f.addClause(c); String vid = getDataVersion(ontology); if (vid != null) { Clause c2 = new Clause(OboFormatTag.TAG_DATA_VERSION.getTag()); c2.setValue(vid); f.addClause(c2); } List collect = asList(ontology.annotations()); for (OWLAnnotation ann : collect) { OWLAnnotationProperty property = ann.getProperty(); String tagString = owlObjectToTag(property); if (OboFormatTag.TAG_COMMENT.getTag().equals(tagString)) { property = df.getOWLAnnotationProperty( OWLAPIObo2Owl.trTagToIRI(OboFormatTag.TAG_REMARK.getTag())); } tr(property, ann.getValue(), asList(ann.annotations()), f); } } /** * Tr. * * @param ax the ax */ protected void tr(OWLEquivalentClassesAxiom ax) { /* * Assumption: the underlying data structure is a set The order is not guaranteed to be * preserved. */ // handle expression list with size other than two elements as error if (ax.classExpressions().count() != 2) { error(ax, false); return; } Iterator it = ax.classExpressions().iterator(); OWLClassExpression ce1 = it.next(); OWLClassExpression ce2 = it.next(); if (ce1.isBottomEntity() || ce1.isTopEntity() || ce2.isBottomEntity() || ce2.isTopEntity()) { error( "Equivalent classes axioms using Top or Bottom entities are not supported in OBO.", ax, false); return; } if (!(ce1 instanceof OWLClass)) { // check whether ce2 is the actual OWLEntity if (ce2 instanceof OWLClass) { // three way exchange OWLClassExpression temp = ce2; ce2 = ce1; ce1 = temp; } else { // this might happen for some GCI axioms, which are not // expressible in OBO error("GCI axioms are not expressible in OBO.", ax, false); return; } } Frame f = getTermFrame(ce1.asOWLClass()); boolean isUntranslateable = false; List equivalenceAxiomClauses = new ArrayList<>(); String cls2 = getIdentifier(ce2); if (cls2 != null) { Clause c = new Clause(OboFormatTag.TAG_EQUIVALENT_TO.getTag()); c.setValue(cls2); f.addClause(c); addQualifiers(c, ax.annotations()); } else if (ce2 instanceof OWLObjectUnionOf) { List list2 = ((OWLObjectUnionOf) ce2).getOperandsAsList(); for (OWLClassExpression oce : list2) { String id = getIdentifier(oce); if (id == null) { error(ax, true); return; } Clause c = new Clause(OboFormatTag.TAG_UNION_OF.getTag()); c.setValue(id); equivalenceAxiomClauses.add(c); addQualifiers(c, ax.annotations()); } } else if (ce2 instanceof OWLObjectIntersectionOf) { List list2 = ((OWLObjectIntersectionOf) ce2).getOperandsAsList(); for (OWLClassExpression ce : list2) { String r = null; cls2 = getIdentifier(ce); Integer exact = null; // cardinality Integer min = null; // minCardinality Integer max = null; // maxCardinality Boolean allSome = null; // all_some Boolean allOnly = null; // all_only if (ce instanceof OWLObjectSomeValuesFrom) { OWLObjectSomeValuesFrom ristriction = (OWLObjectSomeValuesFrom) ce; r = getIdentifier(ristriction.getProperty()); cls2 = getIdentifier(ristriction.getFiller()); } else if (ce instanceof OWLObjectExactCardinality) { OWLObjectExactCardinality card = (OWLObjectExactCardinality) ce; r = getIdentifier(card.getProperty()); cls2 = getIdentifier(card.getFiller()); exact = Integer.valueOf(card.getCardinality()); } else if (ce instanceof OWLObjectMinCardinality) { OWLObjectMinCardinality card = (OWLObjectMinCardinality) ce; r = getIdentifier(card.getProperty()); cls2 = getIdentifier(card.getFiller()); min = Integer.valueOf(card.getCardinality()); } else if (ce instanceof OWLObjectMaxCardinality) { OWLObjectMaxCardinality card = (OWLObjectMaxCardinality) ce; r = getIdentifier(card.getProperty()); cls2 = getIdentifier(card.getFiller()); max = Integer.valueOf(card.getCardinality()); } else if (ce instanceof OWLObjectAllValuesFrom) { OWLObjectAllValuesFrom all = (OWLObjectAllValuesFrom) ce; OWLClassExpression filler = all.getFiller(); if (filler instanceof OWLClass) { r = getIdentifier(all.getProperty()); cls2 = getIdentifier(filler); allOnly = Boolean.TRUE; } else if (filler instanceof OWLObjectComplementOf) { OWLObjectComplementOf restriction = (OWLObjectComplementOf) filler; r = getIdentifier(all.getProperty()); cls2 = getIdentifier(restriction.getOperand()); exact = Integer.valueOf(0); } } else if (ce instanceof OWLObjectIntersectionOf) { // either a min-max or a some-all combination Set operands = asSet(((OWLObjectIntersectionOf) ce).operands(), OWLClassExpression.class); if (operands.size() == 2) { for (OWLClassExpression operand : operands) { if (operand instanceof OWLObjectMinCardinality) { OWLObjectMinCardinality card = (OWLObjectMinCardinality) operand; r = getIdentifier(card.getProperty()); cls2 = getIdentifier(card.getFiller()); min = Integer.valueOf(card.getCardinality()); } else if (operand instanceof OWLObjectMaxCardinality) { OWLObjectMaxCardinality card = (OWLObjectMaxCardinality) operand; r = getIdentifier(card.getProperty()); cls2 = getIdentifier(card.getFiller()); max = Integer.valueOf(card.getCardinality()); } else if (operand instanceof OWLObjectAllValuesFrom) { OWLObjectAllValuesFrom all = (OWLObjectAllValuesFrom) operand; r = getIdentifier(all.getProperty()); cls2 = getIdentifier(all.getFiller()); allOnly = Boolean.TRUE; } else if (operand instanceof OWLObjectSomeValuesFrom) { OWLObjectSomeValuesFrom all = (OWLObjectSomeValuesFrom) operand; r = getIdentifier(all.getProperty()); cls2 = getIdentifier(all.getFiller()); allSome = Boolean.TRUE; } } } } if (cls2 != null) { Clause c = new Clause(OboFormatTag.TAG_INTERSECTION_OF.getTag()); if (r != null) { c.addValue(r); } c.addValue(cls2); equivalenceAxiomClauses.add(c); if (exact != null) { String string = exact.toString(); c.addQualifierValue(new QualifierValue("cardinality", string)); } if (min != null) { String string = min.toString(); c.addQualifierValue(new QualifierValue("minCardinality", string)); } if (max != null) { String string = max.toString(); c.addQualifierValue(new QualifierValue("maxCardinality", string)); } if (allSome != null) { String string = allSome.toString(); c.addQualifierValue(new QualifierValue("all_some", string)); } if (allOnly != null) { String string = allOnly.toString(); c.addQualifierValue(new QualifierValue("all_only", string)); } addQualifiers(c, ax.annotations()); } else if (!f.getClauses(OboFormatTag.TAG_INTERSECTION_OF).isEmpty()) { error( "The axiom is not translated (maximimum one IntersectionOf EquivalenceAxiom)", ax, false); } else { isUntranslateable = true; error(ax, false); } } } else { isUntranslateable = true; error(ax, false); } // Only add clauses if the *entire* equivalence axiom can be translated if (!isUntranslateable) { equivalenceAxiomClauses.forEach(f::addClause); } } /** * Tr. * * @param ax the ax */ protected void tr(OWLDisjointClassesAxiom ax) { // use set, the OWL-API does not provide an order if (ax.classExpressions().count() != 2) { error("Expected two classes in a disjoin classes axiom.", ax, false); } Iterator it = ax.classExpressions().iterator(); OWLClassExpression ce1 = it.next(); OWLClassExpression ce2 = it.next(); if (ce1.isBottomEntity() || ce1.isTopEntity() || ce2.isBottomEntity() || ce2.isTopEntity()) { error("Disjoint classes axiom using Top or Bottom entities are not supported.", ax, false); } String cls2 = getIdentifier(ce2); if (cls2 == null) { error(ax, true); return; } if (ce1.isAnonymous()) { error(ax, false); return; } OWLClass cls1 = ce1.asOWLClass(); Frame f = getTermFrame(cls1); Clause c = new Clause(OboFormatTag.TAG_DISJOINT_FROM.getTag()); c.setValue(cls2); f.addClause(c); addQualifiers(c, ax.annotations()); } /** * Tr. * * @param axiom the axiom */ protected void tr(OWLDeclarationAxiom axiom) { OWLEntity entity = axiom.getEntity(); if (entity.isBottomEntity() || entity.isTopEntity()) { return; } List set = asList(owlOntology.annotationAssertionAxioms(entity.getIRI())); if (set.isEmpty()) { return; } boolean isClass = entity.isOWLClass(); boolean isObjectProperty = entity.isOWLObjectProperty(); boolean isAnnotationProperty = entity.isOWLAnnotationProperty(); // check whether the entity is an alt_id Optional altIdOptional = checkForOboAltId(set); if (altIdOptional.isPresent()) { // the entity will not be translated // instead create the appropriate alt_id in the replaced_by frame String currentId = getIdentifier(entity.getIRI()); addAltId(altIdOptional.get().replacedBy, currentId, isClass, isObjectProperty); // add unrelated annotations to untranslatableAxioms axioms untranslatableAxioms.addAll(altIdOptional.get().unrelated); return; } // translate Frame f = null; if (isClass) { f = getTermFrame(entity.asOWLClass()); } else if (isObjectProperty) { f = getTypedefFrame(entity.asOWLObjectProperty()); } else if (isAnnotationProperty) { for (OWLAnnotationAssertionAxiom ax : set) { OWLAnnotationProperty prop = ax.getProperty(); String tag = owlObjectToTag(prop); if (OboFormatTag.TAG_IS_METADATA_TAG.getTag().equals(tag)) { f = getTypedefFrame(entity); break; } } } if (f != null) { Frame f1 = f; set.forEach(a -> tr(a, f1)); add(f); } } private void addAltId(String replacedBy, String altId, boolean isClass, boolean isProperty) { Frame replacedByFrame = null; if (isClass) { replacedByFrame = getTermFrame(replacedBy); } else if (isProperty) { replacedByFrame = getTypedefFrame(replacedBy); } if (replacedByFrame != null) { boolean addClause = true; // check existing alt_ids to avoid duplicate clauses Collection existing = replacedByFrame.getClauses(OboFormatTag.TAG_ALT_ID); for (Clause clause : existing) { if (altId.equals(clause.getValue(String.class))) { addClause = false; } } if (addClause) { replacedByFrame.addClause(new Clause(OboFormatTag.TAG_ALT_ID, altId)); } } } /** * Gets the identifier. * * @param obj the obj * @return the identifier */ @Nullable public String getIdentifier(OWLObject obj) { try { return getIdentifierFromObject(obj, getOWLOntology()); } catch (UntranslatableAxiomException e) { error(e.getMessage(), true); } return null; } /** * @return true if untranslatable axioms should not be logged */ public boolean isMuteUntranslatableAxioms() { return muteUntranslatableAxioms; } /** * @param muteUntranslatableAxioms true disables logging */ public void setMuteUntranslatableAxioms(boolean muteUntranslatableAxioms) { this.muteUntranslatableAxioms = muteUntranslatableAxioms; } /** * Gets the term frame. * * @param entity the entity * @return the term frame */ protected Frame getTermFrame(OWLClass entity) { String id = getIdentifier(entity.getIRI()); return getTermFrame(id); } private Frame getTermFrame(String id) { Frame f = getObodoc().getTermFrame(id); if (f == null) { f = new Frame(FrameType.TERM); f.setId(id); f.addClause(new Clause(OboFormatTag.TAG_ID, id)); add(f); } return f; } /** * Gets the typedef frame. * * @param entity the entity * @return the typedef frame */ protected Frame getTypedefFrame(OWLEntity entity) { String id = getIdentifier(entity); return getTypedefFrame(checkNotNull(id)); } private Frame getTypedefFrame(String id) { Frame f = getObodoc().getTypedefFrame(id); if (f == null) { f = new Frame(FrameType.TYPEDEF); f.setId(id); f.addClause(new Clause(OboFormatTag.TAG_ID, id)); add(f); } return f; } /** * Tr. * * @param ax the ax */ protected void tr(OWLClassAssertionAxiom ax) { OWLObject cls = ax.getClassExpression(); if (!(cls instanceof OWLClass)) { return; } String clsIRI = ((OWLClass) cls).getIRI().toString(); Frame f = checkNotNull(getObodoc().getHeaderFrame()); assert f != null; if (IRI_CLASS_SYNONYMTYPEDEF.equals(clsIRI)) { Clause c = new Clause(OboFormatTag.TAG_SYNONYMTYPEDEF.getTag()); OWLNamedIndividual indv = (OWLNamedIndividual) ax.getIndividual(); String indvId = checkNotNull(getIdentifier(indv)); // TODO: full specify this in the spec document. // we may want to allow full IDs for subsets in future. // here we would have a convention that an unprefixed // subsetdef/synonymtypedef // gets placed in a temp ID space, and only this id space is // stripped indvId = indvId.replaceFirst(".*:", ""); c.addValue(indvId); c.addValue(indvId); String nameValue = ""; String scopeValue = null; Optional a = getAnnotationObjects(indv, getOWLOntology(), df.getRDFSLabel()).findFirst(); if (a.isPresent()) { nameValue = '"' + a.get().getValue().asLiteral().get().getLiteral() + '"'; } a = getAnnotationObjects(indv, getOWLOntology()) .filter(ann -> !ann.getProperty().equals(df.getRDFSLabel())).findFirst(); if (a.isPresent()) { scopeValue = a.get().getValue().asLiteral().get().getLiteral(); } c.addValue(nameValue); if (scopeValue != null && !scopeValue.isEmpty()) { c.addValue(scopeValue); } f.addClause(c); } else if (IRI_CLASS_SUBSETDEF.equals(clsIRI)) { Clause c = new Clause(OboFormatTag.TAG_SUBSETDEF.getTag()); OWLNamedIndividual indv = (OWLNamedIndividual) ax.getIndividual(); String indvId = checkNotNull(getIdentifier(indv)); // TODO: full specify this in the spec document. // we may want to allow full IDs for subsets in future. // here we would have a convention that an unprefixed // subsetdef/synonymtypedef // gets placed in a temp ID space, and only this id space is // stripped indvId = indvId.replaceFirst(".*:", ""); c.addValue(indvId); String nameValue = ""; Optional value = getAnnotationObjects(indv, getOWLOntology(), df.getRDFSLabel()).findFirst(); if (value.isPresent()) { nameValue = '"' + value.get().getValue().asLiteral().get().getLiteral() + '"'; } c.addValue(nameValue); f.addClause(c); } else { // TODO: individual } } /** * Tr. * * @param ax the ax */ protected void tr(OWLSubClassOfAxiom ax) { OWLClassExpression sub = ax.getSubClass(); OWLClassExpression sup = ax.getSuperClass(); Set qvs = new HashSet<>(); if (sub.isOWLNothing() || sub.isTopEntity() || sup.isTopEntity() || sup.isOWLNothing()) { error(TOP_BOTTOM_NONTRANSLATEABLE, ax, false); return; } // 5.2.2 if (sub instanceof OWLObjectIntersectionOf) { Set xs = asSet(((OWLObjectIntersectionOf) sub).operands(), OWLClassExpression.class); // obo-format is limited to very restricted GCIs - the LHS of the // axiom // must correspond to ObjectIntersectionOf(cls // ObjectSomeValuesFrom(p filler)) if (xs.size() == 2) { OWLClass c = null; OWLObjectProperty p = null; OWLClass filler = null; for (OWLClassExpression x : xs) { if (x instanceof OWLClass) { c = (OWLClass) x; } if (x instanceof OWLObjectSomeValuesFrom) { OWLObjectSomeValuesFrom r = (OWLObjectSomeValuesFrom) x; if (r.getProperty().isOWLObjectProperty() && r.getFiller() instanceof OWLClass) { p = r.getProperty().asOWLObjectProperty(); filler = (OWLClass) r.getFiller(); } } } if (c != null && p != null && filler != null) { sub = c; qvs.add(new QualifierValue("gci_relation", checkNotNull(getIdentifier(p)))); qvs.add(new QualifierValue("gci_filler", checkNotNull(getIdentifier(filler)))); } } } if (sub instanceof OWLClass) { Frame f = getTermFrame((OWLClass) sub); if (sup instanceof OWLClass) { Clause c = new Clause(OboFormatTag.TAG_IS_A.getTag()); c.setValue(checkNotNull(getIdentifier(sup))); c.setQualifierValues(qvs); f.addClause(c); addQualifiers(c, ax.annotations()); } else if (sup instanceof OWLObjectCardinalityRestriction) { // OWLObjectExactCardinality // OWLObjectMinCardinality // OWLObjectMaxCardinality OWLObjectCardinalityRestriction cardinality = (OWLObjectCardinalityRestriction) sup; OWLClassExpression filler = cardinality.getFiller(); if (filler.isBottomEntity() || filler.isTopEntity()) { error(TOP_BOTTOM_NONTRANSLATEABLE, ax, false); return; } String fillerId = getIdentifier(filler); if (fillerId == null) { error(ax, true); return; } f.addClause( createRelationshipClauseWithCardinality(cardinality, fillerId, qvs, ax)); } else if (sup instanceof OWLQuantifiedObjectRestriction) { // OWLObjectSomeValuesFrom // OWLObjectAllValuesFrom OWLQuantifiedObjectRestriction r = (OWLQuantifiedObjectRestriction) sup; OWLClassExpression filler = r.getFiller(); if (filler.isBottomEntity() || filler.isTopEntity()) { error(TOP_BOTTOM_NONTRANSLATEABLE, ax, false); return; } String fillerId = getIdentifier(filler); if (fillerId == null) { error(ax, true); return; } if (r instanceof OWLObjectAllValuesFrom) { qvs.add(new QualifierValue("all_only", "true")); } f.addClause(createRelationshipClauseWithRestrictions(r, fillerId, qvs, ax)); } else if (sup instanceof OWLObjectIntersectionOf) { OWLObjectIntersectionOf i = (OWLObjectIntersectionOf) sup; List clauses = new ArrayList<>(); List collect = asList(i.operands()); for (OWLClassExpression operand : collect) { if (operand instanceof OWLObjectCardinalityRestriction) { OWLObjectCardinalityRestriction restriction = (OWLObjectCardinalityRestriction) operand; OWLClassExpression filler = restriction.getFiller(); if (filler.isBottomEntity() || filler.isTopEntity()) { error(TOP_BOTTOM_NONTRANSLATEABLE, ax, false); return; } String fillerId = getIdentifier(filler); if (fillerId == null) { error(ax, true); return; } clauses.add(createRelationshipClauseWithCardinality(restriction, fillerId, new HashSet<>(qvs), ax)); } else if (operand instanceof OWLQuantifiedObjectRestriction) { OWLQuantifiedObjectRestriction restriction = (OWLQuantifiedObjectRestriction) operand; OWLClassExpression filler = restriction.getFiller(); if (filler.isBottomEntity() || filler.isTopEntity()) { error(TOP_BOTTOM_NONTRANSLATEABLE, ax, false); return; } String fillerId = getIdentifier(filler); if (fillerId == null) { error(ax, true); return; } clauses.add(createRelationshipClauseWithRestrictions(restriction, fillerId, new HashSet<>(qvs), ax)); } else { error(ax, true); return; } } if (clauses.isEmpty()) { error(ax, true); return; } clauses = normalizeRelationshipClauses(clauses); clauses.forEach(f::addClause); } else { error(ax, true); return; } } else { error(ax, true); return; } } /** * Creates the relationship clause with restrictions. * * @param r the r * @param fillerId the filler id * @param qvs the qvs * @param ax the ax * @return the clause */ protected Clause createRelationshipClauseWithRestrictions(OWLQuantifiedObjectRestriction r, String fillerId, Set qvs, OWLSubClassOfAxiom ax) { Clause c = new Clause(OboFormatTag.TAG_RELATIONSHIP.getTag()); c.addValue(getIdentifier(r.getProperty())); c.addValue(fillerId); c.setQualifierValues(qvs); addQualifiers(c, ax.annotations()); return c; } /** * Creates the relationship clause with cardinality. * * @param restriction the restriction * @param fillerId the filler id * @param qvs the qvs * @param ax the ax * @return the clause */ protected Clause createRelationshipClauseWithCardinality( OWLObjectCardinalityRestriction restriction, String fillerId, Set qvs, OWLSubClassOfAxiom ax) { Clause c = new Clause(OboFormatTag.TAG_RELATIONSHIP.getTag()); c.addValue(getIdentifier(restriction.getProperty())); c.addValue(fillerId); c.setQualifierValues(qvs); String q = "cardinality"; if (restriction instanceof OWLObjectMinCardinality) { q = "minCardinality"; } else if (restriction instanceof OWLObjectMaxCardinality) { q = "maxCardinality"; } c.addQualifierValue(new QualifierValue(q, Integer.toString(restriction.getCardinality()))); addQualifiers(c, ax.annotations()); return c; } protected void error(String message, OWLAxiom ax, boolean shouldLogComplaint) { untranslatableAxioms.add(ax); error(message + ax, shouldLogComplaint); } protected void error(OWLAxiom ax, boolean shouldLogComplaint) { untranslatableAxioms.add(ax); error("the axiom is not translated : " + ax, shouldLogComplaint); } protected void error(String message, boolean shouldLogComplaint) { if (strictConversion) { throw new OWLRuntimeException("The conversion is halted: " + message); } else { if (!muteUntranslatableAxioms && shouldLogComplaint) { LOG.error("MASKING ERROR «{}»", message, new Exception()); } } } protected void warn(String message) { if (strictConversion) { throw new OWLRuntimeException("The conversion is halted: " + message); } else { LOG.warn("MASKING ERROR «{}»", message); } } /** * Helper class: allow to return two values for the alt id check. */ private static class OboAltIdCheckResult { final String replacedBy; final Set unrelated; OboAltIdCheckResult(String replacedBy, Set unrelated) { this.replacedBy = replacedBy; this.unrelated = unrelated; } } /** * The Class UntranslatableAxiomException. */ public static class UntranslatableAxiomException extends Exception { /** * Instantiates a new untranslatable axiom exception. * * @param message the message * @param cause the cause */ public UntranslatableAxiomException(String message, Throwable cause) { super(message, cause); } /** * Instantiates a new untranslatable axiom exception. * * @param message the message */ public UntranslatableAxiomException(String message) { super(message); } } class Translator implements OWLAxiomVisitor { @Override public void visit(OWLDeclarationAxiom ax) { tr(ax); } @Override public void visit(OWLSubClassOfAxiom ax) { tr(ax); } @Override public void visit(OWLDisjointClassesAxiom ax) { tr(ax); } @Override public void visit(OWLEquivalentClassesAxiom ax) { tr(ax); } @Override public void visit(OWLClassAssertionAxiom ax) { tr(ax); } @Override public void visit(OWLEquivalentObjectPropertiesAxiom ax) { tr(ax); } @Override public void visit(OWLSubAnnotationPropertyOfAxiom ax) { tr(ax); } @Override public void visit(OWLSubObjectPropertyOfAxiom ax) { tr(ax); } @Override public void visit(OWLObjectPropertyRangeAxiom ax) { tr(ax); } @Override public void visit(OWLFunctionalObjectPropertyAxiom ax) { tr(ax); } @Override public void visit(OWLSymmetricObjectPropertyAxiom ax) { tr(ax); } @Override public void visit(OWLAsymmetricObjectPropertyAxiom ax) { tr(ax); } @Override public void visit(OWLObjectPropertyDomainAxiom ax) { tr(ax); } @Override public void visit(OWLInverseFunctionalObjectPropertyAxiom ax) { tr(ax); } @Override public void visit(OWLInverseObjectPropertiesAxiom ax) { tr(ax); } @Override public void visit(OWLDisjointObjectPropertiesAxiom ax) { tr(ax); } @Override public void visit(OWLReflexiveObjectPropertyAxiom ax) { tr(ax); } @Override public void visit(OWLTransitiveObjectPropertyAxiom ax) { tr(ax); } @Override public void visit(OWLSubPropertyChainOfAxiom ax) { tr(ax); } @Override public void doDefault(Object o) { if (!(o instanceof OWLAnnotationAssertionAxiom)) { error((OWLAxiom) o, false); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy