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

uk.ac.manchester.cs.owl.owlapi.OWLOntologyManagerImpl Maven / Gradle / Ivy

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

import static org.semanticweb.owlapi.model.parameters.Imports.INCLUDED;
import static org.semanticweb.owlapi.util.CollectionFactory.createSyncList;
import static org.semanticweb.owlapi.util.CollectionFactory.createSyncMap;
import static org.semanticweb.owlapi.util.OWLAPIPreconditions.checkNotNull;
import static org.semanticweb.owlapi.util.OWLAPIPreconditions.verifyNotNull;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.function.Supplier;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.inject.Inject;

import org.semanticweb.owlapi.OWLAPIConfigProvider;
import org.semanticweb.owlapi.io.FileDocumentSource;
import org.semanticweb.owlapi.io.IRIDocumentSource;
import org.semanticweb.owlapi.io.OWLOntologyDocumentSource;
import org.semanticweb.owlapi.io.OWLOntologyDocumentSourceBase;
import org.semanticweb.owlapi.io.OWLOntologyDocumentTarget;
import org.semanticweb.owlapi.io.OWLOntologyStorageIOException;
import org.semanticweb.owlapi.io.OWLParserFactory;
import org.semanticweb.owlapi.io.OntologyIRIMappingNotFoundException;
import org.semanticweb.owlapi.io.StreamDocumentSource;
import org.semanticweb.owlapi.io.StreamDocumentTarget;
import org.semanticweb.owlapi.model.AddAxiom;
import org.semanticweb.owlapi.model.AddImport;
import org.semanticweb.owlapi.model.AddOntologyAnnotation;
import org.semanticweb.owlapi.model.AxiomType;
import org.semanticweb.owlapi.model.ChangeDetails;
import org.semanticweb.owlapi.model.DefaultChangeBroadcastStrategy;
import org.semanticweb.owlapi.model.DefaultImpendingChangeBroadcastStrategy;
import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.model.ImmutableOWLOntologyChangeException;
import org.semanticweb.owlapi.model.ImpendingOWLOntologyChangeBroadcastStrategy;
import org.semanticweb.owlapi.model.ImpendingOWLOntologyChangeListener;
import org.semanticweb.owlapi.model.MissingImportEvent;
import org.semanticweb.owlapi.model.MissingImportHandlingStrategy;
import org.semanticweb.owlapi.model.MissingImportListener;
import org.semanticweb.owlapi.model.OWLAnnotation;
import org.semanticweb.owlapi.model.OWLAnnotationAxiom;
import org.semanticweb.owlapi.model.OWLAnnotationProperty;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLAxiomChange;
import org.semanticweb.owlapi.model.OWLDataFactory;
import org.semanticweb.owlapi.model.OWLDeclarationAxiom;
import org.semanticweb.owlapi.model.OWLDocumentFormat;
import org.semanticweb.owlapi.model.OWLDocumentFormatImpl;
import org.semanticweb.owlapi.model.OWLEntity;
import org.semanticweb.owlapi.model.OWLImportsDeclaration;
import org.semanticweb.owlapi.model.OWLLogicalAxiom;
import org.semanticweb.owlapi.model.OWLMutableOntology;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyAlreadyExistsException;
import org.semanticweb.owlapi.model.OWLOntologyChange;
import org.semanticweb.owlapi.model.OWLOntologyChangeBroadcastStrategy;
import org.semanticweb.owlapi.model.OWLOntologyChangeListener;
import org.semanticweb.owlapi.model.OWLOntologyChangeProgressListener;
import org.semanticweb.owlapi.model.OWLOntologyChangeVetoException;
import org.semanticweb.owlapi.model.OWLOntologyChangesVetoedListener;
import org.semanticweb.owlapi.model.OWLOntologyCreationException;
import org.semanticweb.owlapi.model.OWLOntologyDocumentAlreadyExistsException;
import org.semanticweb.owlapi.model.OWLOntologyFactory;
import org.semanticweb.owlapi.model.OWLOntologyFactoryNotFoundException;
import org.semanticweb.owlapi.model.OWLOntologyID;
import org.semanticweb.owlapi.model.OWLOntologyIRIMapper;
import org.semanticweb.owlapi.model.OWLOntologyLoaderConfiguration;
import org.semanticweb.owlapi.model.OWLOntologyLoaderListener;
import org.semanticweb.owlapi.model.OWLOntologyManager;
import org.semanticweb.owlapi.model.OWLOntologyRenameException;
import org.semanticweb.owlapi.model.OWLOntologyStorageException;
import org.semanticweb.owlapi.model.OWLRuntimeException;
import org.semanticweb.owlapi.model.OWLStorer;
import org.semanticweb.owlapi.model.OWLStorerFactory;
import org.semanticweb.owlapi.model.OWLStorerNotFoundException;
import org.semanticweb.owlapi.model.PriorityCollectionSorting;
import org.semanticweb.owlapi.model.RemoveAxiom;
import org.semanticweb.owlapi.model.RemoveImport;
import org.semanticweb.owlapi.model.SetOntologyID;
import org.semanticweb.owlapi.model.UnknownOWLOntologyException;
import org.semanticweb.owlapi.model.UnloadableImportException;
import org.semanticweb.owlapi.model.parameters.ChangeApplied;
import org.semanticweb.owlapi.model.parameters.OntologyCopy;
import org.semanticweb.owlapi.util.CollectionFactory;
import org.semanticweb.owlapi.util.OWLAnnotationPropertyTransformer;
import org.semanticweb.owlapi.util.PriorityCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import uk.ac.manchester.cs.owl.owlapi.concurrent.ConcurrentPriorityCollection;

/**
 * @author Matthew Horridge, The University Of Manchester, Bio-Health Informatics Group
 * @since 2.0.0
 */
