package io.lumify.core.model.ontology;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import io.lumify.core.config.Configuration;
import io.lumify.core.exception.LumifyException;
import io.lumify.core.model.properties.LumifyProperties;
import io.lumify.core.util.LumifyLogger;
import io.lumify.core.util.LumifyLoggerFactory;
import io.lumify.web.clientapi.model.PropertyType;
import org.apache.commons.io.IOUtils;
import org.securegraph.Authorizations;
import org.securegraph.Graph;
import org.securegraph.TextIndexHint;
import org.securegraph.inmemory.InMemoryAuthorizations;
import org.securegraph.util.ConvertingIterable;
import org.semanticweb.owlapi.apibinding.OWLManager;
import org.semanticweb.owlapi.io.OWLOntologyDocumentSource;
import org.semanticweb.owlapi.io.ReaderDocumentSource;
import org.semanticweb.owlapi.model.*;
import java.io.*;
import java.util.*;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.securegraph.util.IterableUtils.toList;
public class InMemoryOntologyRepository extends OntologyRepositoryBase {
private static final LumifyLogger LOGGER = LumifyLoggerFactory.getLogger(InMemoryOntologyRepository.class);
private final Graph graph;
private final OWLOntologyLoaderConfiguration owlConfig = new OWLOntologyLoaderConfiguration();
private final Map<String, InMemoryConcept> conceptsCache = new HashMap<>();
private final Map<String, InMemoryOntologyProperty> propertiesCache = new HashMap<>();
private final Map<String, InMemoryRelationship> relationshipsCache = new HashMap<>();
private final List<OwlData> fileCache = new ArrayList<>();
@Inject
public InMemoryOntologyRepository(
final Graph graph,
final Configuration configuration
) throws Exception {
super(configuration);
this.graph = graph;
clearCache();
Authorizations authorizations = new InMemoryAuthorizations(VISIBILITY_STRING);
owlConfig.setMissingImportHandlingStrategy(MissingImportHandlingStrategy.SILENT);
loadOntologies(getConfiguration(), authorizations);
}
@Override
protected Concept importOntologyClass(OWLOntology o, OWLClass ontologyClass, File inDir, Authorizations authorizations) throws IOException {
InMemoryConcept concept = (InMemoryConcept) super.importOntologyClass(o, ontologyClass, inDir, authorizations);
conceptsCache.put(concept.getIRI(), concept);
return concept;
}
@Override
protected void setIconProperty(Concept concept, File inDir, String glyphIconFileName, String propertyKey, Authorizations authorizations) throws IOException {
if (glyphIconFileName == null) {
concept.setProperty(propertyKey, null, authorizations);
} else {
File iconFile = new File(inDir, glyphIconFileName);
if (!iconFile.exists()) {
throw new RuntimeException("Could not find icon file: " + iconFile.toString());
}
try {
try (InputStream iconFileIn = new FileInputStream(iconFile)) {
concept.setProperty(propertyKey, IOUtils.toByteArray(iconFileIn), authorizations);
}
} catch (IOException ex) {
throw new LumifyException("Failed to set glyph icon to " + iconFile, ex);
}
}
}
@Override
protected void addEntityGlyphIconToEntityConcept(Concept entityConcept, byte[] rawImg) {
entityConcept.setProperty(LumifyProperties.GLYPH_ICON.getPropertyName(), rawImg, null);
}
@Override
protected void storeOntologyFile(InputStream inputStream, IRI documentIRI) {
try {
byte[] inFileData = IOUtils.toByteArray(inputStream);
fileCache.add(new OwlData(documentIRI.toString(), inFileData));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
protected boolean isOntologyDefined(String iri) {
for (OwlData owlData : fileCache) {
if (owlData.iri.equals(iri)) {
return true;
}
}
return false;
}
@Override
protected List<OWLOntology> loadOntologyFiles(OWLOntologyManager m, OWLOntologyLoaderConfiguration config, IRI excludedIRI) throws Exception {
List<OWLOntology> loadedOntologies = new ArrayList<>();
for (OwlData owlData : fileCache) {
IRI lumifyBaseOntologyIRI = IRI.create(owlData.iri);
if (excludedIRI != null && excludedIRI.equals(lumifyBaseOntologyIRI)) {
continue;
}
try (InputStream lumifyBaseOntologyIn = new ByteArrayInputStream(owlData.data)) {
Reader lumifyBaseOntologyReader = new InputStreamReader(lumifyBaseOntologyIn);
LOGGER.info("Loading existing ontology: %s", owlData.iri);
OWLOntologyDocumentSource lumifyBaseOntologySource = new ReaderDocumentSource(lumifyBaseOntologyReader, lumifyBaseOntologyIRI);
OWLOntology o = m.loadOntologyFromOntologyDocument(lumifyBaseOntologySource, config);
loadedOntologies.add(o);
}
}
return loadedOntologies;
}
@Override
protected OntologyProperty addPropertyTo(
List<Concept> concepts,
String propertyIri,
String displayName,
PropertyType dataType,
Map<String, String> possibleValues,
Collection<TextIndexHint> textIndexHints,
boolean userVisible,
boolean searchable,
boolean addable,
String displayType,
String propertyGroup,
Double boost,
String validationFormula,
String displayFormula,
ImmutableList<String> dependentPropertyIris,
String[] intents
) {
checkNotNull(concepts, "concept was null");
InMemoryOntologyProperty property = getOrCreatePropertyType(
propertyIri,
dataType,
displayName,
possibleValues,
textIndexHints,
userVisible,
searchable,
addable,
displayType,
propertyGroup,
boost,
validationFormula,
displayFormula,
dependentPropertyIris,
intents
);
for (Concept concept : concepts) {
concept.getProperties().add(property);
}
checkNotNull(property, "Could not find property: " + propertyIri);
return property;
}
@Override
protected void getOrCreateInverseOfRelationship(Relationship fromRelationship, Relationship inverseOfRelationship) {
InMemoryRelationship fromRelationshipMem = (InMemoryRelationship) fromRelationship;
InMemoryRelationship inverseOfRelationshipMem = (InMemoryRelationship) inverseOfRelationship;
fromRelationshipMem.addInverseOf(inverseOfRelationshipMem);
inverseOfRelationshipMem.addInverseOf(fromRelationshipMem);
}
private InMemoryOntologyProperty getOrCreatePropertyType(
final String propertyIri,
final PropertyType dataType,
final String displayName,
Map<String, String> possibleValues,
Collection<TextIndexHint> textIndexHints,
boolean userVisible,
boolean searchable,
boolean addable,
String displayType,
String propertyGroup,
Double boost,
String validationFormula,
String displayFormula,
ImmutableList<String> dependentPropertyIris,
String[] intents
) {
InMemoryOntologyProperty property = (InMemoryOntologyProperty) getPropertyByIRI(propertyIri);
if (property == null) {
definePropertyOnGraph(graph, propertyIri, dataType, textIndexHints, boost);
property = new InMemoryOntologyProperty();
property.setDataType(dataType);
property.setUserVisible(userVisible);
property.setSearchable(searchable);
property.setAddable(addable);
property.setTitle(propertyIri);
property.setBoost(boost);
property.setDisplayType(displayType);
property.setPropertyGroup(propertyGroup);
property.setValidationFormula(validationFormula);
property.setDisplayFormula(displayFormula);
property.setDependentPropertyIris(dependentPropertyIris);
for (String intent : intents) {
property.addIntent(intent);
}
if (displayName != null && !displayName.trim().isEmpty()) {
property.setDisplayName(displayName);
}
property.setPossibleValues(possibleValues);
propertiesCache.put(propertyIri, property);
}
return property;
}
@Override
public void clearCache() {
// do nothing it's all in memory already.
}
@Override
public Iterable<Relationship> getRelationships() {
return new ConvertingIterable<InMemoryRelationship, Relationship>(relationshipsCache.values()) {
@Override
protected Relationship convert(InMemoryRelationship InMemRelationship) {
return InMemRelationship;
}
};
}
@Override
public Iterable<OntologyProperty> getProperties() {
return new ConvertingIterable<InMemoryOntologyProperty, OntologyProperty>(propertiesCache.values()) {
@Override
protected OntologyProperty convert(InMemoryOntologyProperty ontologyProperty) {
return ontologyProperty;
}
};
}
@Override
public String getDisplayNameForLabel(String relationshipIRI) {
InMemoryRelationship relationship = relationshipsCache.get(relationshipIRI);
checkNotNull(relationship, "Could not find relationship " + relationshipIRI);
return relationship.getDisplayName();
}
@Override
public OntologyProperty getPropertyByIRI(String propertyIRI) {
return propertiesCache.get(propertyIRI);
}
@Override
public Relationship getRelationshipByIRI(String relationshipIRI) {
return relationshipsCache.get(relationshipIRI);
}
@Override
public boolean hasRelationshipByIRI(String relationshipIRI) {
return getRelationshipByIRI(relationshipIRI) != null;
}
@Override
public Iterable<Concept> getConceptsWithProperties() {
return new ConvertingIterable<InMemoryConcept, Concept>(conceptsCache.values()) {
@Override
protected Concept convert(InMemoryConcept concept) {
return concept;
}
};
}
@Override
public Concept getEntityConcept() {
return conceptsCache.get(InMemoryOntologyRepository.ENTITY_CONCEPT_IRI);
}
@Override
public Concept getParentConcept(Concept concept) {
for (String key : conceptsCache.keySet()) {
if (key.equals(concept.getParentConceptIRI())) {
return conceptsCache.get(key);
}
}
return null;
}
@Override
public List<Concept> getConceptAndChildrenByIRI(String conceptIRI) {
List<Concept> concepts = new ArrayList<>();
concepts.add(conceptsCache.get(conceptIRI));
OWLOntologyManager m = OWLManager.createOWLOntologyManager();
try {
List<OWLOntology> owlOntologyList = loadOntologyFiles(m, owlConfig, null);
OWLClass owlClass = m.getOWLDataFactory().getOWLClass(IRI.create(conceptIRI));
for (OWLClassExpression child : owlClass.getSubClasses(new HashSet<>(owlOntologyList))) {
InMemoryConcept inMemoryConcept = conceptsCache.get(child.asOWLClass().getIRI().toString());
concepts.add(inMemoryConcept);
}
} catch (Exception e) {
throw new LumifyException("could not load ontology files");
}
return concepts;
}
@Override
public List<Concept> getAllLeafNodesByConcept(Concept concept) {
List<Concept> concepts = Lists.newArrayList(concept);
OWLOntologyManager m = OWLManager.createOWLOntologyManager();
try {
List<OWLOntology> owlOntologyList = loadOntologyFiles(m, owlConfig, null);
OWLClass owlClass = m.getOWLDataFactory().getOWLClass(IRI.create(((InMemoryConcept) concept).getConceptIRI()));
for (OWLClassExpression child : owlClass.getSubClasses(new HashSet<>(owlOntologyList))) {
InMemoryConcept inMemoryConcept = conceptsCache.get(child.asOWLClass().getIRI().toString());
concepts.add(inMemoryConcept);
}
} catch (Exception e) {
throw new LumifyException("could not load ontology files");
}
return concepts;
}
@Override
public Concept getOrCreateConcept(Concept parent, String conceptIRI, String displayName, File inDir) {
InMemoryConcept concept = (InMemoryConcept) getConceptByIRI(conceptIRI);
if (concept != null) {
return concept;
}
if (parent == null) {
concept = new InMemoryConcept(conceptIRI, null);
} else {
concept = new InMemoryConcept(conceptIRI, ((InMemoryConcept) parent).getConceptIRI());
}
concept.setProperty(LumifyProperties.TITLE.getPropertyName(), conceptIRI, null);
concept.setProperty(LumifyProperties.DISPLAY_NAME.getPropertyName(), displayName, null);
conceptsCache.put(conceptIRI, concept);
return concept;
}
@Override
public Relationship getOrCreateRelationshipType(
Iterable<Concept> domainConcepts,
Iterable<Concept> rangeConcepts,
String relationshipIRI,
String displayName,
String[] intents,
boolean userVisible
) {
Relationship relationship = getRelationshipByIRI(relationshipIRI);
if (relationship != null) {
return relationship;
}
List<String> domainConceptIris = toList(new ConvertingIterable<Concept, String>(domainConcepts) {
@Override
protected String convert(Concept o) {
return o.getIRI();
}
});
List<String> rangeConceptIris = toList(new ConvertingIterable<Concept, String>(rangeConcepts) {
@Override
protected String convert(Concept o) {
return o.getIRI();
}
});
InMemoryRelationship inMemRelationship = new InMemoryRelationship(relationshipIRI, displayName, domainConceptIris, rangeConceptIris, intents, userVisible);
relationshipsCache.put(relationshipIRI, inMemRelationship);
return inMemRelationship;
}
private static class OwlData {
public final String iri;
public final byte[] data;
public OwlData(String iri, byte[] data) {
this.iri = iri;
this.data = data;
}
}
protected Graph getGraph() {
return graph;
}
}