org.integratedmodelling.engine.modelling.kbox.ModelKbox Maven / Gradle / Ivy
The newest version!
package org.integratedmodelling.engine.modelling.kbox;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.h2gis.utilities.SpatialResultSet;
import org.integratedmodelling.api.engine.IModelingEngine;
import org.integratedmodelling.api.knowledge.IConcept;
import org.integratedmodelling.api.metadata.IModelMetadata;
import org.integratedmodelling.api.modelling.IModel;
import org.integratedmodelling.api.modelling.IObservableSemantics;
import org.integratedmodelling.api.modelling.resolution.IModelPrioritizer;
import org.integratedmodelling.api.modelling.resolution.IResolutionScope;
import org.integratedmodelling.api.monitoring.IMonitor;
import org.integratedmodelling.api.space.ISpatialExtent;
import org.integratedmodelling.api.time.ITemporalExtent;
import org.integratedmodelling.common.beans.Model;
import org.integratedmodelling.common.beans.requests.ModelQuery;
import org.integratedmodelling.common.configuration.KLAB;
import org.integratedmodelling.common.model.Models;
import org.integratedmodelling.common.space.IGeometricShape;
import org.integratedmodelling.common.utils.Escape;
import org.integratedmodelling.common.vocabulary.NS;
import org.integratedmodelling.common.vocabulary.Observables;
import org.integratedmodelling.engine.geospace.Geospace;
import org.integratedmodelling.engine.geospace.literals.ShapeValue;
import org.integratedmodelling.engine.kbox.sql.SQL;
import org.integratedmodelling.engine.kbox.sql.h2.H2Kbox;
import org.integratedmodelling.engine.kbox.sql.h2.schema.CompoundSchema;
import org.integratedmodelling.engine.modelling.resolver.ResolutionScope;
import org.integratedmodelling.exceptions.KlabException;
import org.integratedmodelling.exceptions.KlabRuntimeException;
import com.vividsolutions.jts.geom.Geometry;
public class ModelKbox extends ObservableKbox {
private static ModelKbox _this;
private boolean workRemotely = !KLAB.CONFIG.isOffline();
public static ModelKbox get() {
if (_this == null) {
H2Kbox.set("models2_"
+ KBOX_VERSION, new ModelKbox("models2_" + KBOX_VERSION, KLAB.ENGINE.getMonitor()));
_this = (ModelKbox) H2Kbox.get("models2_" + KBOX_VERSION);
}
return _this;
}
private ModelKbox(String name, IMonitor monitor) {
super(name, monitor);
setSchema(Model.class, new CompoundSchema(Model.class) {
@Override
public String getTableName() {
return getMainTableId();
}
@Override
public String getCreateSQL() {
String ret = "CREATE TABLE model ("
+ "oid LONG, "
+ "serverid VARCHAR(64), "
+ "id VARCHAR(256), "
+ "name VARCHAR(256), "
+ "namespaceid VARCHAR(128), "
+ "projectid VARCHAR(128), "
+ "typeid LONG, "
+ "otypeid LONG, "
+ "isprivate BOOLEAN, "
+ "isresolved BOOLEAN, "
+ "isreification BOOLEAN, "
+ "inscenario BOOLEAN, "
+ "hasdirectobjects BOOLEAN, "
+ "hasdirectdata BOOLEAN, "
+ "timestart LONG, "
+ "timeend LONG, "
+ "isspatial BOOLEAN, "
+ "istemporal BOOLEAN, "
+ "timemultiplicity LONG, "
+ "spacemultiplicity LONG, "
+ "scalemultiplicity LONG, "
+ "dereifyingattribute VARCHAR(256), "
+ "space GEOMETRY, "
+ "); "
+ "CREATE INDEX model_oid_index ON model(oid); "
+ "CREATE SPATIAL INDEX model_space ON model(space);";
return ret;
}
});
setSerializer(Model.class, new Serializer() {
private String cn(Object o) {
return o == null ? "" : o.toString();
}
@Override
public String serialize(Model model, Schema schema, long primaryKey, long foreignKey) {
long tid = requireConceptId(model.getObservableConcept());
long oid = requireConceptId(model.getObservationConcept());
String ret = "INSERT INTO model VALUES ("
+ primaryKey + ", "
+ "'" + cn(model.getServerId()) + "', "
+ "'" + cn(model.getId()) + "', "
+ "'" + cn(model.getName()) + "', "
+ "'" + cn(model.getNamespaceId()) + "', "
+ "'" + cn(model.getProjectId()) + "', "
+ tid + ", "
+ oid + ", "
+ (model.isPrivateModel() ? "TRUE" : "FALSE") + ", "
+ (model.isResolved() ? "TRUE" : "FALSE") + ", "
+ (model.isReification() ? "TRUE" : "FALSE") + ", "
+ (model.isInScenario() ? "TRUE" : "FALSE") + ", "
+ (model.isHasDirectObjects() ? "TRUE" : "FALSE") + ", "
+ (model.isHasDirectData() ? "TRUE" : "FALSE") + ", "
+ model.getTimeStart() + ", "
+ model.getTimeEnd() + ", "
+ (model.isSpatial() ? "TRUE" : "FALSE") + ", "
+ (model.isTemporal() ? "TRUE" : "FALSE") + ", "
+ model.getTimeMultiplicity() + ", "
+ model.getSpaceMultiplicity() + ", "
+ model.getScaleMultiplicity() + ", "
+ "'" + cn(model.getDereifyingAttribute()) + "', "
+ "'"
+ (model.getShape() == null
? "GEOMETRYCOLLECTION EMPTY"
: model.getShape().getStandardizedGeometry().toString())
+ "'"
+ ");";
if (model.getMetadata() != null && model.getMetadata().getData().size() > 0) {
storeMetadataFor(primaryKey, model.getMetadata());
}
return ret;
}
});
}
/**
* Pass the output of queryModelData to a contextual prioritizer and return the ranked
* list of IModels. If we're a personal engine, also broadcast the query to the
* network and merge results before returning.
*
* @param observable
* @param context
* @return models resulting from query, best first.
* @throws KlabException
*/
public List query(IObservableSemantics observable, IResolutionScope context)
throws KlabException {
IModelPrioritizer prioritizer = context.getPrioritizer2();
ModelQueryResult ret = new ModelQueryResult(prioritizer, ((ResolutionScope) context).getMonitor());
Set local = new HashSet<>();
/*
* only query locally if we've seen a model before.
*/
if (database.hasTable("model")) {
for (Model md : queryModels(observable, context)) {
local.add(md);
ret.addModel(md);
}
}
/**
* If we're a modeling engine, dispatch the request to all nodes that allow it,
* which we do simply by using the result list as a distributed operation.
*/
if (KLAB.ENGINE instanceof IModelingEngine && workRemotely) {
ModelQuery mquery = new ModelQuery();
mquery.setObservable(KLAB.MFACTORY
.adapt(observable, org.integratedmodelling.common.beans.Observable.class));
mquery.setScope(KLAB.MFACTORY.adapt(context, org.integratedmodelling.common.beans.Scope.class));
ret.setQuery(mquery);
KLAB.ENGINE.getNetwork().broadcast(ret, ((ResolutionScope) context).getMonitor());
}
return ret;
}
/**
* Find and deserialize all modeldata matching the parameters. Do not rank or
* anything.
*
* @param observable
* @param context
* @throws KlabException
*/
public List queryModels(IObservableSemantics observable, IResolutionScope context)
throws KlabException {
List ret = new ArrayList<>();
if (!database.hasTable("model")) {
return ret;
}
String query = "SELECT model.oid FROM model WHERE ";
String typequery = observableQuery(observable.getType(), context);
if (typequery == null) {
return ret;
}
query += "(" + scopeQuery(context, observable) + ")";
query += " AND (" + typequery + ")";
if (context.getScale().getSpace() != null) {
String sq = spaceQuery(context.getScale().getSpace());
if (!sq.isEmpty()) {
query += " AND (" + sq + ")";
}
}
String tquery = timeQuery(context.getScale().getTime());
if (!tquery.isEmpty()) {
query += " AND (" + tquery + ");";
}
// KLAB.info(query);
final List oids = database.queryIds(query);
for (long l : oids) {
Model model = retrieveModel(l);
if (model != null) {
ret.add(model);
} else {
KLAB.warn("kbox is out of sync with knowledge base");
}
}
KLAB.info(KLAB.ENGINE.getName() + ": model query for "
+ (context.isForInstantiation() ? "instantiation of " : "explanation of ") + observable
+ " found "
+ (ret.size() == 1 ? ret.get(0).getName() : (ret.size() + " models")));
return ret;
}
private String observableQuery(IConcept observable, IResolutionScope context) {
/*
* remove any transformations before querying
*/
observable = Observables.getUntransformedObservable(observable);
Set ids = this.getCompatibleTypeIds(observable, context);
if (ids == null || ids.size() == 0) {
return null;
}
String ret = "";
for (long id : ids) {
ret += (ret.isEmpty() ? "" : ", ") + id;
}
return "typeid IN (" + ret + ")";
}
/*
* select models that are [instantiators if required] AND:] [private and in the home
* namespace if not dummy OR] (non-private and non-scenario) OR (in any of the
* scenarios in the context).
*/
private String scopeQuery(IResolutionScope context, IObservableSemantics observable) {
String ret = "";
String namespaceId = context.getResolutionNamespace() == null ? DUMMY_NAMESPACE_ID
: context.getResolutionNamespace().getId();
if (!namespaceId.equals(DUMMY_NAMESPACE_ID)) {
// ret += "(model.isprivate AND model.namespaceid = '" + namespaceId
// + "')";
ret += "(model.namespaceid = '" + namespaceId + "')";
}
ret += (ret.isEmpty() ? "" : " OR ") + "((NOT model.isprivate) AND (NOT model.inscenario))";
if (context.getScenarios() != null && context.getScenarios().size() > 0) {
ret += " OR (" + joinStringConditions("model.namespaceid", context.getScenarios(), "OR") + ")";
}
if (NS.isCountable(observable)) {
if (context.isForInstantiation()
|| ((org.integratedmodelling.common.vocabulary.ObservableSemantics) observable)
.isInstantiator()) {
ret = "(" + ret + ") AND model.isreification";
} else {
ret = "(" + ret + ") AND (NOT model.isreification)";
}
}
return ret;
}
/*
* select models that intersect the given space or have no space at all.
*/
private String spaceQuery(ISpatialExtent space) {
if (space.getExtent().getShape().isEmpty()) {
return "";
}
return "model.space && '"
+ ((IGeometricShape) (space.getExtent().getShape())).getStandardizedGeometry()
+ "' OR ST_IsEmpty(model.space)";
}
/*
* Entirely TODO. For initialization we should use time only to select for most
* current info - either closer to the context or to today if time is null. For
* dynamic models we should either not have a context or cover the context. Guess this
* is the job of the prioritizer, and we should simply let anything through except
* when we look for process models.
*/
private String timeQuery(ITemporalExtent time) {
String ret = "";
boolean checkBoundaries = false;
if (time != null && checkBoundaries) {
ret = "(timestart == -1 AND timeend == -1) OR (";
long start = time.getStart() == null ? -1 : time.getStart().getMillis();
long end = time.getEnd() == null ? -1 : time.getEnd().getMillis();
if (start > 0 && end > 0) {
ret += "timestart >= " + start + " AND timeend <= " + end;
} else if (start > 0) {
ret += "timestart >= " + start;
} else if (end > 0) {
ret += "timeend <= " + end;
}
ret += ")";
}
return ret;
}
public List retrieveAll() throws KlabException {
List ret = new ArrayList<>();
if (!database.hasTable("model")) {
return ret;
}
for (long oid : database.queryIds("SELECT oid FROM model;")) {
ret.add(retrieveModel(oid));
}
return ret;
}
public Model retrieveModel(long oid) throws KlabException {
final Model ret = new Model();
database.query("SELECT * FROM model WHERE oid = " + oid, new SQL.SimpleResultHandler() {
@Override
public void onRow(ResultSet rs) {
try {
SpatialResultSet srs = rs.unwrap(SpatialResultSet.class);
long tyid = srs.getLong(7);
long obid = srs.getLong(8);
ret.setName(srs.getString(4));
IConcept mtype = getType(tyid);
IConcept otype = getType(obid);
// if (mtype == null || otype == null) {
// return;
// }
ret.setObservableConcept(mtype);
ret.setObservationConcept(otype);
ret.setObservable(getTypeDefinition(tyid));
ret.setObservationType(getTypeDefinition(obid));
ret.setServerId(nullify(srs.getString(2)));
ret.setId(srs.getString(3));
ret.setNamespaceId(srs.getString(5));
ret.setProjectId(nullify(srs.getString(6)));
ret.setPrivateModel(srs.getBoolean(9));
ret.setResolved(srs.getBoolean(10));
ret.setReification(srs.getBoolean(11));
ret.setInScenario(srs.getBoolean(12));
ret.setHasDirectObjects(srs.getBoolean(13));
ret.setHasDirectData(srs.getBoolean(14));
ret.setTimeStart(srs.getLong(15));
ret.setTimeEnd(srs.getLong(16));
ret.setSpatial(srs.getBoolean(17));
ret.setTemporal(srs.getBoolean(18));
ret.setTimeMultiplicity(srs.getLong(19));
ret.setSpaceMultiplicity(srs.getLong(20));
ret.setScaleMultiplicity(srs.getLong(21));
ret.setDereifyingAttribute(nullify(srs.getString(22)));
Geometry geometry = srs.getGeometry(23);
if (!geometry.isEmpty()) {
ret.setShape(new ShapeValue(geometry, Geospace.get().getDefaultCRS())); // +
}
} catch (SQLException e) {
throw new KlabRuntimeException(e);
}
}
});
ret.setMetadata(getMetadataFor(oid));
return ret;
}
@Override
protected String getMainTableId() {
return "model";
}
/**
* @param name
* @return true if model with given id exists in database
* @throws KlabException
*/
public boolean hasModel(String name) throws KlabException {
if (!database.hasTable("model")) {
return false;
}
return database.queryIds("SELECT oid FROM model WHERE name = '" + name + "';").size() > 0;
}
@Override
protected int deleteAllObjectsWithNamespace(String namespaceId) throws KlabException {
int n = 0;
for (long oid : database
.queryIds("SELECT oid FROM model where namespaceid = '" + Escape.forSQL(namespaceId)
+ "';")) {
deleteObjectWithId(oid);
n++;
}
return n;
}
@Override
protected void deleteObjectWithId(long id) throws KlabException {
database.execute("DELETE FROM model WHERE oid = " + id);
deleteMetadataFor(id);
}
@Override
public long store(Object o) throws KlabException {
ArrayList