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

org.integratedmodelling.engine.modelling.resolver.Resolver Maven / Gradle / Ivy

The 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.resolver;

import java.text.NumberFormat;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.integratedmodelling.api.knowledge.IConcept;
import org.integratedmodelling.api.knowledge.IExpression;
import org.integratedmodelling.api.knowledge.ISemantic;
import org.integratedmodelling.api.modelling.IActiveDirectObservation;
import org.integratedmodelling.api.modelling.IActiveSubject;
import org.integratedmodelling.api.modelling.IConditionalObserver;
import org.integratedmodelling.api.modelling.ICoverage;
import org.integratedmodelling.api.modelling.IDependency;
import org.integratedmodelling.api.modelling.IDerivedObserver;
import org.integratedmodelling.api.modelling.IDirectObservation;
import org.integratedmodelling.api.modelling.IMediatingObserver;
import org.integratedmodelling.api.modelling.IModel;
import org.integratedmodelling.api.modelling.INamespace;
import org.integratedmodelling.api.modelling.IObjectSource;
import org.integratedmodelling.api.modelling.IObservableSemantics;
import org.integratedmodelling.api.modelling.IObserver;
import org.integratedmodelling.api.modelling.IState;
import org.integratedmodelling.api.modelling.ISubject;
import org.integratedmodelling.api.modelling.contextualization.IContextualizer;
import org.integratedmodelling.api.modelling.resolution.IResolutionScope;
import org.integratedmodelling.api.modelling.resolution.ISubjectResolver;
import org.integratedmodelling.api.monitoring.IMonitor;
import org.integratedmodelling.api.monitoring.Messages;
import org.integratedmodelling.api.provenance.IProvenance;
import org.integratedmodelling.collections.Pair;
import org.integratedmodelling.common.kim.KIMModel;
import org.integratedmodelling.common.kim.KIMObserver;
import org.integratedmodelling.common.model.Coverage;
import org.integratedmodelling.common.states.States;
import org.integratedmodelling.common.vocabulary.NS;
import org.integratedmodelling.common.vocabulary.ObservableSemantics;
import org.integratedmodelling.engine.modelling.kbox.ModelKbox;
import org.integratedmodelling.engine.modelling.runtime.DirectObservation;
import org.integratedmodelling.engine.modelling.runtime.DirectSubjectInstantiator;
import org.integratedmodelling.engine.modelling.runtime.Subject;
import org.integratedmodelling.exceptions.KlabException;
import org.integratedmodelling.lang.LogicalConnector;

/**
 * A resolver has a top-level resolution context, set to ROOT if it's a top resolver, or
 * to the context that resolved the subject if it's called within one.
 * 
 * @author Ferd
 *
 */
public class Resolver extends BaseResolver implements ISubjectResolver {

    ResolutionScope    context;
    Collection scenarios = null;
    IProvenance.Action cause;

    public Resolver(IResolutionScope context, IProvenance.Action cause) {
        this.context = (ResolutionScope) context;
        this.cause = cause;
    }

    /*
     * this constructor overrides the scenarios in the original context.
     */
    public Resolver(IResolutionScope context, Collection scenarios, IProvenance.Action cause) {
        this.context = (ResolutionScope) context;
        if (scenarios != null) {
            this.scenarios = scenarios;
        }
        this.cause = cause;
    }

    /*
     * Entry point when a subject is initialized from a context subject's resolution
     * strategy. (non-Javadoc)
     * 
     * @see org.integratedmodelling.api.modelling.resolution.ISubjectResolver#resolve
     * (org. integratedmodelling.api.modelling.ISubject,
     * org.integratedmodelling.api.monitoring.IMonitor)
     */
    @Override
    public ICoverage resolve(IActiveDirectObservation subject, IMonitor monitor) throws KlabException {
        ResolutionScope ctx = context.forSubject(subject, scenarios);
        ctx.setMonitor(monitor);
        ctx.setCause(cause);
        ((DirectObservation) subject).setResolutionContext(ctx);
        ICoverage ret = resolve(subject, ctx);
        // if (ret != null) {
        // ctx.execute(null);
        // }
        return ret;
    }