public class OWLOntologyManagerImpl
    implements OWLOntologyManager, OWLOntologyFactory.OWLOntologyCreationHandler, Serializable {

    private static final String BADLY_BEHAVING_LISTENER_HAS_BEEN_REMOVED =
        "BADLY BEHAVING LISTENER: {} has been removed";
    private static final long serialVersionUID = 40000L;
    private static final Logger LOGGER = LoggerFactory.getLogger(OWLOntologyManagerImpl.class);
    @Nonnull
    protected final Map ontologiesByID = createSyncMap();
    @Nonnull
    protected final Map documentIRIsByID = createSyncMap();
    @Nonnull
    protected final Map ontologyConfigurationsByOntologyID =
        new HashMap<>();
    @Nonnull
    protected final Map ontologyFormatsByOntology =
        createSyncMap();
    @Nonnull
    protected final Map ontologyIDsByImportsDeclaration =
        createSyncMap();
    protected final AtomicInteger loadCount = new AtomicInteger(0);
    protected final AtomicInteger importsLoadCount = new AtomicInteger(0);
    @Nonnull
    protected final Map importedIRIs = createSyncMap();
    @Nonnull
    protected final OWLDataFactory dataFactory;
    @Nonnull
    protected final Map> importsClosureCache = createSyncMap();
    @Nonnull
    protected final List missingImportsListeners = createSyncList();
    @Nonnull
    protected final List loaderListeners = createSyncList();
    @Nonnull
    protected final List progressListeners = createSyncList();
    @Nonnull
    protected final AtomicLong autoGeneratedURICounter = new AtomicLong();
    private final AtomicBoolean broadcastChanges = new AtomicBoolean(true);
    @Nonnull
    protected OWLOntologyChangeBroadcastStrategy defaultChangeBroadcastStrategy =
        new DefaultChangeBroadcastStrategy();
    @Nonnull
    protected ImpendingOWLOntologyChangeBroadcastStrategy defaultImpendingChangeBroadcastStrategy =
        new DefaultImpendingChangeBroadcastStrategy();
    private transient Map listenerMap =
        createSyncMap();
    private transient Map impendingChangeListenerMap =
        createSyncMap();
    private transient List vetoListeners = new ArrayList<>();
    @Nonnull
    private Supplier configProvider = new OWLAPIConfigProvider();
    private OWLOntologyLoaderConfiguration config = null;
    @Nonnull
    protected final PriorityCollection documentMappers;
    @Nonnull
    protected final PriorityCollection ontologyFactories;
    @Nonnull
    protected final PriorityCollection parserFactories;
    @Nonnull
    protected final PriorityCollection ontologyStorers;
    private final Lock readLock;
    private final Lock writeLock;
    private final ReadWriteLock lock;

    /**
     * @param dataFactory data factory
     * @param readWriteLock lock
     */
    @Inject
    public OWLOntologyManagerImpl(@Nonnull OWLDataFactory dataFactory,
        ReadWriteLock readWriteLock) {
        this(dataFactory, readWriteLock, PriorityCollectionSorting.ON_SET_INJECTION_ONLY);
    }

    /**
     * @param dataFactory data factory
     * @param readWriteLock lock
     * @param sorting sorting approach
     */
    public OWLOntologyManagerImpl(@Nonnull OWLDataFactory dataFactory, ReadWriteLock readWriteLock,
        PriorityCollectionSorting sorting) {
        this.dataFactory = checkNotNull(dataFactory, "dataFactory cannot be null");
        readLock = readWriteLock.readLock();
        writeLock = readWriteLock.writeLock();
        lock = readWriteLock;
        documentMappers = new ConcurrentPriorityCollection<>(readWriteLock, sorting);
        ontologyFactories = new ConcurrentPriorityCollection<>(readWriteLock, sorting);
        parserFactories = new ConcurrentPriorityCollection<>(readWriteLock, sorting);
        ontologyStorers = new ConcurrentPriorityCollection<>(readWriteLock, sorting);
        installDefaultURIMappers();
        installDefaultOntologyFactories();
    }

    /**
     * @param first first id
     * @param second second id
     * @return true if the ids are equal or have the same ontology IRI. Ontology version is ignored.
     */
    private static boolean matchingIDs(OWLOntologyID first, OWLOntologyID second) {
        if (first.isAnonymous() || second.isAnonymous()) {
            return first.equals(second);
        }
        return first.getOntologyIRI().equals(second.getOntologyIRI());
    }

    @Nonnull
    protected static IRI getNextAutoGeneratedIRI() {
        return OWLOntologyDocumentSourceBase.getNextDocumentIRI("urn:unnamed:ontology#ont");
    }

    @Override
    public void setOntologyLoaderConfigurationProvider(
        Supplier provider) {
        writeLock.lock();
        try {
            configProvider = provider;
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    @Nonnull
    public OWLOntologyLoaderConfiguration getOntologyLoaderConfiguration() {
        readLock.lock();
        try {
            if (config != null) {
                return config;
            }
            config = configProvider.get();
            return config;
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public void setOntologyLoaderConfiguration(OWLOntologyLoaderConfiguration newConfig) {
        writeLock.lock();
        try {
            config = newConfig;
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    public OWLDataFactory getOWLDataFactory() {
        return dataFactory;
    }

    @Override
    public Set getOntologies() {
        readLock.lock();
        try {
            return new HashSet<>(ontologiesByID.values());
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public Set getOntologies(OWLAxiom axiom) {
        readLock.lock();
        try {
            Set result = new HashSet<>(ontologiesByID.size());
            for (OWLOntology ont : getOntologies()) {
                if (ont.containsAxiom(axiom)) {
                    result.add(ont);
                }
            }
            return result;
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public boolean contains(OWLOntology ontology) {
        readLock.lock();
        try {
            return ontologiesByID.containsValue(ontology);
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public boolean contains(IRI ontologyIRI) {
        checkNotNull(ontologyIRI, "Ontology IRI cannot be null");
        readLock.lock();
        try {
            Set owlOntologyIDs = ontologiesByID.keySet();
            for (OWLOntologyID nextOntologyID : owlOntologyIDs) {
                if (ontologyIRI.equals(nextOntologyID.getOntologyIRI().orNull())) {
                    return true;
                }
            }
            for (OWLOntologyID ont : owlOntologyIDs) {
                if (ontologyIRI.equals(ont.getVersionIRI().orNull())) {
                    return true;
                }
            }
            // FIXME:
            // ParsableOWLOntologyFactory seems to call this method with a
            // document/physical IRI,
            // but this method fails the general case where the ontology was
            // loaded
            // from the given IRI directly, but was then renamed
            return false;
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public boolean contains(OWLOntologyID id) {
        readLock.lock();
        try {
            if (ontologiesByID.containsKey(id)) {
                return true;
            }
            for (OWLOntologyID nextOntologyID : ontologiesByID.keySet()) {
                if (!id.isAnonymous()
                    && id.getOntologyIRI().equals(nextOntologyID.getOntologyIRI())) {
                    return true;
                }
            }
            return false;
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public boolean containsVersion(IRI ontologyVersionIRI) {
        readLock.lock();
        try {
            for (OWLOntologyID ont : ontologiesByID.keySet()) {
                if (ontologyVersionIRI.equals(ont.getVersionIRI().orNull())) {
                    return true;
                }
            }
            return false;
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public Set getOntologyIDsByVersion(IRI ontologyVersionIRI) {
        readLock.lock();
        try {
            Set result = new TreeSet<>();
            for (OWLOntologyID ont : ontologiesByID.keySet()) {
                if (ontologyVersionIRI.equals(ont.getVersionIRI().orNull())) {
                    result.add(ont);
                }
            }
            return result;
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public OWLOntology getOntology(IRI ontologyIRI) {
        readLock.lock();
        try {
            OWLOntologyID ontologyID = new OWLOntologyID(ontologyIRI, null);
            OWLOntology result = ontologiesByID.get(ontologyID);
            if (result == null) {
                for (OWLOntologyID nextOntologyID : ontologiesByID.keySet()) {
                    if (ontologyIRI.equals(nextOntologyID.getVersionIRI().orNull())
                        || ontologyIRI.equals(nextOntologyID.getOntologyIRI().orNull())
                        || ontologyIRI.equals(nextOntologyID.getDefaultDocumentIRI().orNull())) {
                        result = ontologiesByID.get(nextOntologyID);
                    }
                }
            }
            return result;
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public OWLOntology getOntology(OWLOntologyID ontologyID) {
        readLock.lock();
        try {
            OWLOntology result = ontologiesByID.get(ontologyID);
            if (result == null && !ontologyID.isAnonymous()) {
                for (OWLOntologyID nextOntologyID : ontologiesByID.keySet()) {
                    if (matchingIDs(ontologyID, nextOntologyID)) {
                        result = ontologiesByID.get(nextOntologyID);
                    }
                }
            }
            // HACK: This extra clause is necessary to make getOntology match
            // the
            // behaviour of createOntology in cases where a documentIRI has been
            // recorded, based on the mappers, but an ontology has not been
            // stored
            // in ontologiesByID
            if (result == null) {
                IRI documentIRI = getDocumentIRIFromMappers(ontologyID);
                if (documentIRI == null) {
                    if (!ontologyID.isAnonymous()) {
                        documentIRI = ontologyID.getDefaultDocumentIRI().orNull();
                    } else {
                        documentIRI = IRI.generateDocumentIRI();
                    }
                    Collection existingDocumentIRIs = documentIRIsByID.values();
                    while (existingDocumentIRIs.contains(documentIRI)) {
                        documentIRI = IRI.generateDocumentIRI();
                    }
                }
                if (documentIRIsByID.values().contains(documentIRI)) {
                    throw new OWLRuntimeException(
                        new OWLOntologyDocumentAlreadyExistsException(documentIRI));
                }
            }
            return result;
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public Set getVersions(IRI ontologyIRI) {
        readLock.lock();
        try {
            Set result = new HashSet<>();
            for (OWLOntology ont : getOntologies()) {
                OWLOntologyID ontId = ont.getOntologyID();
                if (ontId.getOntologyIRI().isPresent()
                    && ontId.getOntologyIRI().get().equals(ontologyIRI)) {
                    result.add(ont);
                }
            }
            return result;
        } finally {
            readLock.unlock();
        }
    }

    @Nullable
    @Override
    public OWLOntology getImportedOntology(OWLImportsDeclaration declaration) {
        readLock.lock();
        try {
            OWLOntologyID ontologyID = ontologyIDsByImportsDeclaration.get(declaration);
            if (ontologyID == null) {
                // No such ontology has been loaded through an import
                // declaration, but it might have been loaded manually.
                // Using the IRI to retrieve it will either find the ontology or
                // return null.
                // Last possibility is an import by document IRI; if the
                // ontology is not found by IRI, check by document IRI.
                OWLOntology ontology = getOntology(declaration.getIRI());
                if (ontology == null) {
                    ontology = getOntologyByDocumentIRI(declaration.getIRI());
                }
                return ontology;
            } else {
                return getOntology(ontologyID);
            }
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public Set getDirectImports(OWLOntology ontology) {
        readLock.lock();
        try {
            if (!contains(ontology)) {
                throw new UnknownOWLOntologyException(ontology.getOntologyID());
            }
            Set imports = new HashSet<>();
            for (OWLImportsDeclaration axiom : ontology.getImportsDeclarations()) {
                assert axiom != null;
                OWLOntology importedOntology = getImportedOntology(axiom);
                if (importedOntology != null) {
                    imports.add(importedOntology);
                }
            }
            return imports;
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public Set getImports(OWLOntology ontology) {
        readLock.lock();
        try {
            if (!contains(ontology)) {
                throw new UnknownOWLOntologyException(ontology.getOntologyID());
            }
            Set result = new TreeSet<>();
            getImports(ontology, result);
            return result;
        } finally {
            readLock.unlock();
        }
    }

    /**
     * A method that gets the imports of a given ontology.
     * 
     * @param ont The ontology whose (transitive) imports are to be retrieved.
     * @param result A place to store the result - the transitive closure of the imports will be
     *        stored in this result set.
     */
    private void getImports(@Nonnull OWLOntology ont, @Nonnull Set result) {
        readLock.lock();
        try {
            for (OWLOntology directImport : getDirectImports(ont)) {
                assert directImport != null;
                if (result.add(directImport)) {
                    getImports(directImport, result);
                }
            }
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public Set getImportsClosure(OWLOntology ontology) {
        readLock.lock();
        try {
            Set ontologies = importsClosureCache.get(ontology.getOntologyID());
            if (ontologies == null) {
                ontologies = new TreeSet<>();
                getImportsClosure(ontology, ontologies);
                // store the wrapped set
                importsClosureCache.put(ontology.getOntologyID(), ontologies);
            }
            // the returned set can be mutated, but changes will not be
            // propagated
            // back
            return CollectionFactory.getCopyOnRequestSetFromMutableCollection(ontologies);
        } finally {
            readLock.unlock();
        }
    }

    /**
     * A recursive method that gets the reflexive transitive closure of the ontologies that are
     * imported by this ontology.
     * 
     * @param ontology The ontology whose reflexive transitive closure is to be retrieved
     * @param ontologies a place to store the result
     */
    private void getImportsClosure(@Nonnull OWLOntology ontology,
        @Nonnull Set ontologies) {
        readLock.lock();
        try {
            ontologies.add(ontology);
            for (OWLOntology ont : getDirectImports(ontology)) {
                assert ont != null;
                if (!ontologies.contains(ont)) {
                    getImportsClosure(ont, ontologies);
                }
            }
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public List getSortedImportsClosure(OWLOntology ontology) {
        readLock.lock();
        try {
            return new ArrayList<>(ontology.getImportsClosure());
        } finally {
            readLock.unlock();
        }
    }

    /**
     * Determines if a change is applicable. A change may not be applicable for a number of reasons.
     * 
     * @param change The change to be tested.
     * @return {@code true} if the change is applicable, otherwise, {@code false}.
     */
    private boolean isChangeApplicable(OWLOntologyChange change) {
        OWLOntologyLoaderConfiguration ontologyConfig =
            ontologyConfigurationsByOntologyID.get(change.getOntology().getOntologyID());
        if (ontologyConfig != null && !ontologyConfig.isLoadAnnotationAxioms()
            && change.isAddAxiom() && change.getAxiom() instanceof OWLAnnotationAxiom) {
            return false;
        }
        return true;
    }

    /**
     * Applies a change to an ontology and performs the necessary housekeeping tasks.
     * 
     * @param change The change to be applied.
     * @return A list of changes that were actually applied.
     */
    private ChangeApplied enactChangeApplication(OWLOntologyChange change) {
        if (!isChangeApplicable(change)) {
            return ChangeApplied.UNSUCCESSFULLY;
        }
        OWLOntology ont = change.getOntology();
        if (!(ont instanceof OWLMutableOntology)) {
            throw new ImmutableOWLOntologyChangeException(change.getChangeData(), ont.toString());
        }
        checkForOntologyIDChange(change);
        ChangeApplied appliedChange = ((OWLMutableOntology) ont).applyChange(change);
        checkForImportsChange(change);
        return appliedChange;
    }

    @Override
    public ChangeApplied applyChanges(List changes) {
        writeLock.lock();
        try {
            try {
                broadcastImpendingChanges(changes);
            } catch (OWLOntologyChangeVetoException e) {
                // Some listener blocked the changes.
                broadcastOntologyChangesVetoed(changes, e);
                return ChangeApplied.UNSUCCESSFULLY;
            }
            boolean rollbackRequested = false;
            boolean allNoOps = true;
            // list of changes applied successfully. These are the changes that
            // will be reverted in case of a rollback
            List appliedChanges = new ArrayList<>();
            fireBeginChanges(changes.size());
            for (OWLOntologyChange change : changes) {
                // once rollback is requested by a failed change, do not carry
                // out any more changes
                if (!rollbackRequested) {
                    assert change != null;
                    ChangeApplied enactChangeApplication = enactChangeApplication(change);
                    if (enactChangeApplication == ChangeApplied.UNSUCCESSFULLY) {
                        rollbackRequested = true;
                    }
                    if (enactChangeApplication == ChangeApplied.SUCCESSFULLY) {
                        allNoOps = false;
                        appliedChanges.add(change);
                    }
                    fireChangeApplied(change);
                }
            }
            if (rollbackRequested) {
                for (OWLOntologyChange c : appliedChanges) {
                    ChangeApplied enactChangeApplication =
                        enactChangeApplication(c.reverseChange());
                    if (enactChangeApplication == ChangeApplied.UNSUCCESSFULLY) {
                        // rollback could not complete, throw an exception
                        throw new OWLRuntimeException("Rollback of changes unsuccessful: Change "
                            + c + " could not be rolled back");
                    }
                }
                appliedChanges.clear();
            }
            fireEndChanges();
            broadcastChanges(appliedChanges);
            if (rollbackRequested) {
                return ChangeApplied.UNSUCCESSFULLY;
            }
            if (allNoOps) {
                return ChangeApplied.NO_OPERATION;
            }
            return ChangeApplied.SUCCESSFULLY;
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    public ChangeDetails applyChangesAndGetDetails(List changes) {
        writeLock.lock();
        try {
            broadcastImpendingChanges(changes);
            boolean rollbackRequested = false;
            boolean allNoOps = true;
            // list of changes applied successfully. These are the changes that
            // will be reverted in case of a rollback
            List appliedChanges = new ArrayList<>();
            fireBeginChanges(changes.size());
            for (OWLOntologyChange change : changes) {
                // once rollback is requested by a failed change, do not carry
                // out any more changes
                if (!rollbackRequested) {
                    assert change != null;
                    ChangeApplied enactChangeApplication = enactChangeApplication(change);
                    if (enactChangeApplication == ChangeApplied.UNSUCCESSFULLY) {
                        rollbackRequested = true;
                    }
                    if (enactChangeApplication == ChangeApplied.SUCCESSFULLY) {
                        allNoOps = false;
                        appliedChanges.add(change);
                    }
                    fireChangeApplied(change);
                }
            }
            if (rollbackRequested) {
                for (OWLOntologyChange c : appliedChanges) {
                    ChangeApplied enactChangeApplication =
                        enactChangeApplication(c.reverseChange());
                    if (enactChangeApplication == ChangeApplied.UNSUCCESSFULLY) {
                        // rollback could not complete, throw an exception
                        throw new OWLRuntimeException("Rollback of changes unsuccessful: Change "
                            + c + " could not be rolled back");
                    }
                }
                appliedChanges.clear();
            }
            fireEndChanges();
            broadcastChanges(appliedChanges);
            if (rollbackRequested) {
                return new ChangeDetails(ChangeApplied.UNSUCCESSFULLY, appliedChanges);
            }
            if (allNoOps) {
                return new ChangeDetails(ChangeApplied.NO_OPERATION, appliedChanges);
            }
            return new ChangeDetails(ChangeApplied.SUCCESSFULLY, appliedChanges);
        } catch (OWLOntologyChangeVetoException e) {
            // Some listener blocked the changes.
            broadcastOntologyChangesVetoed(changes, e);
            return new ChangeDetails(ChangeApplied.UNSUCCESSFULLY,
                Collections.emptyList());
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    public ChangeApplied addAxiom(@Nonnull OWLOntology ont, @Nonnull OWLAxiom axiom) {
        writeLock.lock();
        try {
            return addAxioms(ont, CollectionFactory.createSet(axiom));
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    public ChangeApplied addAxioms(@Nonnull OWLOntology ont,
        @Nonnull Set axioms) {
        writeLock.lock();
        try {
            // Write lock not needed at this point
            List changes = new ArrayList<>(axioms.size() + 2);
            for (OWLAxiom ax : axioms) {
                assert ax != null;
                changes.add(new AddAxiom(ont, ax));
            }
            return applyChanges(changes);
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    public ChangeApplied removeAxiom(@Nonnull OWLOntology ont, @Nonnull OWLAxiom axiom) {
        writeLock.lock();
        try {
            // Write lock not needed at this point
            return removeAxioms(ont, CollectionFactory.createSet(axiom));
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    public ChangeApplied removeAxioms(@Nonnull OWLOntology ont,
        @Nonnull Set axioms) {
        // Write lock not needed at this point
        List changes = new ArrayList<>(axioms.size() + 2);
        for (OWLAxiom ax : axioms) {
            assert ax != null;
            changes.add(new RemoveAxiom(ont, ax));
        }
        return applyChanges(changes);
    }

    @Override
    public ChangeApplied applyChange(@Nonnull OWLOntologyChange change) {
        writeLock.lock();
        try {
            // Write lock not needed at this point
            return applyChanges(CollectionFactory.list(change));
        } finally {
            writeLock.unlock();
        }
    }

    private void checkForImportsChange(OWLOntologyChange change) {
        // Called by a write lock holder
        if (change.isImportChange()) {
            resetImportsClosureCache();
            if (change instanceof AddImport) {
                OWLImportsDeclaration addImportDeclaration =
                    ((AddImport) change).getImportDeclaration();
                boolean found = false;
                IRI iri = addImportDeclaration.getIRI();
                for (OWLOntologyID id : ontologiesByID.keySet()) {
                    if (iri.equals(id.getDefaultDocumentIRI().orNull())
                        || iri.equals(id.getOntologyIRI().orNull())
                        || iri.equals(id.getVersionIRI().orNull())) {
                        found = true;
                        ontologyIDsByImportsDeclaration.put(addImportDeclaration, id);
                    }
                }
                if (!found) {
                    // then the import does not refer to a known IRI for
                    // ontologies; check for a document IRI
                    for (Map.Entry e : documentIRIsByID.entrySet()) {
                        if (e.getValue().equals(iri)) {
                            // found the ontology id corresponding to the file
                            // location
                            ontologyIDsByImportsDeclaration.put(addImportDeclaration, e.getKey());
                        }
                    }
                }
            } else {
                // Remove the mapping from declaration to ontology
                OWLImportsDeclaration importDeclaration =
                    ((RemoveImport) change).getImportDeclaration();
                ontologyIDsByImportsDeclaration.remove(importDeclaration);
                importedIRIs.remove(importDeclaration.getIRI());
            }
        }
    }

    private void checkForOntologyIDChange(OWLOntologyChange change) {
        if (change instanceof SetOntologyID) {
            SetOntologyID setID = (SetOntologyID) change;
            OWLOntology existingOntology =
                ontologiesByID.get(((SetOntologyID) change).getNewOntologyID());
            OWLOntology o = change.getOntology();
            if (existingOntology != null && !o.equals(existingOntology)) {
                if (!sameAxioms(existingOntology, o)) {
                    String location = "OWLOntologyManagerImpl.checkForOntologyIDChange()";
                    LOGGER.error(location + " existing:{}", existingOntology);
                    LOGGER.error(location + " new:{}", o);
                    Set diff1 = o.getLogicalAxioms();
                    Set diff2 = existingOntology.getLogicalAxioms();
                    diff1.removeAll(existingOntology.getLogicalAxioms());
                    diff2.removeAll(o.getLogicalAxioms());
                    LOGGER.error(location + " only in existing:{}", diff2);
                    LOGGER.error(location + " only in new:{}", diff1);
                    throw new OWLOntologyRenameException(change.getChangeData(),
                        ((SetOntologyID) change).getNewOntologyID());
                }
            }
            renameOntology(setID.getOriginalOntologyID(), setID.getNewOntologyID());
            resetImportsClosureCache();
        }
    }

    protected boolean sameAxioms(OWLOntology existingOntology, OWLOntology o) {
        for (AxiomType t : AxiomType.AXIOM_TYPES) {
            if (existingOntology.getAxiomCount(t) != o.getAxiomCount(t)) {
                return false;
            }
        }
        for (AxiomType t : AxiomType.AXIOM_TYPES) {
            if (!existingOntology.getAxioms(t).equals(o.getAxioms(t))) {
                return false;
            }
        }
        return true;
    }

    // Methods to create, load and reload ontologies
    @Override
    public void ontologyCreated(OWLOntology ontology) {
        // This method is called when a factory that we have asked to create or
        // load an ontology has created the ontology. We add the ontology to the
        // set of loaded ontologies.
        addOntology(ontology);
    }

    @Override
    public void setOntologyFormat(OWLOntology ontology, OWLDocumentFormat ontologyFormat) {
        writeLock.lock();
        try {
            OWLOntologyID ontologyID = ontology.getOntologyID();
            ontologyFormatsByOntology.put(ontologyID, ontologyFormat);
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    public OWLDocumentFormat getOntologyFormat(@Nonnull OWLOntology ontology) {
        readLock.lock();
        try {
            OWLOntologyID ontologyID = ontology.getOntologyID();
            return ontologyFormatsByOntology.get(ontologyID);
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public OWLOntology createOntology() throws OWLOntologyCreationException {
        // Brand new ontology without a URI
        return createOntology(new OWLOntologyID());
    }

    @Override
    public OWLOntology createOntology(@Nonnull IRI ontologyIRI)
        throws OWLOntologyCreationException {
        return createOntology(new OWLOntologyID(ontologyIRI, null));
    }

    @Override
    public OWLOntology createOntology(@Nonnull OWLOntologyID ontologyID)
        throws OWLOntologyCreationException {
        writeLock.lock();
        try {
            OWLOntology ontology = ontologiesByID.get(ontologyID);
            if (ontology != null) {
                throw new OWLOntologyAlreadyExistsException(ontologyID);
            }
            IRI documentIRI = getDocumentIRIFromMappers(ontologyID);
            if (documentIRI == null) {
                if (!ontologyID.isAnonymous()) {
                    documentIRI = ontologyID.getDefaultDocumentIRI().orNull();
                } else {
                    documentIRI = IRI.generateDocumentIRI();
                }
                Collection existingDocumentIRIs = documentIRIsByID.values();
                while (existingDocumentIRIs.contains(documentIRI)) {
                    documentIRI = IRI.generateDocumentIRI();
                }
            }
            assert documentIRI != null;
            if (documentIRIsByID.values().contains(documentIRI)) {
                throw new OWLOntologyDocumentAlreadyExistsException(documentIRI);
            }
            for (OWLOntologyFactory factory : ontologyFactories) {
                if (factory.canCreateFromDocumentIRI(documentIRI)) {
                    documentIRIsByID.put(ontologyID, documentIRI);
                    factory.setLock(lock);
                    return factory.createOWLOntology(this, ontologyID, documentIRI, this);
                }
            }
            throw new OWLOntologyFactoryNotFoundException(documentIRI);
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    public OWLOntology createOntology(IRI ontologyIRI, Set ontologies)
        throws OWLOntologyCreationException {
        return createOntology(ontologyIRI, ontologies, false);
    }

    @Override
    public OWLOntology createOntology(IRI ontologyIRI, Set ontologies,
        boolean copyLogicalAxiomsOnly) throws OWLOntologyCreationException {
        writeLock.lock();
        try {
            if (contains(ontologyIRI)) {
                throw new OWLOntologyAlreadyExistsException(new OWLOntologyID(ontologyIRI, null));
            }
            OWLOntology ont = createOntology(ontologyIRI);
            Set axioms = new HashSet<>();
            for (OWLOntology ontology : ontologies) {
                if (copyLogicalAxiomsOnly) {
                    axioms.addAll(ontology.getLogicalAxioms());
                } else {
                    axioms.addAll(ontology.getAxioms());
                }
            }
            addAxioms(ont, axioms);
            return ont;
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    public OWLOntology createOntology(Set axioms, IRI ontologyIRI)
        throws OWLOntologyCreationException {
        writeLock.lock();
        try {
            if (contains(ontologyIRI)) {
                throw new OWLOntologyAlreadyExistsException(new OWLOntologyID(ontologyIRI, null));
            }
            OWLOntology ont = createOntology(ontologyIRI);
            addAxioms(ont, axioms);
            return ont;
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    public OWLOntology createOntology(Set axioms) throws OWLOntologyCreationException {
        return createOntology(axioms, getNextAutoGeneratedIRI());
    }

    @Override
    public OWLOntology copyOntology(OWLOntology toCopy, OntologyCopy settings)
        throws OWLOntologyCreationException {
        writeLock.lock();
        try {
            checkNotNull(toCopy);
            checkNotNull(settings);
            OWLOntology toReturn = null;
            if (settings == OntologyCopy.MOVE) {
                toReturn = toCopy;
                ontologiesByID.put(toReturn.getOntologyID(), toReturn);
            } else if (settings == OntologyCopy.SHALLOW || settings == OntologyCopy.DEEP) {
                toReturn = createOntology(toCopy.getOntologyID());
                for (AxiomType type : AxiomType.AXIOM_TYPES) {
                    assert type != null;
                    addAxioms(toReturn, toCopy.getAxioms(type));
                }
                for (OWLAnnotation a : toCopy.getAnnotations()) {
                    assert a != null;
                    applyChange(new AddOntologyAnnotation(toReturn, a));
                }
                for (OWLImportsDeclaration a : toCopy.getImportsDeclarations()) {
                    assert a != null;
                    applyChange(new AddImport(toReturn, a));
                }
            }
            // toReturn now initialized
            assert toReturn != null;
            OWLOntologyManager m = toCopy.getOWLOntologyManager();
            if (settings == OntologyCopy.MOVE || settings == OntologyCopy.DEEP) {
                setOntologyDocumentIRI(toReturn, m.getOntologyDocumentIRI(toCopy));
                OWLDocumentFormat f = m.getOntologyFormat(toCopy);
                if (f != null) {
                    setOntologyFormat(toReturn, f);
                }
            }
            if (settings == OntologyCopy.MOVE) {
                m.removeOntology(toCopy);
                // at this point toReturn and toCopy are the same object
                // change the manager on the ontology
                toReturn.setOWLOntologyManager(this);
                // change the lock on the ontology
                if (toReturn instanceof OWLMutableOntology) {
                    ((OWLMutableOntology) toReturn).setLock(lock);
                }
            }
            return toReturn;
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    public OWLOntology loadOntology(IRI ontologyIRI) throws OWLOntologyCreationException {
        // if an ontology cyclically imports itself, the manager should not try to download from the
        // same URL twice.
        Object value = new Object();
        if (!importedIRIs.containsKey(ontologyIRI)) {
            importedIRIs.put(ontologyIRI, value);
        }
        OWLOntology loadOntology =
            loadOntology(ontologyIRI, false, getOntologyLoaderConfiguration());
        importedIRIs.remove(ontologyIRI, value);
        return loadOntology;
    }

    @Nonnull
    protected OWLOntology loadOntology(@Nonnull IRI ontologyIRI, boolean allowExists,
        @Nonnull OWLOntologyLoaderConfiguration configuration) throws OWLOntologyCreationException {
        writeLock.lock();
        try {
            OWLOntology ontByID = null;
            // Check for matches on the ontology IRI first
            for (OWLOntologyID nextOntologyID : ontologiesByID.keySet()) {
                if (ontologyIRI.equals(nextOntologyID.getOntologyIRI().orNull())) {
                    ontByID = ontologiesByID.get(nextOntologyID);
                }
            }
            // This method may be called using a version IRI, so also check the
            // version IRI if necessary
            if (ontByID == null) {
                for (OWLOntologyID nextOntologyID : ontologiesByID.keySet()) {
                    if (ontologyIRI.equals(nextOntologyID.getVersionIRI().orNull())) {
                        ontByID = ontologiesByID.get(nextOntologyID);
                    }
                }
            }
            if (ontByID != null) {
                return ontByID;
            }
            OWLOntologyID id = new OWLOntologyID(ontologyIRI, null);
            IRI documentIRI = getDocumentIRIFromMappers(id);
            if (documentIRI != null) {
                if (documentIRIsByID.values().contains(documentIRI) && !allowExists) {
                    throw new OWLOntologyDocumentAlreadyExistsException(documentIRI);
                }
                // The ontology might be being loaded, but its IRI might
                // not have been set (as is probably the case with RDF/XML!)
                OWLOntology ontByDocumentIRI = loadOntologyByDocumentIRI(documentIRI);
                if (ontByDocumentIRI != null) {
                    return ontByDocumentIRI;
                }
            } else {
                // Nothing we can do here. We can't get a document IRI to load
                // the ontology from.
                throw new OntologyIRIMappingNotFoundException(ontologyIRI);
            }
            return loadOntology(ontologyIRI, new IRIDocumentSource(documentIRI, null, null),
                configuration);
        } finally {
            writeLock.unlock();
        }
    }

    private OWLOntology loadOntologyByDocumentIRI(IRI documentIRI) {
        readLock.lock();
        try {
            for (OWLOntologyID ontID : documentIRIsByID.keySet()) {
                assert ontID != null;
                IRI docIRI = documentIRIsByID.get(ontID);
                if (docIRI != null && docIRI.equals(documentIRI)) {
                    return getOntology(ontID);
                }
            }
            return null;
        } finally {
            readLock.unlock();
        }
    }

    private OWLOntology getOntologyByDocumentIRI(IRI documentIRI) {
        readLock.lock();
        try {
            for (OWLOntologyID ontID : documentIRIsByID.keySet()) {
                assert ontID != null;
                IRI docIRI = documentIRIsByID.get(ontID);
                if (documentIRI.equals(docIRI)) {
                    return ontologiesByID.get(ontID);
                }
            }
            return null;
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public OWLOntology loadOntologyFromOntologyDocument(IRI documentIRI)
        throws OWLOntologyCreationException {
        // Ontology URI not known in advance
        return loadOntology(null, new IRIDocumentSource(documentIRI, null, null),
            getOntologyLoaderConfiguration());
    }

    @Override
    public OWLOntology loadOntologyFromOntologyDocument(OWLOntologyDocumentSource documentSource)
        throws OWLOntologyCreationException {
        // Ontology URI not known in advance
        return loadOntology(null, documentSource, getOntologyLoaderConfiguration());
    }

    @Override
    public OWLOntology loadOntologyFromOntologyDocument(OWLOntologyDocumentSource documentSource,
        OWLOntologyLoaderConfiguration conf) throws OWLOntologyCreationException {
        return loadOntology(null, documentSource, conf);
    }

    @Override
    public OWLOntology loadOntologyFromOntologyDocument(File file)
        throws OWLOntologyCreationException {
        return loadOntologyFromOntologyDocument(new FileDocumentSource(file));
    }

    @Override
    public OWLOntology loadOntologyFromOntologyDocument(InputStream inputStream)
        throws OWLOntologyCreationException {
        return loadOntologyFromOntologyDocument(new StreamDocumentSource(inputStream));
    }

    /**
     * This is the method that all the other load method delegate to.
     * 
     * @param ontologyIRI The URI of the ontology to be loaded. This is only used to report to
     *        listeners and may be {@code null}
     * @param documentSource The input source that specifies where the ontology should be loaded
     *        from.
     * @param configuration load configuration
     * @return The ontology that was loaded.
     * @throws OWLOntologyCreationException If the ontology could not be loaded.
     */
    @Nonnull
    protected OWLOntology loadOntology(@Nullable IRI ontologyIRI,
        @Nonnull OWLOntologyDocumentSource documentSource,
        @Nonnull OWLOntologyLoaderConfiguration configuration) throws OWLOntologyCreationException {
        writeLock.lock();
        try {
            if (loadCount.get() != importsLoadCount.get()) {
                LOGGER.warn(
                    "Runtime Warning: Parsers should load imported ontologies using the makeImportLoadRequest method.");
            }
            fireStartedLoadingEvent(new OWLOntologyID(ontologyIRI, null),
                documentSource.getDocumentIRI(), loadCount.get() > 0);
            loadCount.incrementAndGet();
            broadcastChanges.set(false);
            Exception ex = null;
            OWLOntologyID idOfLoadedOntology = new OWLOntologyID();
            try {
                OWLOntology ontology = actualParse(documentSource, configuration);
                if (ontology != null) {
                    idOfLoadedOntology = ontology.getOntologyID();
                    return ontology;
                }
            } catch (OWLOntologyRenameException e) {
                // We loaded an ontology from a document and the
                // ontology turned out to have an IRI the same
                // as a previously loaded ontology
                ex = e;
                throw new OWLOntologyAlreadyExistsException(e.getOntologyID(), e);
            } catch (UnloadableImportException e) {
                ex = e;
                throw e;
            } catch (OWLRuntimeException e) {
                if (e.getCause() instanceof OWLOntologyCreationException) {
                    ex = (OWLOntologyCreationException) e.getCause();
                    throw (OWLOntologyCreationException) e.getCause();
                }
                throw e;
            } catch (OWLOntologyCreationException e) {
                ex = e;
                throw e;
            } finally {
                loadCount.decrementAndGet();
                if (loadCount.get() == 0) {
                    broadcastChanges.set(true);
                    // Completed loading ontology and imports
                }
                fireFinishedLoadingEvent(idOfLoadedOntology, documentSource.getDocumentIRI(),
                    loadCount.get() > 0, ex);
            }
            throw new OWLOntologyFactoryNotFoundException(documentSource.getDocumentIRI());
        } finally {
            writeLock.unlock();
        }
    }

    protected OWLOntology actualParse(OWLOntologyDocumentSource documentSource,
        OWLOntologyLoaderConfiguration configuration) throws OWLOntologyCreationException {
        // Check if this is an IRI source and the IRI has already been loaded - this will stop
        // ontologies
        // that cyclically import themselves from being loaded twice.
        if (documentSource instanceof IRIDocumentSource) {
            IRIDocumentSource source = (IRIDocumentSource) documentSource;
            java.util.Optional> findAny =
                documentIRIsByID.entrySet().stream()
                    .filter(v -> Objects.equals(source.getDocumentIRI(), v.getValue())).findAny();
            if (findAny.isPresent()) {
                return getOntology(findAny.get().getKey());
            }
        }

        for (OWLOntologyFactory factory : ontologyFactories) {
            if (factory.canLoad(documentSource)) {
                // Note - there is no need to add the ontology here,
                // because it will be added
                // when the ontology is created.
                factory.setLock(lock);
                OWLOntology ontology =
                    factory.loadOWLOntology(this, documentSource, this, configuration);
                if (configuration.shouldRepairIllegalPunnings()) {
                    fixIllegalPunnings(ontology);
                }
                // Store the ontology to the document IRI mapping
                documentIRIsByID.put(ontology.getOntologyID(), documentSource.getDocumentIRI());
                ontologyConfigurationsByOntologyID.put(ontology.getOntologyID(), configuration);
                if (ontology instanceof HasTrimToSize && configuration.shouldTrimToSize()) {
                    ((HasTrimToSize) ontology).trimToSize();
                }
                return ontology;
            }
        }
        return null;
    }

    protected void fixIllegalPunnings(OWLOntology o) {
        Collection illegals = OWLDocumentFormatImpl.determineIllegalPunnings(true,
            o.getSignature(INCLUDED), o.getPunnedIRIs(INCLUDED));
        Map> illegalDeclarations = new HashMap<>();
        Set declarations = o.getAxioms(AxiomType.DECLARATION, INCLUDED);
        for (OWLDeclarationAxiom d : declarations) {
            if (illegals.contains(d.getEntity().getIRI())) {
                illegalDeclarations.computeIfAbsent(d.getEntity().getIRI(), x -> new HashSet<>())
                    .add(d);
            }
        }
        Map replacementMap = new HashMap<>();
        for (Map.Entry> e : illegalDeclarations.entrySet()) {
            if (e.getValue().size() == 1) {
                // One declaration only: illegal punning comes from use or from
                // defaulting of types
                OWLDeclarationAxiom correctDeclaration = e.getValue().iterator().next();
                // currently we only know how to fix the incorrect defaulting of
                // properties to annotation properties
                OWLEntity entity = correctDeclaration.getEntity();
                if (entity.isOWLDataProperty() || entity.isOWLObjectProperty()) {
                    OWLAnnotationProperty wrongProperty =
                        dataFactory.getOWLAnnotationProperty(entity.getIRI());
                    replacementMap.put(wrongProperty, entity);
                }
            } else {
                // Multiple declarations: bad data. Cannot be repaired automatically.
                String errorMessage =
                    "Illegal redeclarations of entities: reuse of entity {} in punning not allowed {}";
                LOGGER.warn(errorMessage, e.getKey(), e.getValue());
            }
        }
        for (OWLOntology ont : o.getImportsClosure()) {
            for (OWLEntity e : replacementMap.keySet()) {
                if (ont.containsEntityInSignature(e)) {
                    // then all axioms referring the annotation property
                    // must be rebuilt.
                    List list = new ArrayList<>();
                    for (OWLAxiom ax : ont.getAxioms()) {
                        if (ax.getSignature().contains(e)) {
                            list.add(new RemoveAxiom(ont, ax));
                            OWLAnnotationPropertyTransformer changer =
                                new OWLAnnotationPropertyTransformer(replacementMap, dataFactory);
                            list.add(new AddAxiom(ont, changer.transformObject(ax)));
                        }
                    }
                    o.getOWLOntologyManager().applyChanges(list);
                }
            }
        }
    }

    @Override
    public void removeOntology(OWLOntology ontology) {
        writeLock.lock();
        try {
            removeOntology(ontology.getOntologyID());
            ontology.setOWLOntologyManager(null);
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    public void removeOntology(OWLOntologyID ontologyID) {
        writeLock.lock();
        try {
            OWLOntology o = ontologiesByID.remove(ontologyID);
            ontologyFormatsByOntology.remove(ontologyID);
            documentIRIsByID.remove(ontologyID);
            removeValue(ontologyIDsByImportsDeclaration, ontologyID);
            removeValue(importedIRIs, ontologyID);
            if (o != null) {
                o.setOWLOntologyManager(null);
                resetImportsClosureCache();
            }
        } finally {
            writeLock.unlock();
        }
    }

    protected  void removeValue(Map map, S id) {
        List keys = new ArrayList<>();
        for (Map.Entry e : map.entrySet()) {
            if (e.getValue().equals(id)) {
                keys.add(e.getKey());
            }
        }
        for (Q k : keys) {
            map.remove(k);
        }
    }

    private void addOntology(OWLOntology ont) {
        writeLock.lock();
        try {
            ontologiesByID.put(ont.getOntologyID(), ont);
            resetImportsClosureCache();
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    public IRI getOntologyDocumentIRI(OWLOntology ontology) {
        readLock.lock();
        try {
            if (!contains(ontology)) {
                throw new UnknownOWLOntologyException(ontology.getOntologyID());
            }
            return verifyNotNull(documentIRIsByID.get(ontology.getOntologyID()));
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public void setOntologyDocumentIRI(OWLOntology ontology, IRI documentIRI) {
        writeLock.lock();
        try {
            if (!ontologiesByID.containsKey(ontology.getOntologyID())) {
                throw new UnknownOWLOntologyException(ontology.getOntologyID());
            }
            documentIRIsByID.put(ontology.getOntologyID(), documentIRI);
            resetImportsClosureCache();
        } finally {
            writeLock.unlock();
        }
    }

    /**
     * Handles a rename of an ontology. This method should only be called *after* the change has
     * been applied
     * 
     * @param oldID The original ID of the ontology
     * @param newID The new ID of the ontology
     */
    private void renameOntology(OWLOntologyID oldID, OWLOntologyID newID) {
        OWLOntology ont = ontologiesByID.get(oldID);
        if (ont == null) {
            // Nothing to rename!
            return;
        }
        ontologiesByID.remove(oldID);
        ontologiesByID.put(newID, ont);
        if (ontologyFormatsByOntology.containsKey(oldID)) {
            ontologyFormatsByOntology.put(newID, ontologyFormatsByOntology.remove(oldID));
        }
        IRI documentIRI = documentIRIsByID.remove(oldID);
        if (documentIRI != null) {
            documentIRIsByID.put(newID, documentIRI);
        }
        resetImportsClosureCache();
    }

    protected void resetImportsClosureCache() {
        writeLock.lock();
        try {
            importsClosureCache.clear();
        } finally {
            writeLock.unlock();
        }
    }

    // Methods to save ontologies
    @Override
    public void saveOntology(OWLOntology ontology) throws OWLOntologyStorageException {
        readLock.lock();
        try {
            OWLDocumentFormat format = getOntologyFormat(ontology);
            saveOntology(ontology, format);
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public void saveOntology(@Nonnull OWLOntology ontology, OWLDocumentFormat ontologyFormat)
        throws OWLOntologyStorageException {
        readLock.lock();
        try {
            saveOntology(ontology, ontologyFormat, getOntologyDocumentIRI(ontology));
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public void saveOntology(OWLOntology ontology, IRI documentIRI)
        throws OWLOntologyStorageException {
        readLock.lock();
        try {
            OWLDocumentFormat format = getOntologyFormat(ontology);
            saveOntology(ontology, format, documentIRI);
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public void saveOntology(OWLOntology ontology, OWLDocumentFormat ontologyFormat,
        IRI documentIRI) throws OWLOntologyStorageException {
        readLock.lock();
        try {
            try {
                for (OWLStorerFactory storerFactory : ontologyStorers) {
                    OWLStorer storer = storerFactory.createStorer();
                    if (storer.canStoreOntology(ontologyFormat)) {
                        storer.storeOntology(ontology, documentIRI, ontologyFormat);
                        return;
                    }
                }
                throw new OWLStorerNotFoundException(ontologyFormat);
            } catch (IOException e) {
                throw new OWLOntologyStorageIOException(e);
            }
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public void saveOntology(OWLOntology ontology, OutputStream outputStream)
        throws OWLOntologyStorageException {
        // Write lock not needed at this point
        saveOntology(ontology, new StreamDocumentTarget(outputStream));
    }

    @Override
    public void saveOntology(OWLOntology ontology, OWLDocumentFormat ontologyFormat,
        OutputStream outputStream) throws OWLOntologyStorageException {
        // Write lock not needed at this point
        saveOntology(ontology, ontologyFormat, new StreamDocumentTarget(outputStream));
    }

    @Override
    public void saveOntology(OWLOntology ontology, OWLOntologyDocumentTarget documentTarget)
        throws OWLOntologyStorageException {
        readLock.lock();
        try {
            saveOntology(ontology, getOntologyFormat(ontology), documentTarget);
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public void saveOntology(OWLOntology ontology, OWLDocumentFormat ontologyFormat,
        OWLOntologyDocumentTarget documentTarget) throws OWLOntologyStorageException {
        readLock.lock();
        try {
            try {
                for (OWLStorerFactory storerFactory : ontologyStorers) {
                    OWLStorer storer = storerFactory.createStorer();
                    if (storer.canStoreOntology(ontologyFormat)) {
                        storer.storeOntology(ontology, documentTarget, ontologyFormat);
                        return;
                    }
                }
                throw new OWLStorerNotFoundException(ontologyFormat);
            } catch (IOException e) {
                throw new OWLOntologyStorageIOException(e);
            }
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public PriorityCollection getOntologyStorers() {
        // Locking done by collection
        return ontologyStorers;
    }

    @Override
    @Inject
    public void setOntologyStorers(Set storers) {
        // Locking done by collection
        ontologyStorers.set(storers);
    }

    @Override
    public PriorityCollection getIRIMappers() {
        // Locking done by collection
        return documentMappers;
    }

    @Override
    @Inject
    public void setIRIMappers(Set mappers) {
        // Locking done by collection
        documentMappers.set(mappers);
    }

    @Override
    public void addIRIMapper(OWLOntologyIRIMapper mapper) {
        // Locking done by collection
        documentMappers.add(mapper);
    }

    @Override
    public void removeIRIMapper(OWLOntologyIRIMapper mapper) {
        // Locking done by collection
        documentMappers.remove(mapper);
    }

    @Override
    public void clearIRIMappers() {
        // Locking done by collection
        documentMappers.clear();
    }

    @Override
    public void addOntologyStorer(OWLStorerFactory storer) {
        // Locking done by collection
        ontologyStorers.add(storer);
    }

    @Override
    public void removeOntologyStorer(OWLStorerFactory storer) {
        // Locking done by collection
        ontologyStorers.remove(storer);
    }

    @Override
    public void clearOntologyStorers() {
        // Locking done by collection
        ontologyStorers.clear();
    }

    @Override
    public PriorityCollection getOntologyParsers() {
        // Locking done by collection
        return parserFactories;
    }

    @Override
    @Inject
    public void setOntologyParsers(Set parsers) {
        // Locking done by collection
        parserFactories.set(parsers);
    }

    @Override
    public PriorityCollection getOntologyFactories() {
        // Locking done by collection
        return ontologyFactories;
    }

    @Override
    @Inject
    public void setOntologyFactories(Set factories) {
        // Locking done by collection
        ontologyFactories.set(factories);
    }

    /**
     * Uses the mapper mechanism to obtain an ontology document IRI from an ontology IRI.
     * 
     * @param ontologyID The ontology ID for which a document IRI is to be retrieved
     * @return The document IRI that corresponds to the ontology IRI, or {@code null} if no physical
     *         URI can be found.
     */
    @Nullable
    private IRI getDocumentIRIFromMappers(OWLOntologyID ontologyID) {
        if (!ontologyID.getDefaultDocumentIRI().isPresent()) {
            return null;
        }
        IRI iri = ontologyID.getDefaultDocumentIRI().get();
        assert iri != null;
        for (OWLOntologyIRIMapper mapper : documentMappers) {
            IRI documentIRI = mapper.getDocumentIRI(iri);
            if (documentIRI != null) {
                return documentIRI;
            }
        }
        return iri;
    }

    protected final void installDefaultURIMappers() {}

    protected final void installDefaultOntologyFactories() {
        // The default factories are the ones that can load
        // ontologies from http:// and file:// URIs
    }

    // Listener stuff - methods to add/remove listeners
    private void readObject(java.io.ObjectInputStream stream)
        throws IOException, ClassNotFoundException {
        stream.defaultReadObject();
        listenerMap = new ConcurrentHashMap<>();
        impendingChangeListenerMap = new ConcurrentHashMap<>();
        vetoListeners = new ArrayList<>();
    }

    @Override
    public void addOntologyChangeListener(OWLOntologyChangeListener listener) {
        writeLock.lock();
        try {
            listenerMap.put(listener, defaultChangeBroadcastStrategy);
        } finally {
            writeLock.unlock();
        }
    }

    /**
     * Broadcasts to attached listeners, using the various broadcasting strategies that were
     * specified for each listener.
     * 
     * @param changes The ontology changes to broadcast
     */
    protected void broadcastChanges(@Nonnull List changes) {
        writeLock.lock();
        try {
            if (!broadcastChanges.get()) {
                return;
            }
            for (OWLOntologyChangeListener listener : new ArrayList<>(listenerMap.keySet())) {
                assert listener != null;
                OWLOntologyChangeBroadcastStrategy strategy = listenerMap.get(listener);
                if (strategy == null) {
                    // This listener may have been removed during the broadcast
                    // of
                    // the changes, so when we attempt to retrieve it from the
                    // map
                    // it isn't there (because we iterate over a copy).
                    continue;
                }
                try {
                    // Handle exceptions on a per listener basis. If we have
                    // badly behaving listeners, we don't want one listener
                    // to prevent the other listeners from receiving events.
                    strategy.broadcastChanges(listener, changes);
                } catch (Exception e) {
                    LOGGER.warn(BADLY_BEHAVING_LISTENER_HAS_BEEN_REMOVED, e.getMessage(), e);
                    listenerMap.remove(listener);
                }
            }
        } finally {
            writeLock.unlock();
        }
    }

    protected void broadcastImpendingChanges(@Nonnull List changes) {
        writeLock.lock();
        try {
            if (!broadcastChanges.get()) {
                return;
            }
            for (ImpendingOWLOntologyChangeListener listener : new ArrayList<>(
                impendingChangeListenerMap.keySet())) {
                assert listener != null;
                ImpendingOWLOntologyChangeBroadcastStrategy strategy =
                    impendingChangeListenerMap.get(listener);
                if (strategy != null) {
                    strategy.broadcastChanges(listener, changes);
                }
            }
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    public void setDefaultChangeBroadcastStrategy(OWLOntologyChangeBroadcastStrategy strategy) {
        writeLock.lock();
        try {
            defaultChangeBroadcastStrategy = strategy;
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    public void addOntologyChangeListener(OWLOntologyChangeListener listener,
        OWLOntologyChangeBroadcastStrategy strategy) {
        writeLock.lock();
        try {
            listenerMap.put(listener, strategy);
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    public void addImpendingOntologyChangeListener(ImpendingOWLOntologyChangeListener listener) {
        writeLock.lock();
        try {
            impendingChangeListenerMap.put(listener, defaultImpendingChangeBroadcastStrategy);
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    public void removeImpendingOntologyChangeListener(ImpendingOWLOntologyChangeListener listener) {
        writeLock.lock();
        try {
            impendingChangeListenerMap.remove(listener);
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    public void removeOntologyChangeListener(OWLOntologyChangeListener listener) {
        writeLock.lock();
        try {
            listenerMap.remove(listener);
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    public void addOntologyChangesVetoedListener(OWLOntologyChangesVetoedListener listener) {
        writeLock.lock();
        try {
            vetoListeners.add(listener);
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    public void removeOntologyChangesVetoedListener(OWLOntologyChangesVetoedListener listener) {
        writeLock.lock();
        try {
            vetoListeners.remove(listener);
        } finally {
            writeLock.unlock();
        }
    }

    private void broadcastOntologyChangesVetoed(@Nonnull List changes,
        @Nonnull OWLOntologyChangeVetoException veto) {
        writeLock.lock();
        try {
            for (OWLOntologyChangesVetoedListener listener : new ArrayList<>(vetoListeners)) {
                listener.ontologyChangesVetoed(changes, veto);
            }
        } finally {
            writeLock.unlock();
        }
    }

    // Imports etc.
    protected OWLOntology loadImports(OWLImportsDeclaration declaration,
        @Nonnull OWLOntologyLoaderConfiguration configuration) throws OWLOntologyCreationException {
        writeLock.lock();
        try {
            importsLoadCount.incrementAndGet();
            OWLOntology ont = null;
            try {
                ont = loadOntology(declaration.getIRI(), true, configuration);
            } catch (OWLOntologyCreationException e) {
                if (configuration
                    .getMissingImportHandlingStrategy() == MissingImportHandlingStrategy.THROW_EXCEPTION) {
                    throw e;
                } else {
                    // Silent
                    MissingImportEvent evt = new MissingImportEvent(declaration.getIRI(), e);
                    fireMissingImportEvent(evt);
                }
            } finally {
                importsLoadCount.decrementAndGet();
            }
            return ont;
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    public void makeLoadImportRequest(OWLImportsDeclaration declaration) {
        writeLock.lock();
        try {
            makeLoadImportRequest(declaration, getOntologyLoaderConfiguration());
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    public void makeLoadImportRequest(OWLImportsDeclaration declaration,
        OWLOntologyLoaderConfiguration configuration) {
        writeLock.lock();
        try {
            IRI iri = declaration.getIRI();
            if (!configuration.isIgnoredImport(iri) && !importedIRIs.containsKey(iri)) {
                importedIRIs.put(iri, new Object());
                try {
                    OWLOntology ont = loadImports(declaration, configuration);
                    if (ont != null) {
                        ontologyIDsByImportsDeclaration.put(declaration, ont.getOntologyID());
                        importedIRIs.put(iri, ont.getOntologyID());
                    }
                } catch (OWLOntologyCreationException e) {
                    // Wrap as UnloadableImportException and throw
                    throw new UnloadableImportException(e, declaration);
                }
            }
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    public void addMissingImportListener(MissingImportListener listener) {
        writeLock.lock();
        try {
            missingImportsListeners.add(listener);
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    public void removeMissingImportListener(@Nonnull MissingImportListener listener) {
        writeLock.lock();
        try {
            missingImportsListeners.remove(listener);
        } finally {
            writeLock.unlock();
        }
    }

    protected void fireMissingImportEvent(@Nonnull MissingImportEvent evt) {
        writeLock.lock();
        try {
            for (MissingImportListener listener : new ArrayList<>(missingImportsListeners)) {
                listener.importMissing(evt);
            }
        } finally {
            writeLock.unlock();
        }
    }

    // Other listeners etc.
    @Override
    public void addOntologyLoaderListener(OWLOntologyLoaderListener listener) {
        writeLock.lock();
        try {
            loaderListeners.add(listener);
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    public void removeOntologyLoaderListener(OWLOntologyLoaderListener listener) {
        writeLock.lock();
        try {
            loaderListeners.remove(listener);
        } finally {
            writeLock.unlock();
        }
    }

    protected void fireStartedLoadingEvent(OWLOntologyID ontologyID, IRI documentIRI,
        boolean imported) {
        writeLock.lock();
        try {
            for (OWLOntologyLoaderListener listener : new ArrayList<>(loaderListeners)) {
                listener.startedLoadingOntology(new OWLOntologyLoaderListener.LoadingStartedEvent(
                    ontologyID, documentIRI, imported));
            }
        } finally {
            writeLock.unlock();
        }
    }

    protected void fireFinishedLoadingEvent(OWLOntologyID ontologyID, IRI documentIRI,
        boolean imported, Exception ex) {
        writeLock.lock();
        try {
            for (OWLOntologyLoaderListener listener : new ArrayList<>(loaderListeners)) {
                listener.finishedLoadingOntology(new OWLOntologyLoaderListener.LoadingFinishedEvent(
                    ontologyID, documentIRI, imported, ex));
            }
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    public void addOntologyChangeProgessListener(OWLOntologyChangeProgressListener listener) {
        writeLock.lock();
        try {
            progressListeners.add(listener);
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    public void removeOntologyChangeProgessListener(OWLOntologyChangeProgressListener listener) {
        writeLock.lock();
        try {
            progressListeners.remove(listener);
        } finally {
            writeLock.unlock();
        }
    }

    protected void fireBeginChanges(int size) {
        writeLock.lock();
        try {
            if (!broadcastChanges.get()) {
                return;
            }
            for (OWLOntologyChangeProgressListener listener : progressListeners) {
                try {
                    listener.begin(size);
                } catch (Exception e) {
                    LOGGER.warn(BADLY_BEHAVING_LISTENER_HAS_BEEN_REMOVED, e.getMessage(), e);
                    progressListeners.remove(listener);
                }
            }
        } finally {
            writeLock.unlock();
        }
    }

    protected void fireEndChanges() {
        writeLock.lock();
        try {
            if (!broadcastChanges.get()) {
                return;
            }
            for (OWLOntologyChangeProgressListener listener : progressListeners) {
                try {
                    listener.end();
                } catch (Exception e) {
                    LOGGER.warn(BADLY_BEHAVING_LISTENER_HAS_BEEN_REMOVED, e.getMessage(), e);
                    progressListeners.remove(listener);
                }
            }
        } finally {
            writeLock.unlock();
        }
    }

    private void fireChangeApplied(@Nonnull OWLOntologyChange change) {
        writeLock.lock();
        try {
            if (!broadcastChanges.get()) {
                return;
            }
            if (progressListeners.isEmpty()) {
                return;
            }
            for (OWLOntologyChangeProgressListener listener : progressListeners) {
                try {
                    listener.appliedChange(change);
                } catch (Exception e) {
                    LOGGER.warn(BADLY_BEHAVING_LISTENER_HAS_BEEN_REMOVED, e.getMessage(), e);
                    progressListeners.remove(listener);
                }
            }
        } finally {
            writeLock.unlock();
        }
    }

    @Nonnull
    protected  Optional of(T t) {
        return Optional.ofNullable(t);
    }

    @Nonnull
    protected Optional absent() {
        return Optional.empty();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy