/** * Copyright [2013] EMBL - European Bioinformatics Institute * 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 uk.ac.ebi.brain.core; import java.io.File; import java.net.*; import java.util.*; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.coode.owlapi.manchesterowlsyntax.*; import org.semanticweb.elk.owlapi.*; import org.semanticweb.elk.reasoner.config.ReasonerConfiguration; import org.semanticweb.owlapi.apibinding.OWLManager; import org.semanticweb.owlapi.expression.*; import org.semanticweb.owlapi.model.*; import org.semanticweb.owlapi.profiles.*; import org.semanticweb.owlapi.reasoner.*; import org.semanticweb.owlapi.util.*; import org.semanticweb.owlapi.vocab.PrefixOWLOntologyFormat; import uk.ac.ebi.brain.error.*; /** * Facade class to manipulate the OWL-API. * The documentation is available at https://github.com/loopasam/Brain/wiki * @author Samuel Croset */ public class Brain { private OWLOntology ontology; private OWLReasoner reasoner; private OWLOntologyManager manager; private OWLDataFactory factory; private OWLReasonerFactory reasonerFactory; private BidirectionalShortFormProvider bidiShortFormProvider; private OWLEntityChecker entityChecker; private DefaultPrefixManager prefixManager; public OWLDatatype INTEGER; public OWLDatatype FLOAT; public OWLDatatype BOOLEAN; public static final String DEFAULT_PREFIX = "brain#"; private boolean isClassified; private ElkReasonerConfiguration configuration; public OWLOntology getOntology() { return ontology; } @Deprecated public void setOntology(OWLOntology ontology) { this.ontology = ontology; } public OWLReasoner getReasoner() { return reasoner; } @Deprecated public void setReasoner(OWLReasoner reasoner) { this.reasoner = reasoner; } public OWLOntologyManager getManager() { return manager; } @Deprecated public void setManager(OWLOntologyManager manager) { this.manager = manager; } public OWLDataFactory getFactory() { return factory; } @Deprecated public void setFactory(OWLDataFactory factory) { this.factory = factory; } public OWLReasonerFactory getReasonerFactory() { return reasonerFactory; } @Deprecated public void setReasonerFactory(OWLReasonerFactory reasonerFactory) { this.reasonerFactory = reasonerFactory; } public BidirectionalShortFormProvider getBidiShortFormProvider() { return bidiShortFormProvider; } @Deprecated public void setBidiShortFormProvider(BidirectionalShortFormProvider bidiShortFormProvider) { this.bidiShortFormProvider = bidiShortFormProvider; } public OWLEntityChecker getEntityChecker() { return entityChecker; } @Deprecated public void setEntityChecker(OWLEntityChecker entityChecker) { this.entityChecker = entityChecker; } @Deprecated public void setPrefixManager(DefaultPrefixManager prefixManager) { this.prefixManager = prefixManager; } public PrefixManager getPrefixManager() { return prefixManager; } @Deprecated public void setClassified(boolean isClassified) { this.isClassified = isClassified; } public boolean isClassified() { return isClassified; } /** * Creates a Brain instance with the specified prefix and ontology IRI. * The OWL entities created will inherit from this prefix. * The number of working threads for reasoning is set to the maximum (default). * @param prefix * @param ontologyIri * @throws BadPrefixException * @throws NewOntologyException */ public Brain(String prefix, String ontologyIri) throws NewOntologyException, BadPrefixException { this(prefix, ontologyIri, -1); } /** * Creates a Brain instance with the a default prefix (<brain#>) and ontology IRI (<brain.owl>). * The number of working threads for reasoning is set to the maximum (default). * @throws BadPrefixException * @throws NewOntologyException */ public Brain() throws NewOntologyException, BadPrefixException { this(DEFAULT_PREFIX, "brain.owl", -1); } /** * Creates a Brain instance from an ontology object. * This method is useful for people with a pre-loaded ontology in memory and willing * to quickly create a Brain instance from it. * The default prefix (<brain#>) and ontology IRI (<brain.owl>) are used. * The number of working threads for reasoning is set to the maximum (default). * @param ontology * @throws BadPrefixException * @throws NewOntologyException */ public Brain(OWLOntology ontology) throws NewOntologyException { this.prefixManager = new DefaultPrefixManager(DEFAULT_PREFIX); this.manager = ontology.getOWLOntologyManager(); this.ontology = ontology; this.factory = this.manager.getOWLDataFactory(); ShortFormProvider shortFormProvider = new SimpleShortFormProvider(); Set<OWLOntology> importsClosure = this.ontology.getImportsClosure(); this.bidiShortFormProvider = new BidirectionalShortFormProviderAdapter(this.manager, importsClosure, shortFormProvider); this.entityChecker = new ShortFormEntityChecker(this.bidiShortFormProvider); Logger.getLogger("org.semanticweb.elk").setLevel(Level.OFF); this.reasonerFactory = new ElkReasonerFactory(); this.reasoner = this.getReasonerFactory().createReasoner(this.ontology); this.isClassified = false; this.INTEGER = this.factory.getIntegerOWLDatatype(); this.FLOAT = this.factory.getFloatOWLDatatype(); this.BOOLEAN = this.factory.getBooleanOWLDatatype(); update(); try { this.addClass("http://www.w3.org/2002/07/owl#Thing"); } catch (BrainException e) { //If the class throws an error it's no big deal. //It means that the top class was already present in the loaded //ontology } } /** * Creates a Brain instance with the specified prefix and ontology IRI. * The OWL entities created will inherit from this prefix. * The number of working threads for reasoning is set to the passed value (numberOfWorkers parameter). * @param prefix * @param ontologyIri * @param numberOfWorkers * @throws BadPrefixException * @throws NewOntologyException */ public Brain(String prefix, String ontologyIri, int numberOfWorkers) throws NewOntologyException, BadPrefixException { if(!prefix.equals(DEFAULT_PREFIX)){ try { @SuppressWarnings("unused") URL url = new URL(prefix); } catch (MalformedURLException e) { throw new BadPrefixException(e); } if(!(prefix.endsWith("#") || prefix.endsWith("/"))){ throw new BadPrefixException("The separator symbol must either be '/' or '#'"); } } this.prefixManager = new DefaultPrefixManager(prefix); this.manager = OWLManager.createOWLOntologyManager(); this.factory = manager.getOWLDataFactory(); try { this.ontology = manager.createOntology(IRI.create(ontologyIri)); } catch (OWLOntologyCreationException e) { throw new NewOntologyException(e); } ShortFormProvider shortFormProvider = new SimpleShortFormProvider(); Set<OWLOntology> importsClosure = this.ontology.getImportsClosure(); this.bidiShortFormProvider = new BidirectionalShortFormProviderAdapter(this.manager, importsClosure, shortFormProvider); this.entityChecker = new ShortFormEntityChecker(this.bidiShortFormProvider); Logger.getLogger("org.semanticweb.elk").setLevel(Level.OFF); if(numberOfWorkers != -1){ this.configuration = new ElkReasonerConfiguration(); this.configuration.getElkConfiguration().setParameter(ReasonerConfiguration.NUM_OF_WORKING_THREADS, Integer.toString(numberOfWorkers)); this.reasonerFactory = new ElkReasonerFactory(); this.reasoner = this.getReasonerFactory().createReasoner(this.ontology, this.configuration); }else{ this.reasonerFactory = new ElkReasonerFactory(); this.reasoner = this.getReasonerFactory().createReasoner(this.ontology); } this.isClassified = false; this.INTEGER = this.factory.getIntegerOWLDatatype(); this.FLOAT = this.factory.getFloatOWLDatatype(); this.BOOLEAN = this.factory.getBooleanOWLDatatype(); update(); try { this.addClass("http://www.w3.org/2002/07/owl#Thing"); } catch (BrainException e) { e.printStackTrace(); } } /** * Declare a prefix mapping. Will be used when the ontology is serialized * (.owl file). Example of usage: * <code> * <pre> * {@code * Brain brain = new Brain(); * brain.prefix("http://www.example.org/", "example"); * } * </pre> * </code> * @param longForm * @param abbreviation */ public void prefix(String longForm, String abbreviation) { this.prefixManager.setPrefix(abbreviation + ":", longForm); } /** * Add an OWL class to the ontology. An OWLClass * object is returned. The input value can either be * the short form of the class or the full IRI. Examples of input * parameter: "http://example.org/MyClass" or "MyClass". * @param className * @return owlClass * @throws ExistingClassException * @throws BadNameException */ public OWLClass addClass(String className) throws BadNameException, ExistingClassException { if(isExternalEntity(className)){ return addExternalClass(className); }else{ try { this.getOWLClass(className); throw new ExistingClassException("The class '"+ className +"' already exists."); } catch (NonExistingClassException e) { validate(className); OWLClass owlClass = null; try{ owlClass = this.factory.getOWLClass(className, this.prefixManager); } catch(RuntimeException re) { throw new BadNameException("The prefix you are using is not recognized. Use either no prefix or a valid URI. More info: " + re); } declare(owlClass); update(); return owlClass; } } } /** * Add an OWL class to the ontology. The class refers to an external class (external ontology). * @param className * @return owlClass * @throws ExistingClassException * @throws BadNameException */ private OWLClass addExternalClass(String className) throws ExistingClassException, BadNameException { if(this.ontology.containsClassInSignature(IRI.create(className))){ throw new ExistingClassException("The class already '"+ className +"' already exists."); } validateExternalEntity(className); OWLClass owlClass = this.factory.getOWLClass(IRI.create(className)); SimpleShortFormProvider shortFormProvider = new SimpleShortFormProvider(); String sf = shortFormProvider.getShortForm(owlClass); try { this.getOWLClass(sf); removeExternalClass(className); throw new ExistingClassException("A class as already the short form '"+ sf +"'. A class name as to be unique."); } catch (NonExistingClassException e) { declare(owlClass); update(); return owlClass; } } /** * Check whether an entity is an external entity (URL present) or not. * @param entityName * @return isExternalEntity */ private boolean isExternalEntity(String entityName) { try { new URL(entityName); return true; } catch (MalformedURLException e) { return false; } } /** * Remove an OWL class from the ontology. The class refers to an external class (external ontology). * @param externalClass */ private void removeExternalClass(String externalClass) { OWLEntityRemover remover = new OWLEntityRemover(this.manager, Collections.singleton(this.ontology)); OWLClass owlClassToRemove = this.factory.getOWLClass(IRI.create(externalClass)); owlClassToRemove.accept(remover); this.manager.applyChanges(remover.getChanges()); remover.reset(); update(); } /** * Declare (register) an OWL entity in the ontology. * @param owlEntity */ private void declare(OWLEntity owlEntity) { OWLDeclarationAxiom declarationAxiom = this.factory.getOWLDeclarationAxiom(owlEntity); manager.addAxiom(this.ontology, declarationAxiom); } /** * Validate an OWL entity. * @param entityName * @throws BadNameException */ private void validate(String entityName) throws BadNameException { URL url; String prefix = null; if(this.prefixManager.getDefaultPrefix().equals(DEFAULT_PREFIX)){ prefix = "http://localhost/"; }else{ prefix = this.prefixManager.getDefaultPrefix(); } try { url = new URL(prefix + entityName); url.toURI(); } catch (MalformedURLException e) { throw new BadNameException(e); } catch (URISyntaxException e) { throw new BadNameException("'"+entityName+"' is not valid valid name for an OWL entity. Use only characters that are valid for a URI (no space, etc...)."); } } /** * Add an OWL named individual to the ontology. An OWLNamedIndividual * object is returned. The input value can either be * the short form of the property or the full IRI. Examples of input * parameter: "http://example.org/MyIndividual" or "MyIndividual". * @param individualName * @return owlNamedIndividual * @throws BadNameException * @throws ExistingNamedIndividualException */ public OWLNamedIndividual addNamedIndividual(String individualName) throws BadNameException, ExistingNamedIndividualException { if(isExternalEntity(individualName)){ return addExternalIndividual(individualName); }else{ try { this.getOWLNamedIndividual(individualName); throw new ExistingNamedIndividualException("The named individual '"+ individualName +"' already exists."); } catch (NonExistingNamedIndividualException e) { validate(individualName); OWLNamedIndividual owlNamedIndividual = null; try{ owlNamedIndividual = this.factory.getOWLNamedIndividual(individualName, this.prefixManager); } catch(RuntimeException re) { throw new BadNameException("The prefix you are using is not recognized. Use either no prefix or a valid URI. More info: " + re); } declare(owlNamedIndividual); update(); return owlNamedIndividual; } } } /** * Add an external named individual to the ontology. * @param individualName * @return owlNamedIndividual * @throws ExistingNamedIndividualException * @throws BadNameException */ private OWLNamedIndividual addExternalIndividual(String individualName) throws ExistingNamedIndividualException, BadNameException { if(this.ontology.containsIndividualInSignature(IRI.create(individualName))){ throw new ExistingNamedIndividualException("The named individual '"+ individualName +"' already exists."); } validateExternalEntity(individualName); OWLNamedIndividual owlNamedIndividual = this.factory.getOWLNamedIndividual(IRI.create(individualName)); SimpleShortFormProvider shortFormProvider = new SimpleShortFormProvider(); String sf = shortFormProvider.getShortForm(owlNamedIndividual); try { this.getOWLNamedIndividual(sf); removeExternalNamedIndividual(individualName); throw new ExistingNamedIndividualException("A named individual as already the short form '"+ sf +"'. The short form cannot be re-used name as to be unique."); } catch (NonExistingNamedIndividualException e) { declare(owlNamedIndividual); update(); return owlNamedIndividual; } } /** * Remove an external OWL named individual from the ontology. * @param externalIndividual */ private void removeExternalNamedIndividual(String externalIndividual) { OWLEntityRemover remover = new OWLEntityRemover(this.manager, Collections.singleton(this.ontology)); OWLNamedIndividual owlNamedIndividualToRemove = this.factory.getOWLNamedIndividual(IRI.create(externalIndividual)); owlNamedIndividualToRemove.accept(remover); this.manager.applyChanges(remover.getChanges()); remover.reset(); update(); } /** * Validate an external OWL entity. * @param entityIri * @throws BadNameException */ private void validateExternalEntity(String entityIri) throws BadNameException { URL url; try { url = new URL(entityIri); url.toURI(); } catch (MalformedURLException e) { throw new BadNameException(e); } catch (URISyntaxException e) { throw new BadNameException("'"+entityIri+"' is not valid valid name for an OWL entity. Use only characters that are valid for a URI (no space, etc...)."); } } /** * Add the axiom to the ontology. * @param owlAxiom */ private void addAxiom(OWLAxiom owlAxiom) { AddAxiom addAx = new AddAxiom(this.ontology, owlAxiom); this.manager.applyChange(addAx); update(); } /** * Add an OWL object property to the ontology. An OWLObjectProperty * object is returned. The input value can either be * the short form of the property or the full IRI. Examples of input * parameter: "http://example.org/MyProperty" or "MyProperty". * @param objectPropertyName * @return owlObjectProperty * @throws ExistingObjectPropertyException * @throws BadNameException */ public OWLObjectProperty addObjectProperty(String objectPropertyName) throws ExistingObjectPropertyException, BadNameException { if(isExternalEntity(objectPropertyName)){ return addExternalObjectProperty(objectPropertyName); }else{ try { this.getOWLObjectProperty(objectPropertyName); throw new ExistingObjectPropertyException("The object property '"+ objectPropertyName +"' already exists."); } catch (NonExistingObjectPropertyException e) { validate(objectPropertyName); OWLObjectProperty owlObjectProperty = null; try{ owlObjectProperty = this.factory.getOWLObjectProperty(objectPropertyName, this.prefixManager); } catch(RuntimeException re) { throw new BadNameException("The prefix you are using is not recognized. Use either no prefix or a valid URI. More info: " + re); } declare(owlObjectProperty); update(); return owlObjectProperty; } } } /** * Add an external OWL object property to the ontology. * @param objectPropertyName * @return owlObjectProperty * @throws ExistingObjectPropertyException * @throws BadNameException */ private OWLObjectProperty addExternalObjectProperty(String objectPropertyName) throws BadNameException, ExistingObjectPropertyException { if(this.ontology.containsObjectPropertyInSignature(IRI.create(objectPropertyName))){ throw new ExistingObjectPropertyException("The object property already '"+ objectPropertyName +"' already exists."); } validateExternalEntity(objectPropertyName); OWLObjectProperty owlObjectProperty = this.factory.getOWLObjectProperty(IRI.create(objectPropertyName)); SimpleShortFormProvider shortFormProvider = new SimpleShortFormProvider(); String sf = shortFormProvider.getShortForm(owlObjectProperty); try { this.getOWLObjectProperty(sf); removeExternalObjectProperty(objectPropertyName); throw new ExistingObjectPropertyException("An object property as already the short form '"+ sf +"'. The short form cannot be re-used name as to be unique."); } catch (NonExistingObjectPropertyException e) { declare(owlObjectProperty); update(); return owlObjectProperty; } } /** * Remove an external OWL object property to the ontology. * @param externalObjectProperty */ private void removeExternalObjectProperty(String externalObjectProperty) { OWLEntityRemover remover = new OWLEntityRemover(this.manager, Collections.singleton(this.ontology)); OWLObjectProperty owlObjectPropertyToRemove = this.factory.getOWLObjectProperty(IRI.create(externalObjectProperty)); owlObjectPropertyToRemove.accept(remover); this.manager.applyChanges(remover.getChanges()); remover.reset(); update(); } /** * Add an OWL data property to the ontology. An OWLDataProperty * object is returned. The input value can either be * the short form of the property or the full IRI. Examples of input * parameter: "http://example.org/MyProperty" or "MyProperty". * @param dataPropertyName * @return owlDataProperty * @throws ExistingDataPropertyException * @throws BadNameException */ public OWLDataProperty addDataProperty(String dataPropertyName) throws ExistingDataPropertyException, BadNameException { if(isExternalEntity(dataPropertyName)){ return addExternalDataProperty(dataPropertyName); }else{ try { this.getOWLDataProperty(dataPropertyName); throw new ExistingDataPropertyException("The data property '"+ dataPropertyName +"' already exists."); } catch (NonExistingDataPropertyException e) { validate(dataPropertyName); OWLDataProperty owlDataProperty = null; try{ owlDataProperty = this.factory.getOWLDataProperty(dataPropertyName, this.prefixManager); } catch(RuntimeException re) { throw new BadNameException("The prefix you are using is not recognized. Use either no prefix or a valid URI. More info: " + re); } declare(owlDataProperty); update(); return owlDataProperty; } } } /** * Add an external OWL data property to the ontology. * @param dataPropertyName * @return owlDataProperty * @throws ExistingDataPropertyException * @throws BadNameException */ private OWLDataProperty addExternalDataProperty(String dataPropertyName) throws BadNameException, ExistingDataPropertyException { if(this.ontology.containsDataPropertyInSignature(IRI.create(dataPropertyName))){ throw new ExistingDataPropertyException("The data property already '"+ dataPropertyName +"' already exists."); } validateExternalEntity(dataPropertyName); OWLDataProperty owlDataProperty = this.factory.getOWLDataProperty(IRI.create(dataPropertyName)); SimpleShortFormProvider shortFormProvider = new SimpleShortFormProvider(); String sf = shortFormProvider.getShortForm(owlDataProperty); try { this.getOWLDataProperty(sf); removeExternalDataProperty(dataPropertyName); throw new ExistingDataPropertyException("An entity as already the short form '"+ sf +"'. The short form cannot be re-used name as to be unique."); } catch (NonExistingDataPropertyException e) { declare(owlDataProperty); update(); return owlDataProperty; } } /** * Remove an external OWL data property to the ontology. * @param externalDataProperty */ private void removeExternalDataProperty(String externalDataProperty) { OWLEntityRemover remover = new OWLEntityRemover(this.manager, Collections.singleton(this.ontology)); OWLDataProperty owlDataPropertyToRemove = this.factory.getOWLDataProperty(IRI.create(externalDataProperty)); owlDataPropertyToRemove.accept(remover); this.manager.applyChanges(remover.getChanges()); remover.reset(); update(); } /** * Add an OWL annotation property to the ontology. An OWLAnnotationProperty * object is returned. The input value can either be * the short form of the property or the full IRI. Examples of input * parameter: "http://example.org/MyProperty" or "MyProperty". * @param annotationProperty * @return owlAnnotationProperty * @throws ExistingDataPropertyException * @throws BadNameException */ public OWLAnnotationProperty addAnnotationProperty(String annotationProperty) throws ExistingAnnotationPropertyException, BadNameException { if(isExternalEntity(annotationProperty)){ return addExternalAnnotationProperty(annotationProperty); }else{ try { this.getOWLAnnotationProperty(annotationProperty); throw new ExistingAnnotationPropertyException("The annotation property '"+ annotationProperty +"' already exists."); } catch (NonExistingAnnotationPropertyException e) { validate(annotationProperty); OWLAnnotationProperty owlAnnotationProperty = null; try{ owlAnnotationProperty = this.factory.getOWLAnnotationProperty(annotationProperty, this.prefixManager); } catch(RuntimeException re) { throw new BadNameException("The prefix you are using is not recognized. Use either no prefix or a valid URI. More info: " + re); } declare(owlAnnotationProperty); update(); return owlAnnotationProperty; } } } /** * Add an external OWL annotation property to the ontology. * @param annotationPropertyName * @return owlAnnotationProperty * @throws ExistingAnnotationPropertyException * @throws BadNameException */ private OWLAnnotationProperty addExternalAnnotationProperty(String annotationPropertyName) throws BadNameException, ExistingAnnotationPropertyException { if(this.ontology.containsAnnotationPropertyInSignature(IRI.create(annotationPropertyName))){ throw new ExistingAnnotationPropertyException("The annotation property already '"+ annotationPropertyName +"' already exists."); } validateExternalEntity(annotationPropertyName); OWLAnnotationProperty owlAnnotationProperty = this.factory.getOWLAnnotationProperty(IRI.create(annotationPropertyName)); SimpleShortFormProvider shortFormProvider = new SimpleShortFormProvider(); String sf = shortFormProvider.getShortForm(owlAnnotationProperty); try { this.getOWLAnnotationProperty(sf); removeExternalAnnotationProperty(annotationPropertyName); throw new ExistingAnnotationPropertyException("An entity as already the short form '"+ sf +"'. The short form cannot be re-used name as to be unique."); } catch (NonExistingAnnotationPropertyException e) { declare(owlAnnotationProperty); update(); return owlAnnotationProperty; } } /** * Remove an external OWL annotation property to the ontology. * @param externalAnnotationProperty */ private void removeExternalAnnotationProperty(String externalAnnotationProperty) { OWLEntityRemover remover = new OWLEntityRemover(this.manager, Collections.singleton(this.ontology)); OWLAnnotationProperty owlAnnotationPropertyToRemove = this.factory.getOWLAnnotationProperty(IRI.create(externalAnnotationProperty)); owlAnnotationPropertyToRemove.accept(remover); this.manager.applyChanges(remover.getChanges()); remover.reset(); update(); } /** * Set the reasoner is the not classified state and flush it. * A classification will have to be redone. */ private void update() { this.reasoner.flush(); this.isClassified = false; } /** * Returns the OWL class corresponding to the input string (short form). * @param className * @return owlClass * @throws NonExistingClassException */ public OWLClass getOWLClass(String className) throws NonExistingClassException { OWLEntity entity = this.bidiShortFormProvider.getEntity(className); if(entity != null && entity.isOWLClass()){ return (OWLClass) entity; }else{ throw new NonExistingClassException("The entity '"+ className +"' does not exist or is not an OWL class."); } } /** * Returns the named individual corresponding to the input string (short form). * @param individualName * @return namedIndividual * @throws NonExistingNamedIndividualException */ public OWLNamedIndividual getOWLNamedIndividual(String individualName) throws NonExistingNamedIndividualException { OWLEntity entity = this.bidiShortFormProvider.getEntity(individualName); if(entity != null && entity.isOWLNamedIndividual()){ return (OWLNamedIndividual) entity; }else{ throw new NonExistingNamedIndividualException("The entity '"+ individualName +"' does not exist or is not an OWL named individual."); } } /** * Returns the OWL annotation property corresponding to the input string (short form). * @param propertyName * @return owlAnnotationProperty * @throws NonExistingAnnotationPropertyException */ public OWLAnnotationProperty getOWLAnnotationProperty(String propertyName) throws NonExistingAnnotationPropertyException { OWLEntity entity = this.bidiShortFormProvider.getEntity(propertyName); if(entity != null && entity.isOWLAnnotationProperty()){ return (OWLAnnotationProperty) entity; }else{ throw new NonExistingAnnotationPropertyException("The annotation property '"+ propertyName +"' does not exist."); } } /** * Returns the OWL object property corresponding to the input string (short form). * @param objectPropertyName * @return owlObjectProperty * @throws NonExistingObjectPropertyException */ public OWLObjectProperty getOWLObjectProperty(String objectPropertyName) throws NonExistingObjectPropertyException { OWLEntity entity = this.bidiShortFormProvider.getEntity(objectPropertyName); if(entity != null && entity.isOWLObjectProperty()){ return (OWLObjectProperty) entity; }else{ throw new NonExistingObjectPropertyException("The object property '"+ objectPropertyName +"' does not exist."); } } /** * Returns the OWL data property corresponding to the input string (short form). * @param dataPropertyName * @return owlDataProperty * @throws NonExistingDataPropertyException */ public OWLDataProperty getOWLDataProperty(String dataPropertyName) throws NonExistingDataPropertyException { OWLEntity entity = this.bidiShortFormProvider.getEntity(dataPropertyName); if(entity != null && entity.isOWLDataProperty()){ return (OWLDataProperty) entity; }else{ throw new NonExistingDataPropertyException("The data property '"+ dataPropertyName +"' does not exist."); } } /** * Declare a subClassOf axiom. * @param subClass * @param superClass * @throws ClassExpressionException */ public void subClassOf(String subClass, String superClass) throws ClassExpressionException { OWLClassExpression subClassExpression = parseClassExpression(subClass); OWLClassExpression superClassExpression = parseClassExpression(superClass); OWLSubClassOfAxiom subClassAxiom = this.factory.getOWLSubClassOfAxiom(subClassExpression, superClassExpression); addAxiom(subClassAxiom); } /** * Declare an equivalence axiom. * @param class1 * @param class2 * @throws ClassExpressionException */ public void equivalentClasses(String class1, String class2) throws ClassExpressionException { OWLClassExpression classExpression1 = parseClassExpression(class1); OWLClassExpression classExpression2 = parseClassExpression(class2); OWLEquivalentClassesAxiom equivalentClassAxiom = this.factory.getOWLEquivalentClassesAxiom(classExpression1, classExpression2); addAxiom(equivalentClassAxiom); } /** * Declare an disjoint axiom. * @param class1 * @param class2 * @throws ClassExpressionException */ public void disjointClasses(String class1, String class2) throws ClassExpressionException { OWLClassExpression classExpression1 = parseClassExpression(class1); OWLClassExpression classExpression2 = parseClassExpression(class2); OWLDisjointClassesAxiom disjointClassAxiom = this.factory.getOWLDisjointClassesAxiom(classExpression1, classExpression2); addAxiom(disjointClassAxiom); } /** * Assert a type to an individual (class assertion). * @param classExpression * @param individualName * @throws ClassExpressionException * @throws NamedIndividualException */ public void type(String classExpression, String individualName) throws ClassExpressionException, NamedIndividualException { OWLClassExpression owlClassExpression = parseClassExpression(classExpression); OWLNamedIndividual owlIndividual = parseNamedIndividual(individualName); OWLClassAssertionAxiom axiom = this.factory.getOWLClassAssertionAxiom(owlClassExpression, owlIndividual); addAxiom(axiom); } /** * Assert a relation between two individuals (triple or fact). * @param subjectIndividual * @param objectProperty * @param objectIndividual * @throws ObjectPropertyExpressionException * @throws NamedIndividualException */ public void objectPropertyAssertion(String subjectIndividual, String objectProperty, String objectIndividual) throws ObjectPropertyExpressionException, NamedIndividualException { OWLObjectPropertyExpression owlObjectProperty = parseObjectPropertyExpression(objectProperty); OWLNamedIndividual owlSubjectIndividual = parseNamedIndividual(subjectIndividual); OWLNamedIndividual owlObjectIndividual = parseNamedIndividual(objectIndividual); OWLObjectPropertyAssertionAxiom axiom = this.factory.getOWLObjectPropertyAssertionAxiom(owlObjectProperty, owlSubjectIndividual, owlObjectIndividual); addAxiom(axiom); } //TODO implement axiom same individual once supported by ELK //TODO implement axiom different individual once supported by ELK //TODO implement axiom data property assertion once supported by ELK //TODO implement axiom negative object property assertion once supported by ELK //TODO implement axiom negative data property assertion once supported by ELK /** * Declare an transitive axiom. * @param propertyExpression * @throws ObjectPropertyExpressionException */ public void transitive(String propertyExpression) throws ObjectPropertyExpressionException { OWLObjectPropertyExpression expression = parseObjectPropertyExpression(propertyExpression); OWLTransitiveObjectPropertyAxiom axiom = this.factory.getOWLTransitiveObjectPropertyAxiom(expression); addAxiom(axiom); } /** * Declare an reflexive axiom. * @param propertyExpression * @throws ObjectPropertyExpressionException */ public void reflexive(String propertyExpression) throws ObjectPropertyExpressionException { OWLObjectPropertyExpression expression = parseObjectPropertyExpression(propertyExpression); OWLReflexiveObjectPropertyAxiom axiom = this.factory.getOWLReflexiveObjectPropertyAxiom(expression); addAxiom(axiom); } /** * Declare an functional axiom. * @param propertyExpression * @throws DataPropertyExpressionException */ public void functional(String propertyExpression) throws DataPropertyExpressionException { OWLDataPropertyExpression expression = parseDataPropertyExpression(propertyExpression); OWLFunctionalDataPropertyAxiom axiom = this.factory.getOWLFunctionalDataPropertyAxiom(expression); addAxiom(axiom); } /** * Declare an domain axiom. * @param propertyExpression * @param classExpression * @throws ClassExpressionException * @throws NonExistingEntityException */ public void domain(String propertyExpression, String classExpression) throws ClassExpressionException, NonExistingEntityException{ OWLClassExpression domainExpression = parseClassExpression(classExpression); try { OWLObjectPropertyExpression owlPropertyExpression = parseObjectPropertyExpression(propertyExpression); OWLObjectPropertyDomainAxiom axiom = this.factory.getOWLObjectPropertyDomainAxiom(owlPropertyExpression, domainExpression); addAxiom(axiom); } catch (ObjectPropertyExpressionException e) { OWLDataPropertyExpression owlPropertyExpression; try { owlPropertyExpression = parseDataPropertyExpression(propertyExpression); OWLDataPropertyDomainAxiom axiom = this.factory.getOWLDataPropertyDomainAxiom(owlPropertyExpression, domainExpression); addAxiom(axiom); } catch (DataPropertyExpressionException e1) { throw new NonExistingEntityException("The property '"+propertyExpression+"' does not exist."); } } } /** * Declare an range axiom. * @param propertyExpression * @param classExpression * @throws ClassExpressionException * @throws ObjectPropertyExpressionException */ public void range(String propertyExpression, String classExpression) throws ClassExpressionException, ObjectPropertyExpressionException { OWLObjectPropertyExpression owlPropertyExpression = parseObjectPropertyExpression(propertyExpression); OWLClassExpression rangeExpression = parseClassExpression(classExpression); OWLObjectPropertyRangeAxiom axiom = this.factory.getOWLObjectPropertyRangeAxiom(owlPropertyExpression, rangeExpression); addAxiom(axiom); } /** * Declare an range axiom. * @param propertyExpression * @param dataType * @throws DataPropertyExpressionException */ public void range(String propertyExpression, OWLDatatype dataType) throws DataPropertyExpressionException { OWLDataPropertyExpression owlPropertyExpression = parseDataPropertyExpression(propertyExpression); OWLDataPropertyRangeAxiom axiom = this.factory.getOWLDataPropertyRangeAxiom(owlPropertyExpression, dataType); addAxiom(axiom); } /** * Declare an equivalentProperties axiom. * @param property1 * @param property2 * @throws BrainException */ public void equivalentProperties(String property1, String property2) throws BrainException { try { OWLObjectPropertyExpression owlProperty1 = parseObjectPropertyExpression(property1); OWLObjectPropertyExpression owlProperty2 = parseObjectPropertyExpression(property2); OWLEquivalentObjectPropertiesAxiom axiom = this.factory.getOWLEquivalentObjectPropertiesAxiom(owlProperty1, owlProperty2); addAxiom(axiom); } catch (ObjectPropertyExpressionException e) { try{ OWLDataPropertyExpression owlProperty1 = parseDataPropertyExpression(property1); OWLDataPropertyExpression owlProperty2 = parseDataPropertyExpression(property2); OWLEquivalentDataPropertiesAxiom axiom = this.factory.getOWLEquivalentDataPropertiesAxiom(owlProperty1, owlProperty2); addAxiom(axiom); } catch (DataPropertyExpressionException e1) { throw new BrainException("One of the properties ('"+property1+"' or '"+property2+"') does not exist or the properties have different types."); } } } /** * Declare an subPropertyOf axiom. * @param subProperty * @param superProperty * @throws BrainException */ public void subPropertyOf(String subProperty, String superProperty) throws BrainException { try{ OWLObjectPropertyExpression subPropertyExpression = parseObjectPropertyExpression(subProperty); OWLObjectPropertyExpression superPropertyExpression = parseObjectPropertyExpression(superProperty); OWLSubObjectPropertyOfAxiom subPropertyAxiom = this.factory.getOWLSubObjectPropertyOfAxiom(subPropertyExpression, superPropertyExpression); addAxiom(subPropertyAxiom); } catch(ObjectPropertyExpressionException e){ try{ OWLDataPropertyExpression subPropertyExpression = parseDataPropertyExpression(subProperty); OWLDataPropertyExpression superPropertyExpression = parseDataPropertyExpression(superProperty); OWLSubDataPropertyOfAxiom subPropertyAxiom = this.factory.getOWLSubDataPropertyOfAxiom(subPropertyExpression, superPropertyExpression); addAxiom(subPropertyAxiom); }catch (DataPropertyExpressionException e1) { throw new BrainException("One of the properties ('"+subProperty+"' or '"+superProperty+"') does not exist or the properties have different types."); } } } /** * Declare an chain axiom. * @param chain * @param superProperty * @throws ObjectPropertyExpressionException */ public void chain(String chain, String superProperty) throws ObjectPropertyExpressionException { List<OWLObjectPropertyExpression> chainExpression = parseObjectPropertyChain(chain); OWLObjectPropertyExpression superPropertyExpression = parseObjectPropertyExpression(superProperty); OWLSubPropertyChainOfAxiom axiom = this.factory.getOWLSubPropertyChainOfAxiom(chainExpression, superPropertyExpression); addAxiom(axiom); } /** * Declare an annotation axiom. * @param entity * @param annotationProperty * @param content * @throws NonExistingEntityException */ public void annotation(String entity, String annotationProperty, String content) throws NonExistingEntityException { if(this.bidiShortFormProvider.getEntity(entity) != null){ OWLEntity owlEntity = this.bidiShortFormProvider.getEntity(entity); OWLAnnotationProperty owlAnnotationProperty = this.getOWLAnnotationProperty(annotationProperty); OWLAnnotation labelAnnotation = this.factory.getOWLAnnotation(owlAnnotationProperty, this.factory.getOWLLiteral(content)); OWLAxiom axiom = this.factory.getOWLAnnotationAssertionAxiom(owlEntity.getIRI(), labelAnnotation); addAxiom(axiom); }else{ throw new NonExistingEntityException("The entity '"+entity+"' does not exist."); } } /** * Declare an annotation axiom. * @param entity * @param annotationProperty * @param content * @throws NonExistingEntityException */ private void annotation(String entity, OWLAnnotationProperty annotationProperty, String content) throws NonExistingEntityException { if(this.bidiShortFormProvider.getEntity(entity) != null){ OWLEntity owlEntity = this.bidiShortFormProvider.getEntity(entity); OWLAnnotation labelAnnotation = this.factory.getOWLAnnotation(annotationProperty, this.factory.getOWLLiteral(content)); OWLAxiom axiom = this.factory.getOWLAnnotationAssertionAxiom(owlEntity.getIRI(), labelAnnotation); addAxiom(axiom); }else{ throw new NonExistingEntityException("The entity '"+entity+"' does not exist."); } } /** * Retrieve the content of an annotation. * @param entity * @param annotationProperty * @throws NonExistingEntityException * @return content */ private String getAnnotation(String entity, OWLAnnotationProperty annotationProperty) throws NonExistingEntityException { if(this.bidiShortFormProvider.getEntity(entity) != null){ OWLEntity owlEntity = this.bidiShortFormProvider.getEntity(entity); for (OWLAnnotation annotation : owlEntity.getAnnotations(this.ontology, annotationProperty)) { if (annotation.getValue() instanceof OWLLiteral) { OWLLiteral val = (OWLLiteral) annotation.getValue(); return val.getLiteral(); } } }else{ throw new NonExistingEntityException("The entity '"+entity+"' does not exist."); } throw new NonExistingEntityException("The entity '"+entity+"' has no annotation of this type attached to it."); } /** * Retrieve the content of an annotation. * @param entity * @param annotationProperty * @throws NonExistingEntityException * @return content */ public String getAnnotation(String entity, String annotationProperty) throws NonExistingEntityException { OWLAnnotationProperty owlAnnotationProperty = this.getOWLAnnotationProperty(annotationProperty); return this.getAnnotation(entity, owlAnnotationProperty); } /** * Retrieve the list of values for an annotation. * @param entity * @param annotationProperty * @throws NonExistingEntityException * @return content */ private List<String> getAnnotations(String entity, OWLAnnotationProperty annotationProperty) throws NonExistingEntityException { if(this.bidiShortFormProvider.getEntity(entity) != null){ OWLEntity owlEntity = this.bidiShortFormProvider.getEntity(entity); List<String> annotations = new ArrayList<String>(); Set<OWLAnnotation> owlAnnotations = owlEntity.getAnnotations(this.ontology, annotationProperty); if(owlAnnotations.size() <= 0){ throw new NonExistingEntityException("The entity '"+entity+"' has no annotation of this type attached to it."); } for (OWLAnnotation annotation : owlAnnotations) { if (annotation.getValue() instanceof OWLLiteral) { OWLLiteral val = (OWLLiteral) annotation.getValue(); annotations.add(val.getLiteral()); } } return annotations; }else{ throw new NonExistingEntityException("The entity '"+entity+"' does not exist."); } } /** * Retrieve the list of values for an annotation. * @param entity * @param annotationProperty * @throws NonExistingEntityException * @return content */ public List<String> getAnnotations(String entity, String annotationProperty) throws NonExistingEntityException { OWLAnnotationProperty owlAnnotationProperty = this.getOWLAnnotationProperty(annotationProperty); return this.getAnnotations(entity, owlAnnotationProperty); } /** * Set the content of the label. * @param entity * @param label * @throws NonExistingEntityException */ public void label(String entity, String label) throws NonExistingEntityException { this.annotation(entity, this.factory.getRDFSLabel(), label); } /** * Get the content of the label. * @param entity * @throws NonExistingEntityException * @return label */ public String getLabel(String entity) throws NonExistingEntityException { return this.getAnnotation(entity, this.factory.getRDFSLabel()); } /** * Set the content of the comment. * @param entity * @param comment * @throws NonExistingEntityException */ public void comment(String entity, String comment) throws NonExistingEntityException { this.annotation(entity, this.factory.getRDFSComment(), comment); } /** * Get the content of the comment. * @param entity * @throws NonExistingEntityException * @return comment */ public String getComment(String entity) throws NonExistingEntityException { return this.getAnnotation(entity, this.factory.getRDFSComment()); } /** * Set the content of rdfs:isDefinedBy. * @param entity * @param isDefinedBy * @throws NonExistingEntityException */ public void isDefinedBy(String entity, String isDefinedBy) throws NonExistingEntityException { this.annotation(entity, this.factory.getRDFSIsDefinedBy(), isDefinedBy); } /** * Get the content of the rdfs:isDefinedBy. * @param entity * @throws NonExistingEntityException * @return rdfs:isDefinedBy */ public String getIsDefinedBy(String entity) throws NonExistingEntityException { return this.getAnnotation(entity, this.factory.getRDFSIsDefinedBy()); } /** * Set the content of rdfs:seeAlso. * @param entity * @param seeAlso * @throws NonExistingEntityException */ public void seeAlso(String entity, String seeAlso) throws NonExistingEntityException { this.annotation(entity, this.factory.getRDFSSeeAlso(), seeAlso); } /** * Get the content of the rdfs:seeAlso. * @param entity * @throws NonExistingEntityException * @return rdfs:isDefinedBy */ public String getSeeAlso(String entity) throws NonExistingEntityException { return this.getAnnotation(entity, this.factory.getRDFSSeeAlso()); } /** * Converts a string into an OWLClassExpression. If a problem is encountered, an error is thrown. * @param expression * @return owlClassExpression * @throws ClassExpressionException */ private OWLClassExpression parseClassExpression(String expression) throws ClassExpressionException { OWLClassExpression owlClassExpression = null; ManchesterOWLSyntaxEditorParser parser = getParser(expression); try { owlClassExpression = parser.parseClassExpression(); } catch (ParserException e) { throw new ClassExpressionException(e); } return owlClassExpression; } /** * Converts a string representing an OWL class expression with labels into * an OWLClassExpression object. If a problem is * encountered, an error is thrown. * @param labelClassExpression * @return owlClassExpression * @throws ClassExpressionException */ public OWLClassExpression parseLabelClassExpression(String labelClassExpression) throws ClassExpressionException { OWLClassExpression owlClassExpression = null; ManchesterOWLSyntaxEditorParser parser = getLabelParser(labelClassExpression); try { owlClassExpression = parser.parseClassExpression(); } catch (ParserException e) { throw new ClassExpressionException(e); } return owlClassExpression; } /** * Converts a string into an OWLObjectPropertyExpression. If a problem is encountered, an error is thrown. * @param objectPropertyExpression * @return owlObjectPropertyExpression * @throws ObjectPropertyExpressionException */ private OWLObjectPropertyExpression parseObjectPropertyExpression(String objectPropertyExpression) throws ObjectPropertyExpressionException { OWLObjectPropertyExpression owlObjectPropertyExpression = null; ManchesterOWLSyntaxEditorParser parser = getParser(objectPropertyExpression); try { owlObjectPropertyExpression = parser.parseObjectPropertyExpression(); } catch (ParserException e) { throw new ObjectPropertyExpressionException(e); } return owlObjectPropertyExpression; } /** * Converts a string into an OWLDataPropertyExpression. If a problem is encountered, an error is thrown. * @param dataPropertyExpression * @return owlDataPropertyExpression * @throws DataPropertyExpressionException */ private OWLDataPropertyExpression parseDataPropertyExpression(String dataPropertyExpression) throws DataPropertyExpressionException { OWLDataPropertyExpression owlDataPropertyExpression = null; ManchesterOWLSyntaxEditorParser parser = getParser(dataPropertyExpression); try { owlDataPropertyExpression = parser.parseDataPropertyExpression(); } catch (ParserException e) { throw new DataPropertyExpressionException(e); } return owlDataPropertyExpression; } /** * Converts a string into a list of OWLObjectPropertyExpression. If a problem is encountered, an error is thrown. * @param chainExpression * @return owlObjectPropertyExpressions * @throws ObjectPropertyExpressionException */ private List<OWLObjectPropertyExpression> parseObjectPropertyChain(String chainExpression) throws ObjectPropertyExpressionException { List<OWLObjectPropertyExpression> owlObjectPropertyExpressions = null; ManchesterOWLSyntaxEditorParser parser = getParser(chainExpression); try { owlObjectPropertyExpressions = parser.parseObjectPropertyChain(); } catch (ParserException e) { throw new ObjectPropertyExpressionException(e); } return owlObjectPropertyExpressions; } /** * Converts a string into an OWLNamedIndividual. If a problem is encountered, an error is thrown. * @param namedIndividual * @return owlNamedIndividual * @throws NamedIndividualException */ private OWLNamedIndividual parseNamedIndividual(String namedIndividual) throws NamedIndividualException { OWLNamedIndividual owlNamedIndividual = null; ManchesterOWLSyntaxEditorParser parser = getParser(namedIndividual); try { owlNamedIndividual = (OWLNamedIndividual) parser.parseIndividual(); } catch (ParserException e) { throw new NamedIndividualException(e); } return owlNamedIndividual; } /** * Instantiate a new Manchester syntax parser. * @param expression * @return parser */ private ManchesterOWLSyntaxEditorParser getParser(String expression) { ManchesterOWLSyntaxEditorParser parser = new ManchesterOWLSyntaxEditorParser(this.factory, expression); parser.setDefaultOntology(this.ontology); parser.setOWLEntityChecker(this.entityChecker); return parser; } /** * Instantiate a new Manchester syntax parser for labels. * @param labelClassExpression * @return parser */ private ManchesterOWLSyntaxEditorParser getLabelParser(String labelClassExpression) { List<OWLAnnotationProperty> rdfsLabels = Arrays.asList(this.factory.getRDFSLabel()); ShortFormProvider sfp = new AnnotationValueShortFormProvider(rdfsLabels, Collections.<OWLAnnotationProperty, List<String>> emptyMap(), this.manager); BidirectionalShortFormProvider bidiLabel = new BidirectionalShortFormProviderAdapter(this.manager.getOntologies(), sfp); ShortFormEntityChecker labelChecker = new ShortFormEntityChecker(bidiLabel); ManchesterOWLSyntaxEditorParser parser = new ManchesterOWLSyntaxEditorParser(this.factory, labelClassExpression); parser.setOWLEntityChecker(labelChecker); parser.setDefaultOntology(this.ontology); return parser; } /** * Save the ontology at the specified location. * The ontology is serialized using Manchester syntax. * @param path * @throws StorageException */ public void save(String path) throws StorageException { File file = new File(path); OWLOntologyFormat format = this.manager.getOntologyFormat(this.ontology); ManchesterOWLSyntaxOntologyFormat manSyntaxFormat = new ManchesterOWLSyntaxOntologyFormat(); if (format.isPrefixOWLOntologyFormat()) { manSyntaxFormat.copyPrefixesFrom(this.prefixManager); } try { this.manager.saveOntology(this.ontology, manSyntaxFormat, IRI.create(file)); } catch (OWLOntologyStorageException e) { throw new StorageException(e); } } /** * Load an external ontology from it's IRI or from a local file * and merge it with the current ontology. * @param pathToOntology * @throws NewOntologyException * @throws ExistingEntityException */ public void learn(String pathToOntology) throws NewOntologyException, ExistingEntityException { OWLOntologyManager newManager = OWLManager.createOWLOntologyManager(); OWLOntology newOnto; if(isExternalEntity(pathToOntology)){ IRI iriOnto = IRI.create(pathToOntology); try { newOnto = newManager.loadOntologyFromOntologyDocument(iriOnto); } catch (OWLOntologyCreationException e) { throw new NewOntologyException(e); } }else{ File file = new File(pathToOntology); try { newOnto = newManager.loadOntologyFromOntologyDocument(file); } catch (OWLOntologyCreationException e) { throw new NewOntologyException(e); } } SimpleShortFormProvider sf = new SimpleShortFormProvider(); Set<OWLOntology> importsClosure = newOnto.getImportsClosure(); BidirectionalShortFormProviderAdapter newBidiShortFormProvider = new BidirectionalShortFormProviderAdapter(newManager, importsClosure, sf); //For each of the new shortForms, checks if it exists already in the existing one. //throws an error if the same entity was found, otherwise carries on. for (String shortFromNewOnto : newBidiShortFormProvider.getShortForms()) { if(this.bidiShortFormProvider.getEntity(shortFromNewOnto) != null){ //If the short form is already known, can be problematic, because multiple short forms can //refer to the same entity. The code below is made to identify this. OWLEntity existingEntity = this.bidiShortFormProvider.getEntity(shortFromNewOnto); OWLEntity newEntity = newBidiShortFormProvider.getEntity(shortFromNewOnto); boolean identicalEntities = false; boolean identicalType = false; boolean identicalIri = false; //Compares the URI of the entities if(newEntity.getIRI().equals(existingEntity.getIRI())){ identicalIri = true; } //Compares their types if(newEntity.getEntityType().equals(existingEntity.getEntityType())){ identicalType = true; if(identicalIri){ //If they have the same type and same name, therefore they are the same entities. identicalEntities = true; } } if(!shortFromNewOnto.equals("Thing") && !identicalEntities){ //A problem was identified: the new short form is not unique and refers in a different already existing entity. String message = "Inside the file you are trying to load (" + pathToOntology + "), there's an entity with the short form '"+ shortFromNewOnto + "' (derived from " + newEntity.getIRI() + ") which already exists in the kwnoledge base.\n" + "This short form currently refers to the existing entity '" + existingEntity.getIRI() + "'.\n" + " Please make sure short forms are unique, otherwise you will have ambiguity problems when" + " running queries later on."; if(!identicalType && identicalIri){ message += " These two entities have the IRI (" + existingEntity.getIRI() + "), but different types. " + "The type of the already existing one is '" + existingEntity.getEntityType() + "' and the type" + " of the one to be learned is '" + newEntity.getEntityType() + "'."; } throw new ExistingEntityException(message); } } } //Keep the prefix information if present OWLOntologyFormat format = newManager.getOntologyFormat(newOnto); if (format.isPrefixOWLOntologyFormat()) { PrefixOWLOntologyFormat newPrefixesFormat = format.asPrefixOWLOntologyFormat(); Set<String> newPrefixes = newPrefixesFormat.getPrefixNames(); for (String prefix : newPrefixes) { if(!this.prefixManager.containsPrefixMapping(prefix)){ prefix(newPrefixesFormat.getPrefix(prefix), prefix.replaceAll(":", "")); } } } //Transfer all the axioms from the old ontology into the new one //Bottleneck this.manager.addAxioms(this.ontology, newOnto.getAxioms()); update(); } /** * Load an external ontology from it's IRI or from a local file * and merge it with the current ontology. In case the shortform is already used, Brain * re-uses the one already existing (first-come, first-served strategy). Brain will try do * disambiguate shortform as much as possible, however, because this process is hacky by nature, the solution * might not be exactly accurate - so double check the results! * @param pathToOntology * @throws NewOntologyException * @throws ExistingEntityException */ public void learnAndDisambiguate(String pathToOntology) throws NewOntologyException, ExistingEntityException { OWLOntologyManager newManager = OWLManager.createOWLOntologyManager(); OWLOntology newOnto; if(isExternalEntity(pathToOntology)){ IRI iriOnto = IRI.create(pathToOntology); try { newOnto = newManager.loadOntologyFromOntologyDocument(iriOnto); } catch (OWLOntologyCreationException e) { throw new NewOntologyException(e); } }else{ File file = new File(pathToOntology); try { newOnto = newManager.loadOntologyFromOntologyDocument(file); } catch (OWLOntologyCreationException e) { throw new NewOntologyException(e); } } SimpleShortFormProvider sf = new SimpleShortFormProvider(); Set<OWLOntology> importsClosure = newOnto.getImportsClosure(); BidirectionalShortFormProviderAdapter newBidiShortFormProvider = new BidirectionalShortFormProviderAdapter(newManager, importsClosure, sf); //For each of the new shortForms, checks if it exists already in the existing one. //throws an error if the same entity was found, otherwise carries on. for (String shortFromNewOnto : newBidiShortFormProvider.getShortForms()) { //check if the shortform is already known, to identify the problematic cases. if(this.bidiShortFormProvider.getEntity(shortFromNewOnto) != null){ //If the short form is already known, can be problematic, because multiple short forms can //refer to the same entity. This method tries to disambiguate the shortform using //a first-come first-served strategy: if an identical shortform is already detected, then it //uses the already existing version rather than the new one, and prints a warning to the user. OWLEntity existingEntity = this.bidiShortFormProvider.getEntity(shortFromNewOnto); OWLEntity newEntity = newBidiShortFormProvider.getEntity(shortFromNewOnto); boolean identicalEntities = false; boolean identicalType = false; boolean identicalIri = false; //Compares the URI of the entities if(newEntity.getIRI().equals(existingEntity.getIRI())){ identicalIri = true; } //Compares their types if(newEntity.getEntityType().equals(existingEntity.getEntityType())){ identicalType = true; if(identicalIri){ //If they have the same type and same name, therefore they are the same entities. //No problem, just carries on with the checking. identicalEntities = true; } } if(!shortFromNewOnto.equals("Thing") && !identicalEntities){ //Should be an error in this case, impossible to disambiguate if(!identicalType && identicalIri){ throw new ExistingEntityException(" These two entities (" + existingEntity.getIRI() + ") have the same IRI, but different types. " + "The type of the already existing one is '" + existingEntity.getEntityType() + "' and the type" + " of the one to be learned is '" + newEntity.getEntityType() + "'."); }else{ //Attempt to disambiguate, the entities have the same shortform and types, but different IRIs //Covert all the new IRIs to the existing one Set<OWLOntology> ontologies = new HashSet<OWLOntology>(); ontologies.add(newOnto); OWLEntityRenamer renamer = new OWLEntityRenamer(newManager, ontologies); IRI existingIri = existingEntity.getIRI(); IRI newIri = newEntity.getIRI(); //Replace the IRI of the conflicting new entity with the IRI already existing in the KB. List<OWLOntologyChange> changes = renamer.changeIRI(newIri, existingIri); //Applies the changes to the ontology newManager.applyChanges(changes); } } } } //Keep the prefix information if present OWLOntologyFormat format = newManager.getOntologyFormat(newOnto); if (format.isPrefixOWLOntologyFormat()) { PrefixOWLOntologyFormat newPrefixesFormat = format.asPrefixOWLOntologyFormat(); Set<String> newPrefixes = newPrefixesFormat.getPrefixNames(); for (String prefix : newPrefixes) { if(!this.prefixManager.containsPrefixMapping(prefix)){ prefix(newPrefixesFormat.getPrefix(prefix), prefix.replaceAll(":", "")); } } } //Transfer all the axioms from the old ontology into the new one //Bottleneck this.manager.addAxioms(this.ontology, newOnto.getAxioms()); update(); } /** * Load another brain object. The axioms are extracted * and merged with the current one. * * @throws NewOntologyException * @throws ExistingEntityException */ public void learn(Brain brainToLearn) throws ExistingEntityException { for (String shortFromNewOnto : brainToLearn.getBidiShortFormProvider().getShortForms()) { if(this.bidiShortFormProvider.getEntity(shortFromNewOnto) != null){ OWLEntity existingEntity = this.bidiShortFormProvider.getEntity(shortFromNewOnto); OWLEntity newEntity = brainToLearn.getBidiShortFormProvider().getEntity(shortFromNewOnto); boolean identicalEntities = false; if(newEntity.getIRI().equals(existingEntity.getIRI()) && newEntity.getEntityType().equals(existingEntity.getEntityType())){ identicalEntities = true; } if(!shortFromNewOnto.equals("Thing") && !identicalEntities){ throw new ExistingEntityException("The entity '"+shortFromNewOnto+"' already exists and is of different type or has a" + "different prefix."); } } } //Keep the prefix information if present for (String prefix : brainToLearn.getPrefixManager().getPrefixNames()) { if(!this.prefixManager.containsPrefixMapping(prefix)){ prefix(brainToLearn.getPrefixManager().getPrefix(prefix), prefix.replaceAll(":", "")); } } this.manager.addAxioms(this.ontology, brainToLearn.getOntology().getAxioms()); update(); } /** * Classify the ontology. It is usually the most expensive * operation, so use it carefully! */ public void classify() { //Maybe this is performance bottleneck. Elk implements as in 0.3.2 an lazy way to trigger the //reasoning. Inside Brain the idea is to handle the reasoning separately, so we have to trigger the //reclassification for each type. //this.reasoner.precomputeInferences(InferenceType.CLASS_HIERARCHY); //this.reasoner.precomputeInferences(InferenceType.CLASS_ASSERTIONS); this.reasoner.precomputeInferences(); this.isClassified = true; } /** * Test whether the ontology is following an OWL 2 EL profile or not. * @return hasElProfile */ public boolean hasElProfile() { OWL2ELProfile profile = new OWL2ELProfile(); OWLProfileReport report = profile.checkOntology(this.ontology); if(report.getViolations().size() == 0){ return true; }else{ return false; } } /** * Returns the list of violations of the OWL 2 EL profile. * @return violations */ public List<String> getElProfileViolations() { OWL2ELProfile profile = new OWL2ELProfile(); OWLProfileReport report = profile.checkOntology(this.ontology); ArrayList<String> violations = new ArrayList<String>(); if(report.getViolations().size() != 0){ for (OWLProfileViolation violation : report.getViolations()) { violations.add(violation.toString()); } } return violations; } /** * Returns the list of subclasses from the expression. * The second parameter is a flag telling whether only the direct classes * should be returned. The method is synchronized in order * to avoid concurrency problems that could arise in threaded * environment (such as a web server with a shared Brain instance). * @param classExpression * @param direct * @return subClasses * @throws ClassExpressionException */ public synchronized List<String> getSubClasses(String classExpression, boolean direct) throws ClassExpressionException { OWLClassExpression owlClassExpression = parseClassExpression(classExpression); return getSubClasses(owlClassExpression, direct); } /** * Returns the list of subclasses from the OWL expression. * The second parameter is a flag telling whether only the direct classes * should be returned. * @param owlClassExpression * @param direct * @return subClasses */ private List<String> getSubClasses(OWLClassExpression owlClassExpression, boolean direct) { Set<OWLClass> subClasses = null; //Can be simplified once Elk would have implemented a better way to deal with anonymous classes if(owlClassExpression.isAnonymous()){ OWLClass anonymousClass = getTemporaryAnonymousClass(owlClassExpression); this.classify(); subClasses = this.reasoner.getSubClasses(anonymousClass, direct).getFlattened(); removeTemporaryAnonymousClass(anonymousClass); }else{ if(!this.isClassified){ this.classify(); } subClasses = this.reasoner.getSubClasses(owlClassExpression, direct).getFlattened(); } return sortClasses(subClasses); } /** * Returns the list of subclasses from the label expression. * The second parameter is a flag telling whether only the direct classes * should be returned. The method is synchronized in order * to avoid concurrency problems that could arise in threaded * environment (such as a web server with a shared Brain instance). * @param labelClassExpression * @param direct * @return subClasses * @throws ClassExpressionException */ public synchronized List<String> getSubClassesFromLabel(String labelClassExpression, boolean direct) throws ClassExpressionException { OWLClassExpression owlClassExpression = parseLabelClassExpression(labelClassExpression); return getSubClasses(owlClassExpression, direct); } /** * Returns the list of super classes from the expression. * The second parameter is a flag telling whether only the direct classes * should be returned. The method is synchronized in order * to avoid concurrency problems that could arise in threaded * environment (such as a web server with a shared Brain instance). * @param classExpression * @param direct * @return superClasses * @throws ClassExpressionException */ public synchronized List<String> getSuperClasses(String classExpression, boolean direct) throws ClassExpressionException { OWLClassExpression owlClassExpression = parseClassExpression(classExpression); return getSuperClasses(owlClassExpression, direct); } /** * Returns the list of super classes from the OWL expression. * The second parameter is a flag telling whether only the direct classes * should be returned. * @param owlClassExpression * @param direct * @return subClasses */ private List<String> getSuperClasses(OWLClassExpression owlClassExpression, boolean direct) { Set<OWLClass> superClasses = null; //Can be simplified once Elk would have implemented a better way to deal with anonymous classes if(owlClassExpression.isAnonymous()){ OWLClass anonymousClass = getTemporaryAnonymousClass(owlClassExpression); this.classify(); superClasses = this.reasoner.getSuperClasses(anonymousClass, direct).getFlattened(); removeTemporaryAnonymousClass(anonymousClass); }else{ if(!this.isClassified){ this.classify(); } superClasses = this.reasoner.getSuperClasses(owlClassExpression, direct).getFlattened(); } return sortClasses(superClasses); } /** * Returns the list of super classes from the label expression. * The second parameter is a flag telling whether only the direct classes * should be returned. The method is synchronized in order * to avoid concurrency problems that could arise in threaded * environment (such as a web server with a shared Brain instance). * @param labelClassExpression * @param direct * @return superClasses * @throws ClassExpressionException */ public synchronized List<String> getSuperClassesFromLabel(String labelClassExpression, boolean direct) throws ClassExpressionException { OWLClassExpression owlClassExpression = parseLabelClassExpression(labelClassExpression); return getSuperClasses(owlClassExpression, direct); } /** * Retrieves the named equivalent classes corresponding to the class expression. * The method is synchronized in order to avoid concurrency problems * that could arise in threaded * environment (such as a web server with a shared Brain instance). * @return equivalentClasses * @throws ClassExpressionException * @throws ClassExpressionException */ public synchronized List<String> getEquivalentClasses(String classExpression) throws ClassExpressionException { OWLClassExpression owlClassExpression = parseClassExpression(classExpression); return getEquivalentClasses(owlClassExpression); } /** * Returns the list of equivalent classes from the OWL expression. * should be returned. * @param owlClassExpression * @return equivalentClasses */ private List<String> getEquivalentClasses(OWLClassExpression owlClassExpression) { Set<OWLClass> equivalentClasses = null; //Can be simplified once Elk would have implemented a better way to deal with anonymous classes if(owlClassExpression.isAnonymous()){ OWLClass anonymousClass = getTemporaryAnonymousClass(owlClassExpression); this.classify(); equivalentClasses = this.reasoner.getEquivalentClasses(anonymousClass).getEntitiesMinus(anonymousClass); removeTemporaryAnonymousClass(anonymousClass); }else{ if(!this.isClassified){ this.classify(); } equivalentClasses = this.reasoner.getEquivalentClasses(owlClassExpression).getEntitiesMinus((OWLClass) owlClassExpression); } return sortClasses(equivalentClasses); } /** * Retrieves the named equivalent classes corresponding to the label class expression. * The method is synchronized in order to avoid concurrency problems * that could arise in threaded * environment (such as a web server with a shared Brain instance). * @return equivalentClasses * @throws ClassExpressionException * @throws ClassExpressionException */ public synchronized List<String> getEquivalentClassesFromLabel(String labelClassExpression) throws ClassExpressionException { OWLClassExpression owlClassExpression = parseLabelClassExpression(labelClassExpression); return getEquivalentClasses(owlClassExpression); } /** * Removes any class. Should be used to remove temporary classes used for expressions only at the moment. * @param anonymousClass */ private void removeTemporaryAnonymousClass(OWLClass anonymousClass) { OWLEntityRemover remover = new OWLEntityRemover(this.manager, Collections.singleton(this.ontology)); anonymousClass.accept(remover); this.manager.applyChanges(remover.getChanges()); remover.reset(); update(); } /** * Add a temporary class to the ontology based on the expression. Helpful because Elk doesn't support * anonymous queries at the moment. To be removed in the future. * @param classExpression */ private OWLClass getTemporaryAnonymousClass(OWLClassExpression classExpression) { IRI anonymousIri = IRI.create("temp"); int counter = 0; while (this.ontology.containsClassInSignature(anonymousIri)) { anonymousIri = IRI.create("temp" + counter); counter++; } OWLClass anonymousClass = this.factory.getOWLClass(anonymousIri); OWLEquivalentClassesAxiom equivalenceAxiom = this.factory.getOWLEquivalentClassesAxiom(anonymousClass, classExpression); addAxiom(equivalenceAxiom); this.reasoner.flush(); return anonymousClass; } /** * Sort the classes based on their short forms. * @param classes * @return listClasses */ private List<String> sortClasses(Set<OWLClass> classes) { List<String> listClasses = new ArrayList<String>(); for (OWLClass owlClass : classes) { if(!owlClass.isOWLNothing()){ listClasses.add(this.bidiShortFormProvider.getShortForm(owlClass)); } } Collections.sort(listClasses); return listClasses; } /** * Sort the individuals based on their short forms. * @param individuals * @return listIndividuals */ private List<String> sortInstances(Set<OWLNamedIndividual> individuals) { List<String> listIndividuals = new ArrayList<String>(); for (OWLNamedIndividual owlIndividual : individuals) { listIndividuals.add(this.bidiShortFormProvider.getShortForm(owlIndividual)); } Collections.sort(listIndividuals); return listIndividuals; } /** * Returns the list of individuals from the expression. * The second parameter is a flag telling whether only the individuals * should be returned. The method is synchronized in order * to avoid concurrency problems that could arise in threaded * environment (such as a web server with a shared Brain instance). * @param classExpression * @param direct * @return individuals * @throws ClassExpressionException */ public synchronized List<String> getInstances(String classExpression, boolean direct) throws ClassExpressionException { OWLClassExpression owlClassExpression = parseClassExpression(classExpression); return getInstances(owlClassExpression, direct); } /** * Returns the list of named individuals from the OWL expression. * The second parameter is a flag telling whether only the direct named individuals * should be returned. * @param owlClassExpression * @param direct * @return instances */ private List<String> getInstances(OWLClassExpression owlClassExpression, boolean direct) { Set<OWLNamedIndividual> instances = null; //Can be simplified once Elk would have implemented a better way to deal with anonymous classes if(owlClassExpression.isAnonymous()){ OWLClass anonymousClass = getTemporaryAnonymousClass(owlClassExpression); this.classify(); instances = this.reasoner.getInstances(anonymousClass, direct).getFlattened(); removeTemporaryAnonymousClass(anonymousClass); }else{ if(!this.isClassified){ this.classify(); } instances = this.reasoner.getInstances(owlClassExpression, direct).getFlattened(); } return sortInstances(instances); } /** * Returns the list of instances (named individuals) from the label expression. * The second parameter is a flag telling whether only the direct individuals * should be returned. The method is synchronized in order * to avoid concurrency problems that could arise in threaded * environment (such as a web server with a shared Brain instance). * @param labelClassExpression * @param direct * @return instances * @throws ClassExpressionException */ public synchronized List<String> getInstancesFromLabel(String labelClassExpression, boolean direct) throws ClassExpressionException { OWLClassExpression owlClassExpression = parseLabelClassExpression(labelClassExpression); return getInstances(owlClassExpression, direct); } /** * Test whether an OWLClass is the subclass of an other. * @param presumedSubClass * @param presumedSuperClass * @param direct * @return isASubClass * @throws ClassExpressionException */ public boolean isSubClass(String presumedSubClass, String presumedSuperClass, boolean direct) throws ClassExpressionException { List<String> subClasses = this.getSubClasses(presumedSuperClass, direct); boolean contained = subClasses.contains(presumedSubClass); return contained; } /** * Test whether an OWLClass is the super class of an other. * @param presumedSuperClass * @param presumedSubClass * @param direct * @return isASuperClass * @throws ClassExpressionException */ public boolean isSuperClass(String presumedSuperClass, String presumedSubClass, boolean direct) throws ClassExpressionException { List<String> superClasses = this.getSubClasses(presumedSuperClass, direct); boolean contained = superClasses.contains(presumedSubClass); return contained; } /** * Returns the unsatisfiable classes (aka no possible instances). * @return unsatisfiableClasses */ public List<String> getUnsatisfiableClasses() { if(!this.isClassified){ this.classify(); } Set<OWLClass> unsatisfiableClasses = this.reasoner.getUnsatisfiableClasses().getEntitiesMinusBottom(); return sortClasses(unsatisfiableClasses); } /** * Free the resources used by the reasoner. * Once this method is called, the reasoner is destroyed and replaced * by a fresh one. Re-classification is needed before re-querying. */ public void sleep() { this.reasoner.flush(); this.reasoner.dispose(); if(this.configuration == null){ this.reasoner = this.getReasonerFactory().createReasoner(this.ontology); }else{ this.reasoner = this.getReasonerFactory().createReasoner(this.ontology, this.configuration); } this.isClassified = false; } /** * Checks if the class (short form) is already inside the brain. Useful while * parsing in order to avoid errors. * @return whether the class is known or not. */ public boolean knowsClass(String owlClass) { try { this.getOWLClass(owlClass); return true; } catch (NonExistingClassException e) { return false; } } /** * Checks if the object property (short form) is already inside the brain. Useful while * parsing in order to avoid errors. * @return whether the class is known or not. */ public boolean knowsObjectProperty(String owlObjectProperty) { try { this.getOWLObjectProperty(owlObjectProperty); return true; }catch(NonExistingObjectPropertyException e){ return false; } } /** * Checks if the data property is already inside the brain. Useful while * parsing in order to avoid errors. * @return whether the class is known or not. */ public boolean knowsDataProperty(String owlDataProperty) { try { this.getOWLDataProperty(owlDataProperty); return true; }catch(NonExistingDataPropertyException e){ return false; } } /** * Checks if the annotation property is already inside the brain. Useful while * parsing in order to avoid errors. * @return whether the class is known or not. */ public boolean knowsAnnotationProperty(String owlAnnotationProperty) { try { this.getOWLAnnotationProperty(owlAnnotationProperty); return true; }catch(NonExistingAnnotationPropertyException e){ return false; } } /** * Calculates the Jaccard index between two classes of the ontology. * The super classes of each classes are compared... * http://en.wikipedia.org/wiki/Jaccard_index * http://webwhompers.com/set-theory.html * @return The Jaccard index * @throws ClassExpressionException */ //TODO finish the doc public float getJaccardSimilarityIndex(String class1, String class2) throws ClassExpressionException { float intersection = 0; List<String> superClasses1 = this.getSuperClasses(class1, false); superClasses1.add(class1); List<String> superClasses2 = this.getSuperClasses(class2, false); superClasses2.add(class2); float sizeSet1 = 0; float sizeSet2 = superClasses2.size(); for (String superClass1 : superClasses1) { if(superClasses2.contains(superClass1)){ intersection++; sizeSet2--; }else{ sizeSet1++; } } float union = intersection + sizeSet2 + sizeSet1; float index = intersection/union; return index; } //TODO doc //Groupwise similarity coefficient public float getJaccardSimilarityIndex(List<String> classes1, List<String> classes2) throws ClassExpressionException { List<String> allClasses1 = new ArrayList<String>(); List<String> allClasses2 = new ArrayList<String>(); for (String class1 : classes1) { List<String> superClasses1 = this.getSuperClasses(class1, false); superClasses1.add(class1); for (String superClass1 : superClasses1) { if(!allClasses1.contains(superClass1)){ allClasses1.add(superClass1); } } } for (String class2 : classes2) { List<String> superClasses2 = this.getSuperClasses(class2, false); superClasses2.add(class2); for (String superClass2 : superClasses2) { if(!allClasses2.contains(superClass2)){ allClasses2.add(superClass2); } } } float intersection = 0; float sizeSet1 = 0; float sizeSet2 = allClasses2.size(); for (String superClass1 : allClasses1) { if(allClasses2.contains(superClass1)){ intersection++; sizeSet2--; }else{ sizeSet1++; } } float union = intersection + sizeSet2 + sizeSet1; float index = intersection/union; return index; } }