    /*
     * Entry point when an observable is resolved from a subject's observe().
     * (non-Javadoc)
     * 
     * @see org.integratedmodelling.api.modelling.resolution.ISubjectResolver#resolve
     * (org. integratedmodelling.api.modelling.IObservable,
     * org.integratedmodelling.api.monitoring.IMonitor)
     */
    @Override
    public ICoverage resolve(IObservableSemantics observable, IMonitor monitor, IActiveSubject contextSubject)
            throws KlabException {

        ResolutionScope ctx = context.forObservable(observable, scenarios);
        if (NS.isThing(observable)) {
            ctx.contextSubject = contextSubject;
        }
        ctx.setMonitor(monitor);
        ctx.setCause(cause);
        ICoverage ret = resolve(observable, ctx);
        if (ret.isRelevant()) {
            ctx.finish();
        }

        return ret;
    }

    @Override
    public IConcept resolve(IConcept abstractConcept, IActiveDirectObservation subject) throws KlabException {
        // TODO Auto-generated method stub
        return abstractConcept;
    }

    private ICoverage resolve(IModel model, ResolutionScope context) throws KlabException {

        if (context.isUnsatisfiable)
            return Coverage.EMPTY;

        if (context.monitor.hasErrors()) {
            return Coverage.EMPTY;
        }

        if (model.getObserver() != null) {
            if (resolve(model.getObserver(), context
                    .forObserver(model.getObserver(), getDatasource(model, context.monitor))).isEmpty()) {
                return Coverage.EMPTY;
            }
        }

        /*
         * resolve all model dependencies, contextualizing at every group if the model is
         * a subject model. This will call finish() on the context passed.
         */
        ICoverage ret = resolveDependencies(model, context, model.getNamespace());

        if (ret.isEmpty())
            return ret;

        IObjectSource objectSource = model.getObjectSource(context.getMonitor());
        if (objectSource != null) {
            context.forObjectSource(objectSource).finish();
        }

        if (!context.monitor.hasErrors()) {
            context.acceptModel();
        }

        return context.finish();

    }

    /*
     * resolves dependencies for a model, subject or observer, also ensuring that any
     * semantic-induced dependencies are in.
     */
    private ICoverage resolveDependencies(ISemantic observation, ResolutionScope context, INamespace namespace)
            throws KlabException {

        if (context.isUnsatisfiable)
            return Coverage.EMPTY;

        ICoverage ret = Coverage.FULL(context.getScale());

        for (List dg : groupDependencies(observation, context)) {

            ResolutionScope cd = context.forGroup(LogicalConnector.INTERSECTION);
            ret = cd.getCoverage();
            for (IDependency d : dg) {
                if (resolve(d, cd.forDependency(d, namespace)).isEmpty()) {
                    return Coverage.EMPTY;
                }
            }
            ret = cd.finish();
        }

        return ret;
    }

    private ICoverage resolve(IObserver observer, ResolutionScope context) throws KlabException {

        if (context.isUnsatisfiable)
            return Coverage.EMPTY;

        if (observer instanceof IMediatingObserver
                && ((IMediatingObserver) observer).getMediatedObserver() != null) {
            if (resolve(((IMediatingObserver) observer).getMediatedObserver(), context
                    .forMediatedObserver(((IMediatingObserver) observer).getMediatedObserver())).isEmpty()) {
                return Coverage.EMPTY;
            }
        } else if (observer instanceof IConditionalObserver) {

            int i = 0;

            ResolutionScope cd = context.forGroup(LogicalConnector.UNION);
            for (Pair observedModelPair : ((IConditionalObserver) observer)
                    .getModels()) {
                IModel model = observedModelPair.getFirst();
                IExpression expression = observedModelPair.getSecond();
                resolve(model, cd.forCondition(expression, i++));
            }
            cd.finish();

            /*
             * model dependencies are only relevant when we have conditional observers; we
             * link them to the observer. FIXME: see
             * https://integratedmodelling.org/jira/browse/KLAB-100
             */
            IResolutionScope dc = context.forGroup(LogicalConnector.UNION);
            for (IDependency d : ((KIMModel) context.model)
                    .getAllDependencies(context.getMonitor(), context)) {
                if (resolve(d, ((ResolutionScope) dc).forDependency(d, observer.getModel().getNamespace()))
                        .isEmpty())
                    break;
            }
            ((ResolutionScope) dc).finish();

        } else if (context.datasource != null) {

            if (context.forDatasource(context.datasource).finish().isEmpty()) {
                return Coverage.EMPTY;
            }
        } else if (observer.needsResolution()) {

            /*
             * resolve the observer using the k.LAB network.
             */
            IObservableSemantics observable = observer.getObservable();
            if (resolve(observable, context.forObservable(observable)).isEmpty()) {

                if (observer instanceof IDerivedObserver) {
                    /*
                     * try the indirect way.
                     */
                    ICoverage indirectCoverage = resolveIndirect((IDerivedObserver) observer, context);
                    if (indirectCoverage.isEmpty()) {
                        return Coverage.EMPTY;
                    } 
                    context.coverage = indirectCoverage;
                } else {
                    return Coverage.EMPTY;
                }
            }
        }

        List alldeps = ((KIMObserver) observer)
                .getAllDependencies(context.getMonitor(), context);
        if (alldeps.size() > 0) {
            /*
             * FIXME/TODO: UNION is necessary to capture partial coverages, but it should
             * be for COUNTABLE INHERENTS, which means separate groups should be defined
             * for those. Qualities should use INTERSECTION.
             * https://integratedmodelling.org/jira/browse/KLAB-100
             */
            IResolutionScope dc = context.forGroup(LogicalConnector.UNION);
            for (IDependency d : alldeps) {
                if (resolve(d, ((ResolutionScope) dc).forDependency(d, observer.getNamespace())).isEmpty())
                    break;
            }
            ((ResolutionScope) dc).finish();
        }

        return context.finish();
    }

    private ICoverage resolveIndirect(IDerivedObserver observer, ResolutionScope context)
            throws KlabException {

        ICoverage cov = Coverage.EMPTY;

        int i = 0;

        for (IObservableSemantics observable : observer.getAlternativeObservables()) {

            context.monitor.info(((i == 0)
                    ? ("no direct observations of " + observer.getObservable().getType() + " found :") : "")
                    + "resolving " + observable.getType(), Messages.INFOCLASS_MODEL);

            ResolutionScope childScope = context.forIndirectObservable(observable, observer);
            ICoverage ocov = resolve(observable, childScope);
            if (ocov.isRelevant()) {
                IContextualizer contextualizer = childScope.observer == null
                        ? (childScope.model == null ? null
                                : childScope.model.getContextualizer(childScope, context.monitor))
                        : childScope.observer.getContextualizer(childScope, context.monitor);
                if (contextualizer == null) {
                    continue;
                }

                cov = cov.or(ocov);

                if (NS.isThing(observable)) {
                    ((Coverage)cov).forceRelevant();
                }
                
                ICoverage acov = observer.acceptAlternativeContextualizer(contextualizer, childScope.coverage);
                if (acov.isComplete()) {
                    cov = acov;
                }

            }
            i++;
        }
        return cov;
    }

    private ICoverage resolve(IDirectObservation subject, ResolutionScope context) throws KlabException {

        if (context.isUnsatisfiable)
            return Coverage.EMPTY;

        if (!((DirectObservation) subject).isInitialized()) {

            ICoverage cov = resolve(subject.getObservable().getSemantics(), context
                    .forObservable(subject.getObservable().getSemantics())
                    .forExplanatoryModel());

            if (cov.isEmpty()) {
                context.getMonitor().error("cannot resolve subject " + subject.getName());
                return cov;
            }
        }

        ICoverage ret = resolveDependencies(subject, context, subject.getNamespace());

        if (ret.isEmpty())
            return ret;

        return context.finish();
    }

    private ICoverage resolve(IDependency dependency, ResolutionScope context) throws KlabException {

        if (context.isUnsatisfiable)
            return Coverage.EMPTY;

        IObservableSemantics observable = dependency.getObservable();
        ICoverage cov = null;

        if (dependency.getContextModel() != null) {
            // TODO - resolve the context model, then set the result in the
            // context for
            // the
            // following resolution. The next step will necessarily be an object
            // resolution.
        }

        if (observable.getModel() != null) {
            cov = resolve(observable.getModel(), context.forModel(observable.getModel()));
        } else {
            cov = resolve(observable, context.forObservable(observable));
        }

        if (cov.isEmpty() && !context.isOptional()) {
            context.getMonitor().error("mandatory dependency on " + NS.getDisplayName(observable.getType())
                    + " is unresolved");
            context.coverage = Coverage.EMPTY;
        }

        /*
         * record dependency so that even optional outputs can be made mandatory.
         * Dependencies that declare an internal concept record the observer's observable
         * instead (deps can't do much with it, so the top concept is usually just a
         * placeholder).
         */
        if (!cov.isEmpty()) {
            if (observable.getModel() != null && observable.getObserver() != null) {
                context.requireOutput(observable.getModel().getObserver().getObservable());
            } else {
                context.requireOutput(observable);
            }
        }

        return context.finish();
    }

    /*
     * this will be passed a context.forObservable(), which automatically only merges in
     * models that cover enough more context to be relevant, and merges them into one
     * conditional model at finish().
     */
    private ICoverage resolve(IObservableSemantics observable, ResolutionScope context) throws KlabException {

        if (context.isUnsatisfiable) {
            return Coverage.EMPTY;
        }

        ICoverage cov = Coverage.EMPTY;
        if (observable.getModel() != null) {
            cov = resolve(observable.getModel(), context.forModel(observable.getModel()));
        } else {

            IModel previous = context.getModelFor(observable, !context.isExplanatoryModel);
            if (previous != null) {
                /*
                 * TODO we must not re-accept this, but tell the workflow to use the
                 * results of its previous run.
                 */
                cov = resolve(previous, context.forModel(previous));
                if (cov != null) {
                    cov = context.finish();
                }
            } else {

                /*
                 * if we're resolving a subject, coverage is full if we don't find any
                 * model. Any other situation, including processes and events, must have a
                 * model.
                 */
                cov = (NS.isThing(observable) && context.isExplanatoryModel)
                        ? Coverage.FULL(context.getScale())
                        : Coverage.EMPTY;

                /*
                 * lookup model for this observable. ModelKbox will do all the ranking and
                 * sorting as long as its own iterator is used.
                 */
                boolean found = false;

                /*
                 * we go through search only if we have not already seen the observable in
                 * a context where we have recorded that we can proceed with no model.
                 */
                if (!(context.isExplanatoryModel && context.hasNoModel(observable))) {

                    /*
                     * lookup state for same model in parent contexts. If found, create a
                     * state view, which may (some day) be intelligent enough to make
                     * further observations to adapt to different scales, and just resolve
                     * a state model using it.
                     */
                    if (context.contextSubject != null
                            && (NS.isQuality(observable) || NS.isTrait(observable))) {

                        // ensure we have one. Will not create NetCDFs unless
                        // there is
                        // raster space.
                        ((Subject) context.subject).requireBackingDataset();
                        IState prevobs = States
                                .findView(context.contextSubject, observable, (IActiveSubject) context.subject);

                        if (prevobs != null) {
                            StateModel sm = new StateModel(observable, prevobs, context
                                    .getResolutionNamespace());
                            cov = resolve(sm, context.forModel(sm));
                            if (cov.isComplete()) {
                                return finish(observable, context);
                            }
                        }
                    }

                    /*
                     * found or not, we want to also check the database and network for
                     * subjects if the observable is a subject. This will produce a custom
                     * instantiator that will be given back to the model to decide what to
                     * do with.
                     */
                    if (NS.isThing(observable)
                            && (!context.isExplanatoryModel
                                    || ((ObservableSemantics) observable).isInstantiator())) {
                        List observed = this.lookupSubjects(observable.getType(), context);
                        if (observed.size() > 0) {
                            IContextualizer instantiator = new DirectSubjectInstantiator(observed, context.monitor);

                            if (context.notifyContextualizer(instantiator)) {
                                context.monitor.info(observed.size()
                                        + " observations found on the network: suspending search for instantiators", Messages.INFOCLASS_DOWNLOAD);
                                return proceed(observable, context);
                            }
                            context.monitor
                                    .info("observations found on the network. Continuing resolution.", Messages.INFOCLASS_DOWNLOAD);

                            // boolean stopResolving = true;
                            // if (context.model != null) {
                            // stopResolving = ((KIMModel)
                            // context.model).acceptContextualizer(instantiator);
                            // } else {
                            // IModel subjectModel = new KIMModel(observable,
                            // instantiator, context);
                            // cov = resolve(subjectModel,
                            // context.forModel(subjectModel));
                            // context.modelGraph.add(context.node);
                            // }
                            //
                            // if (stopResolving) {
                            // context.monitor.info(observed.size()
                            // + " observations found on the network: suspending search
                            // for instantiators", Messages.INFOCLASS_DOWNLOAD);
                            // return finish(observable, context);
                            // } else {
                            // context.monitor
                            // .info("observations found on the network. Continuing
                            // resolution.", Messages.INFOCLASS_DOWNLOAD);
                            // }
                        }
                    }

                    /*
                     * with abstract countable observables, we explore the whole model
                     * space and collect any different subtype instantiator until all
                     * represented types have complete coverage or there are no more
                     * models.
                     */
                    boolean isAbstractCountable = context.isGeneric()
                            || (observable.getType().isAbstract() && NS.isCountable(observable) && context.isForInstantiation());
                    Map, ICoverage>> modelCache = new HashMap<>();

                    for (IModel m : ModelKbox.get().query(observable, context)) {

                        if (m == null) {
                            continue;
                        }

                        found = true;

                        if (context.isResolving(m)) {
                            continue;
                        }

                        if (!m.isAvailable()) {
                            context.monitor.warn("model " + m.getName() + " is unavailable");
                            continue;
                        }

                        /*
                         * TODO if this is an abstract countable, we need to keep
                         * accumulating models for different subclasses even if coverage
                         * is complete. Also we should accumulate models with the
                         * "archetype" flag, which don't provide complete observations.
                         */
                        ICoverage modCov = resolve(m, context.forModel(m));
                        cov = cov.or(modCov);

                        if (isAbstractCountable) {

                            /*
                             * coverage is always relevant; compute coverage for each
                             * individual sub-concept
                             */
                            if (modelCache.containsKey(m.getObservable().getType())) {
                                Pair, ICoverage> mdata = modelCache
                                        .get(m.getObservable().getType());
                                if (!mdata.getSecond().isComplete()) {
                                    mdata.getFirst().add(m);
                                    mdata.setSecond(mdata.getSecond().or(modCov));
                                }
                            } else {
                                modelCache.put(m.getObservable()
                                        .getType(), new Pair<>(Collections.singletonList(m), modCov));
                            }
                        }

                        if (cov.isComplete() && !isAbstractCountable) {
                            return finish(observable, context);
                        }
                    }

                    if (!modelCache.isEmpty()) {

                        /*
                         * report on types and coverages
                         */
                        context.monitor.info("query for abstract " + NS.getDisplayName(observable.getType())
                                + " produced:", Messages.INFOCLASS_NETWORK);
                        for (IConcept c : modelCache.keySet()) {
                            Pair, ICoverage> mdata = modelCache.get(c);
                            context.monitor.info("   " + NS.getDisplayName(c) + " (" + mdata.getFirst().size()
                                    + " models, "
                                    + NumberFormat.getCurrencyInstance()
                                            .format(mdata.getSecond().getCoverage())
                                    + "%)", Messages.INFOCLASS_NETWORK);
                        }

                        /*
                         * TODO create merging instantiator and finish context
                         */
                        context.getMonitor().warn("MODELS NOT USED - MERGING BEING IMPLEMENTED");
                    }

                    if (!cov.isEmpty() && !cov.isComplete()) {
                        if (cov.isRelevant()) {
                            context.monitor
                                    .warn("incomplete context coverage: total coverage for "
                                            + observable.getType()
                                            + " is "
                                            + NumberFormat.getCurrencyInstance().format(cov.getCoverage()));
                            return finish(observable, context);
                        } else {
                            return fail(observable, context);
                        }
                    }

                    /*
                     * if we have not found a model but we're continuing, record the
                     * incident so we don't have to query again for the next. We record
                     * this in the parent or it won't be transferred, as finish() is only
                     * called when we have a complete context.
                     */
                    if (!found && !cov.isEmpty()) {
                        context.parent.proceedWithNoModel(observable);
                        return proceed(observable, context);
                    }
                } else if (context.hasNoModel(observable)) {
                    return proceed(observable, context);
                }

            }
        }

        return finish(observable, context);
    }

    private ICoverage proceed(IObservableSemantics observable, ResolutionScope scope) throws KlabException {
        ICoverage ret = Coverage.FULL(scope.scale);
        ((Subject) scope.subject).addPreviousObservation(observable, ret);
        scope.coverage = ret;
        scope.finish();
        return ret;
    }

    private ICoverage finish(IObservableSemantics observable, ResolutionScope scope) throws KlabException {
        ICoverage ret = scope.finish();
        ((Subject) scope.subject).addPreviousObservation(observable, ret);
        return ret;
    }

    private ICoverage fail(IObservableSemantics observable, ResolutionScope scope) {
        ICoverage ret = Coverage.EMPTY;
        ((Subject) scope.subject).addPreviousObservation(observable, ret);
        return ret;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy