
org.integratedmodelling.engine.modelling.kbox.ModelData Maven / Gradle / Ivy
/*******************************************************************************
* Copyright (C) 2007, 2015:
*
* - Ferdinando Villa
* - integratedmodelling.org
* - any other authors listed in @author annotations
*
* All rights reserved. This file is part of the k.LAB software suite,
* meant to enable modular, collaborative, integrated
* development of interoperable data and model components. For
* details, see http://integratedmodelling.org.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the Affero General Public License
* Version 3 or 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
* Affero General Public License for more details.
*
* You should have received a copy of the Affero General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* The license is also available at: https://www.gnu.org/licenses/agpl.html
*******************************************************************************/
package org.integratedmodelling.engine.modelling.kbox;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.integratedmodelling.api.knowledge.IConcept;
import org.integratedmodelling.api.knowledge.IKnowledge;
import org.integratedmodelling.api.knowledge.IProperty;
import org.integratedmodelling.api.metadata.IMetadata;
import org.integratedmodelling.api.metadata.IModelMetadata;
import org.integratedmodelling.api.modelling.IClassification;
import org.integratedmodelling.api.modelling.IDataSource;
import org.integratedmodelling.api.modelling.IModel;
import org.integratedmodelling.api.modelling.IModelBean;
import org.integratedmodelling.api.modelling.INumericObserver;
import org.integratedmodelling.api.modelling.IObservable;
import org.integratedmodelling.api.modelling.IObserver;
import org.integratedmodelling.api.modelling.IScale;
import org.integratedmodelling.api.monitoring.IMonitor;
import org.integratedmodelling.api.time.ITemporalExtent;
import org.integratedmodelling.base.HashableObject;
import org.integratedmodelling.collections.Pair;
import org.integratedmodelling.common.ConceptPair;
import org.integratedmodelling.common.configuration.KLAB;
import org.integratedmodelling.common.interfaces.NetworkDeserializable;
import org.integratedmodelling.common.interfaces.NetworkSerializable;
import org.integratedmodelling.common.kim.KIMModel;
import org.integratedmodelling.common.kim.KIMPresenceObserver;
import org.integratedmodelling.common.metadata.Metadata;
import org.integratedmodelling.common.space.IGeometricShape;
import org.integratedmodelling.common.vocabulary.NS;
import org.integratedmodelling.exceptions.KlabException;
import org.integratedmodelling.exceptions.KlabRuntimeException;
/*
* the structure stored in an ObservationKbox for each model instead of storing the model itself.
* Facilitates and speeds up query and ranking, allowing ModelPrioritizer to obtain
* the contextual ranking as fast as possible. The actual objects are then loaded from their
* authoritative definitions. It can also be assigned a server ID so that the models can be
* queried and loaded from remote servers.
*/
public class ModelData extends HashableObject
implements IModelMetadata, NetworkDeserializable, NetworkSerializable {
public static Collection getModelData(IModel model, IMonitor monitor)
throws KlabException {
List ret = new ArrayList<>();
ret.add(new ModelData((KIMModel) model, monitor));
for (Pair attr : model.getAttributeObservers()) {
ModelData amd = new ModelData((KIMModel) model, attr.getFirst(), attr.getSecond(), monitor);
ret.add(amd);
}
if (model.getObjectSource(monitor) instanceof IDataSource) {
IObserver observer = new KIMPresenceObserver(model.getObservable());
// presence model - FIXME this does not work, wants observer. How did it work before?
ret.add(new ModelData((KIMModel) model, ModelData.PRESENCE_ATTRIBUTE, observer, monitor));
}
if (NS.isClass(model.getObservable())) {
List trs = NS.getExposedTraitsForType(model.getObservable().getTypeAsConcept());
if (trs != null) {
for (IObservable tr : trs) {
ret.add(new ModelData((KIMModel) model, tr, monitor));
/**
* TODO add presence model for context of given trait
*/
}
}
}
return ret;
}
/*
* customized for storage; uses per-object hashing to avoid messing with the
* caching in kbox.
*/
public static class Observable extends HashableObject {
public IKnowledge mainType = null;
// either a main concept or a main property for relationship models.
public IProperty mainProperty = null;
public IConcept obsType = null;
public IKnowledge inhrType = null;
public IKnowledge subjType = null;
public IConcept traiType = null;
public String formalName = null;
public int downTo = -1;
public Observable() {
}
Observable(ModelData data, IObserver observer, IObservable obs) {
this.mainType = obs.getType();
this.obsType = obs.getObservationType();
this.inhrType = obs.getInherentType();
this.subjType = obs.getContextType();
this.traiType = data.checkDiscretization(observer, obs.getTraitType());
this.formalName = obs.getFormalName();
this.downTo = obs.getDetailLevel();
}
Observable(ModelData data, IObserver observer, IObservable obs, IKnowledge contextType) {
this.mainType = obs.getType();
this.obsType = obs.getObservationType();
this.inhrType = obs.getInherentType();
this.subjType = contextType;
this.traiType = data.checkDiscretization(observer, obs.getTraitType());
this.formalName = obs.getFormalName();
this.downTo = obs.getDetailLevel();
}
Observable(IObservable obs) {
this.mainType = obs.getType();
this.obsType = obs.getObservationType();
this.inhrType = obs.getInherentType();
this.subjType = obs.getContextType();
// this.traiType = data.checkDiscretization(observer, obs.getTraitType());
this.formalName = obs.getFormalName();
this.downTo = obs.getDetailLevel();
}
/*
* for debugging
* TODO doesn't check level
*/
public boolean is(IObservable concept) {
boolean conceptOK = mainType.is(concept.getType());
boolean obsOK = obsType.is(concept.getObservationType());
boolean inhOK = (inhrType == null && concept.getInherentType() == null)
|| (inhrType != null && concept.getInherentType() != null && inhrType.is(concept
.getInherentType()));
boolean sbjOK = (subjType == null && concept.getContextType() == null)
|| (subjType != null && concept.getContextType() != null && subjType.is(concept
.getContextType()));
return conceptOK && obsOK && inhOK && sbjOK;
}
}
// if this is set as the dereification attribute, dereification is presence/absence
public static final String PRESENCE_ATTRIBUTE = "__PRESENCE__";
public Map ranks = null;
/*
* null serverId means that the object comes from this server. It is set externally after a query,
* depending on what context the query was triggered in. A REST command that performs a kbox query
* will typically pass the serverId it wants the retrieved objects to be tagged with.
*
* This is memorized so that the project will have to be reloaded even if the object is in the
* kbox from a previous import.
*/
public String serverId = null;
/*
* Observables. The type/oType/iType below is for the primary observable and it's
* redundant - may be removed, they allow performing queries on the primary observable
* easily, but for now we don't use them.
*/
public ArrayList observables = new ArrayList();
// stored TODO remove all these concepts and switch to the faster H2 kbox.
public String id;
public String name;
public String namespaceId;
public String projectId;
public String projectUrn;
public ArrayList traits;
public IKnowledge type;
public IConcept oType;
public IKnowledge iType;
public IKnowledge cType;
public boolean namespacePrivate;
public IConcept tType;
public boolean resolved;
// the next is unused and obsolete - remains to avoid changing the DB schema.
public boolean computed;
public boolean reifying;
public boolean inScenario;
public boolean hasDirectObjects;
public boolean hasDirectData;
public IGeometricShape spaceExtent;
public IMetadata metadata = new Metadata();
public int downTo = -1;
public Set neededCapabilities = new HashSet<>();
/*
* I don't really want to build a Lucene index for intervals that supports intersection with partially
* defined intervals, and using a LineString is not an option because Neo4j only supports one spatial
* property per object, so we store the temporal boundaries, if any, with two longs, and the day we
* need non-continuous time intervals we'll reconsider.
*/
public long timeStart = -1;
public long timeEnd = -1;
/*
* The following two may be true even when there is no space/time extent or multiplicity, and
* mean that the object is aware of the respective domain (so it requires it in contextualization) but
* doesn't have full scale information. They're always true if there is a scale for space/time, but
* the contrary isn't true. Objects that are nonspatial and/or nontemporal can be used in a spatial/temporal
* context, but objects that are spatial and/or temporal are given precedence, and will make a
* nonspatial/nontemporal context spatial/temporal. These are used to compute the domain specificity
* criterion.
*/
public boolean isSpatial;
public boolean isTemporal;
/*
* these allow to compute metrics of resolution given the intersection size of each domain and to check
* if there are more domains besides space and time. They should only be relied upon in resolved models with
* data, as computed models may not know their full scale until contextualized.
*/
public long timeMultiplicity = 0;
public long spaceMultiplicity = 0;
public long scaleMultiplicity = 0;
public String dereifyingAttribute;
public int discreteLevelsCount = 0;
// for the instantiator - do not remove
public ModelData() {
}
// painkiller method
public boolean isTemporal() {
return !(timeStart < 0 && timeEnd < 0);
}
@Override
public void deserialize(IModelBean object) {
if (!(object instanceof org.integratedmodelling.common.beans.Model)) {
throw new KlabRuntimeException("cannot deserialize model data from a "
+ object.getClass().getCanonicalName());
}
org.integratedmodelling.common.beans.Model bean = (org.integratedmodelling.common.beans.Model) object;
this.serverId = bean.getServerId();
this.id = bean.getId();
this.namespaceId = bean.getNamespaceId();
this.projectUrn = bean.getProjectUrn();
this.projectId = bean.getProjectId();
this.name = this.namespaceId + "." + this.id;
this.dereifyingAttribute = bean.getDereifyingAttribute();
this.ranks = bean.getRanks();
this.reifying = bean.isReification();
this.namespacePrivate = bean.isPrivateModel();
this.hasDirectData = bean.isHasDirectData();
// TODO the rest
}
@SuppressWarnings("unchecked")
@Override
public T serialize(Class extends IModelBean> desiredClass) {
if (!desiredClass.isAssignableFrom(org.integratedmodelling.common.beans.Model.class)) {
throw new KlabRuntimeException("cannot serialize model data to a "
+ desiredClass.getCanonicalName());
}
org.integratedmodelling.common.beans.Model ret = new org.integratedmodelling.common.beans.Model();
ret.setDereifyingAttribute(dereifyingAttribute);
ret.setHasDirectData(hasDirectData);
ret.setId(id);
ret.setInScenario(inScenario);
ret.setNamespaceId(namespaceId);
ret.setPrivateModel(namespacePrivate);
ret.setProjectUrn(projectUrn);
ret.setRanks(getRanks());
ret.setReification(reifying);
ret.setServerId(KLAB.NAME);
ret.setProjectId(projectId);
return (T) ret;
}
/**
* Use to store an auxiliary observable for a given model. Should also include the
* way this model has to be configured in order to provide the requested results.
*
* @param o
* @param auxObservable
* @param monitor
* @throws KlabException
*/
public ModelData(KIMModel o, IObservable auxObservable, IMonitor monitor) throws KlabException {
id = o.getId();
name = o.getName();
metadata = o.getMetadata();
namespaceId = o.getNamespace().getId();
projectId = o.getNamespace().getProject().getId();
if (o.getNamespace().getProject().isRemote()) {
serverId = o.getNamespace().getProject().getOriginatingNodeId();
}
IScale scale = o.getCoverage(monitor);
if (scale.getSpace() != null) {
spaceExtent = (IGeometricShape) scale.getSpace().getExtent();
// may be null when we just say 'over space'.
if (spaceExtent != null) {
spaceMultiplicity = scale.getSpace().getMultiplicity();
}
isSpatial = true;
}
if (scale.getTime() != null) {
ITemporalExtent timeExtent = scale.getTime().getExtent();
if (timeExtent != null) {
if (timeExtent.getStart() != null) {
timeStart = timeExtent.getStart().getMillis();
}
if (timeExtent.getEnd() != null) {
timeEnd = timeExtent.getEnd().getMillis();
}
timeMultiplicity = scale.getTime().getMultiplicity();
}
isTemporal = true;
}
observables.add(new Observable(auxObservable));
type = auxObservable.getType();
oType = auxObservable.getObservationType();
iType = auxObservable.getInherentType();
cType = auxObservable.getContextType();
tType = checkDiscretization(o.getObserver(), auxObservable.getTraitType());
downTo = auxObservable.getDetailLevel();
for (Pair tt : NS.getTraits(auxObservable.getType())) {
if (traits == null) {
traits = new ArrayList();
}
traits.add(new ConceptPair(tt.getFirst(), tt.getSecond()));
}
namespacePrivate = o.isPrivate();
// computed = !o.needsResolution();
inScenario = o.getNamespace().isScenario();
reifying = o.isInstantiator();
resolved = o.isResolved();
hasDirectData = o.hasDatasource();
hasDirectObjects = o.hasObjectSource();
}
public ModelData(KIMModel o, IMonitor monitor) throws KlabException {
id = o.getId();
name = o.getName();
metadata = o.getMetadata();
namespaceId = o.getNamespace().getId();
projectId = o.getNamespace().getProject().getId();
if (o.getNamespace().getProject().isRemote()) {
serverId = o.getNamespace().getProject().getOriginatingNodeId();
}
IScale scale = o.getCoverage(monitor);
if (scale != null) {
if (scale.getSpace() != null) {
spaceExtent = (IGeometricShape) scale.getSpace().getExtent();
// may be null when we just say 'over space'.
if (spaceExtent != null) {
spaceMultiplicity = scale.getSpace().getMultiplicity();
}
isSpatial = true;
}
if (scale.getTime() != null) {
ITemporalExtent timeExtent = scale.getTime().getExtent();
if (timeExtent != null) {
if (timeExtent.getStart() != null) {
timeStart = timeExtent.getStart().getMillis();
}
if (timeExtent.getEnd() != null) {
timeEnd = timeExtent.getEnd().getMillis();
}
timeMultiplicity = scale.getTime().getMultiplicity();
}
isTemporal = true;
}
}
for (int i = 0; i < o.getObservables().size(); i++) {
IObservable obs = o.getObservables().get(i);
// if (i == 0 && o.getObserver() != null) {
// obs = o.getObserver().getObservable();
// }
observables.add(new Observable(this, o.getObserver(), obs));
}
/*
* the type of the MAIN observable. We use the observer types (if any) for
* matching, but we extract the traits from this one, so that any of our
* model's observables will match them.
*/
IObservable obs = o.getObservable();
type = obs.getType();
oType = obs.getObservationType();
iType = obs.getInherentType();
cType = obs.getContextType();
tType = checkDiscretization(o.getObserver(), obs.getTraitType());
downTo = obs.getDetailLevel();
for (Pair tt : NS.getTraits(o.getObservable().getType())) {
if (traits == null) {
traits = new ArrayList();
}
traits.add(new ConceptPair(tt.getFirst(), tt.getSecond()));
}
namespacePrivate = o.isPrivate();
// computed = !o.needsResolution();
inScenario = o.getNamespace().isScenario();
reifying = o.isInstantiator();
resolved = o.isResolved();
hasDirectData = o.hasDatasource();
hasDirectObjects = o.hasObjectSource();
}
/**
* If the passed observer is a numeric observer that discretizes the
* value, do not use the trait type but keep a count of the highest number of levels
* to indicate that the data have lower precision. We can use that for
* ranking.
*
* @param observer
* @param traitType
* @return
*/
IConcept checkDiscretization(IObserver observer, IConcept traitType) {
if (observer == null || traitType == null) {
return traitType;
}
if (observer instanceof INumericObserver) {
IClassification zc = ((INumericObserver) observer).getDiscretization();
if (zc != null) {
if (this.discreteLevelsCount < zc.getClassifiers().size()) {
this.discreteLevelsCount = zc.getClassifiers().size();
}
traitType = null;
}
}
return traitType;
}
public Map getRanks() {
return ranks;
}
public ModelData(KIMModel model, String dereifyingAttribute, IObserver observer, IMonitor monitor)
throws KlabException {
this(model, monitor);
/*
* reset observable
*/
this.type = observer.getObservable().getType();
this.oType = observer.getObservable().getObservationType();
this.iType = observer.getObservable().getInherentType();
this.cType = model.getObservable().getType();
this.tType = checkDiscretization(observer, observer.getObservable().getTraitType());
this.downTo = observer.getObservable().getDetailLevel();
for (Pair tt : NS.getTraits(type)) {
if (traits == null) {
traits = new ArrayList();
}
traits.add(new ConceptPair(tt.getFirst(), tt.getSecond()));
}
this.observables.clear();
/*
* presence (of context) should not include the context
*/
Observable obs = dereifyingAttribute.equals(PRESENCE_ATTRIBUTE)
? new Observable(this, observer, observer.getObservable())
: new Observable(this, observer, observer.getObservable(), model.getObservable().getType());
this.observables.add(obs);
this.reifying = false;
this.hasDirectData = true;
this.hasDirectObjects = false;
/*
* add attribute
*/
this.dereifyingAttribute = dereifyingAttribute;
}
@Override
public String toString() {
String ret = name + "\nMain observable:\n";
ret += " <" + type + " " + oType + " " + (iType == null ? "N/A" : iType)
+ (tType == null ? " N/A" : (" " + tType)) + ">\n ";
ret += "Project ID: " + projectUrn + "\n";
ret += "All observables:\n";
for (Observable ob : observables) {
ret += " <" + ob.mainType + " " + ob.obsType + " " + (ob.inhrType == null ? "N/A" : ob.inhrType)
+ " " + (ob.subjType == null ? "N/A" : ob.subjType)
+ (ob.traiType == null ? " N/A" : (" " + ob.traiType)) + ">\n ";
}
ret += isTemporal() ? "[" + (timeStart < 0 ? "*" : "" + timeStart) + ","
+ (timeEnd < 0 ? "*" : "" + timeEnd) + "] (#" + timeMultiplicity + ")" : "NoTime";
ret += " ";
ret += spaceExtent != null ? spaceExtent + " (#" + spaceMultiplicity + ")" : "NoSpace";
ret += "\n ";
if (traits != null) {
for (int i = 0; i < traits.size(); i++) {
ret += "T<" + traits.get(i).getFirst() + " = " + traits.get(i).getSecond() + ">\n ";
}
}
ret += namespacePrivate ? "private " : "public ";
ret += computed ? "computed " : "notComputed ";
ret += inScenario ? "inScenario " : "notInScenario ";
ret += reifying ? "reification " : "notReification ";
ret += hasDirectData ? "directData " : "noDirectData ";
ret += hasDirectObjects ? "directObjects " : "noDirectObjects ";
return ret;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
return obj instanceof ModelData && name.equals(((ModelData) obj).name);
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return name.hashCode();
}
public ArrayList getTraits() {
// no nulls, cleaner code.
return traits == null ? new ArrayList() : traits;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy