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

org.integratedmodelling.engine.modelling.kbox.ModelData Maven / Gradle / Ivy

There is a newer version: 0.9.10
Show newest version
/*******************************************************************************
 *  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 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