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

org.integratedmodelling.engine.modelling.resolver.ResolutionGraph 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.util.ArrayList;
import java.util.HashMap;

import org.integratedmodelling.api.knowledge.IConcept;
import org.integratedmodelling.api.knowledge.IExpression;
import org.integratedmodelling.api.knowledge.IProperty;
import org.integratedmodelling.api.lang.IMetadataHolder;
import org.integratedmodelling.api.metadata.IMetadata;
import org.integratedmodelling.api.modelling.ICoverage;
import org.integratedmodelling.api.modelling.IDataSource;
import org.integratedmodelling.api.modelling.IModel;
import org.integratedmodelling.api.modelling.IModelBean;
import org.integratedmodelling.api.modelling.IObjectSource;
import org.integratedmodelling.api.modelling.IObservableSemantics;
import org.integratedmodelling.api.modelling.IObserver;
import org.integratedmodelling.api.modelling.IObservingObject;
import org.integratedmodelling.api.modelling.IState;
import org.integratedmodelling.api.modelling.contextualization.IStateContextualizer;
import org.integratedmodelling.api.modelling.resolution.IResolution;
import org.integratedmodelling.api.modelling.resolution.IResolutionScope;
import org.integratedmodelling.api.modelling.runtime.IActiveObserver;
import org.integratedmodelling.api.monitoring.IMonitor;
import org.integratedmodelling.api.monitoring.Messages;
import org.integratedmodelling.base.HashableObject;
import org.integratedmodelling.collections.Pair;
import org.integratedmodelling.common.beans.generic.Graph;
import org.integratedmodelling.common.data.Edge;
import org.integratedmodelling.common.interfaces.NetworkSerializable;
import org.integratedmodelling.common.kim.KIMObserver;
import org.integratedmodelling.common.metadata.Metadata;
import org.integratedmodelling.common.utils.NameGenerator;
import org.integratedmodelling.exceptions.KlabException;
import org.integratedmodelling.utils.graph.GraphViz;
import org.integratedmodelling.utils.graph.GraphViz.NodePropertiesProvider;
import org.jgrapht.graph.DefaultDirectedGraph;

/**
 * The model strategy produced by the ModelResolver. Individual models are
 * linked by edges that contain the description of what they are doing in
 * relation to each other.
 * 
 * TODO must incorporate a trace of the decisions made by the resolver in the
 * metadata for each model or each link (easier).
 * 
 * @author Ferd
 *
 */
public class ResolutionGraph
		extends DefaultDirectedGraph
		implements IResolution, NetworkSerializable {

	HashMap _nodes = new HashMap<>();
	IMonitor monitor;

	public static class ProvenanceNode extends HashableObject implements IMetadataHolder {

		/*
		 * only one of these is non-null at any time.
		 */
		public IModel model;
		public IActiveObserver observer;
		public IDataSource datasource;
		public IState state;
		public IMetadata metadata;
		public IObjectSource objectsource;
		ICoverage coverage;

		public IStateContextualizer getContextualizer(IResolutionScope scope) throws KlabException {
			if (observer != null) {
				return observer.getContextualizer(scope, ((ResolutionScope) scope).getMonitor());
			} else if (datasource != null) {
				return datasource.getContextualizer(scope.getScale(), /* FIXME */ null,
						((ResolutionScope) scope).getMonitor());
			}
			/*
			 * TODO state
			 */
			return null;
		}

		/*
		 * methods below are for the compiler.
		 */
		public IObserver getObserver() {
			if (observer != null) {
				return observer;
			}
			if (model != null) {
				return model.getObserver();
			}
			if (state != null) {
				return state.getObserver();
			}

			return null;
		}

		public void setCoverage(ICoverage coverage) {
			this.coverage = coverage;
		}

		public Object getObject() {

			if (model != null) {
				return model;
			}
			if (observer != null) {
				return observer;
			}
			if (state != null) {
				return state;
			}
			if (datasource != null) {
				return datasource;
			}

			return null;
		}

		public static String describeNode(ProvenanceNode n) {

			if (n.model != null) {
				return "m " + n.model.getName();
			} else if (n.observer != null) {
				return "o " + n.observer;
			} else if (n.state != null) {
				return "s " + n.state;
			} else if (n.datasource != null) {
				return "d " + n.datasource;
			}

			return "";
		}

		@Override
		public String toString() {
			return "[" + describeNode(this) + "]";
		}

		@Override
		public IMetadata getMetadata() {
			return metadata == null ? new Metadata() : metadata;
		}
	}

	public static class DependencyEdge extends Edge {

		static public enum Type {
			/**
			 * links a model to an {@link IObservingObject} that depends on it.
			 */
			DEPENDENCY,
			/**
			 * links an observer to another that mediates it.
			 */
			MEDIATE_TO,
			/**
			 * links a datasource to the observer that interprets it
			 */
			INTERPRET_AS,
			/**
			 * links an observer to the model that contains it.
			 */
			DEFINE_STATE,
			/**
			 * links a resolved model to the observer that needs its observable.
			 */
			RESOLVES,
			/**
			 * a dependency that coexists with others for the same observable
			 * and is chosen conditionally.
			 */
			CONDITIONAL_DEPENDENCY,
			/*
			 * used only by the dataflow compiler to avoid having to write null
			 * checks in one of "those" algorithms.
			 */
			NONE
		};

		public DependencyEdge(Type type, String formalName, IObservableSemantics observable) {
			this.formalName = formalName;
			this.type = type;
			this.observable = observable;
		}

		private static final long serialVersionUID = 2366743581134478147L;

		public String formalName = null;
		public IObservableSemantics observable = null;
		public IProperty property;
		public Type type;
		public double coverage;
		public int conditionIndex = -1;
		public IExpression condition;

		@Override
		public boolean equals(Object edge) {
			return edge instanceof ResolutionGraph.DependencyEdge
					&& this.getSource().equals(((ResolutionGraph.DependencyEdge) edge).getSource())
					&& this.getTarget().equals(((ResolutionGraph.DependencyEdge) edge).getTarget())
					&& type == ((ResolutionGraph.DependencyEdge) edge).type;
		}

		public ProvenanceNode getTargetNode() {
			return (ProvenanceNode) super.getTarget();
		}

		public ProvenanceNode getSourceNode() {
			return (ProvenanceNode) super.getSource();
		}

		public String describeType() {

			if (conditionIndex >= 0) {
				return "#" + conditionIndex + (condition == null ? "" : (" " + condition + "?"));
			}

			switch (type) {
			case DEPENDENCY:
				return (formalName == null || formalName.isEmpty()) ? "computes" : formalName;
			case MEDIATE_TO:
				return "mediate";
			case INTERPRET_AS:
				return "interpreted as";
			case DEFINE_STATE:
				return "defines";
			case RESOLVES:
				return "resolves";
			}

			return "";
		}

		@Override
		public String toString() {
			return getSourceNode() + " -- " + describeType() + " -> " + getTargetNode();
		}
	}

	private static final long serialVersionUID = -5836939340704909163L;

	public ResolutionGraph(IMonitor monitor) {
		super(ResolutionGraph.DependencyEdge.class);
		this.monitor = monitor;
	}

	public ProvenanceNode getObjectSourceNode(IObjectSource source) {
		ProvenanceNode ret;
		if (_nodes.containsKey(source)) {
			ret = _nodes.get(source);
		} else {
			ret = new ProvenanceNode();
			ret.objectsource = source;
			_nodes.put(source, ret);
		}
		return ret;

	}

	/**
	 * Wrap the passed object into a node. If we have seen that object before,
	 * just return the correspondent node. Return the node but do not add it to
	 * the graph.
	 * 
	 * @param o
	 */
	public ProvenanceNode getNode(Object o) {

		ProvenanceNode ret;

		if (_nodes.containsKey(o)) {
			ret = _nodes.get(o);
		} else {
			ret = new ProvenanceNode();

			if (o instanceof IModel) {
				ret.model = (IModel) o;
			} else if (o instanceof IObserver) {
				ret.observer = (IActiveObserver) o;
			} else if (o instanceof IDataSource) {
				ret.datasource = (IDataSource) o;
			} else if (o instanceof IObjectSource) {
				ret.objectsource = (IObjectSource) o;
			} else if (o instanceof IState) {
				ret.state = (IState) o;
			}

			_nodes.put(o, ret);
		}
		return ret;
	}

	public void add(ProvenanceNode n) {
		addVertex(n);
	}

	// DEBUG - remove
	public boolean hasDatasource() {
		for (ProvenanceNode n : vertexSet()) {
			if (n.datasource != null) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Merge in everything in the passed graph. If there is a node in each graph
	 * for a the same object, use the same node in the merged result.
	 * 
	 * @param g
	 */
	public void merge(ResolutionGraph g) {

		for (ProvenanceNode n : g.vertexSet()) {
			if (!hasNode(n.getObject())) {
				addVertex(n);
				_nodes.put(n.getObject(), n);
			}
		}
		for (DependencyEdge d : g.edgeSet()) {
			ProvenanceNode source = getNode(d.getSourceNode().getObject());
			ProvenanceNode target = getNode(d.getTargetNode().getObject());
			addEdge(source, target, d);
		}
	}

	public ProvenanceNode get(Object o) {
		return _nodes.get(o);
	}

	public String dump() {

		GraphViz ziz = new GraphViz();
		ziz.loadGraph(this, new NodePropertiesProvider() {

			@Override
			public int getNodeWidth(ProvenanceNode o) {
				return 40;
			}

			@Override
			public String getNodeId(ProvenanceNode o) {

				String id = "?";

				if (o.model != null) {

					id = o.model.getId();
					if (id == null || NameGenerator.isGenerated(id)) {
						id = o.toString();
					}
				} else if (o.state != null) {
					id = o.state.toString();
				} else if (o.observer != null) {
					id = o.observer.toString();
				} else if (o.datasource != null) {
					id = o.datasource.toString();
				}

				return id + " (" + o.hashCode() + ")";
			}

			@Override
			public int getNodeHeight(ProvenanceNode o) {
				return 20;
			}

			@Override
			public String getNodeShape(ProvenanceNode o) {

				if (o.model != null) {
					return (o.model.getObserver() == null ? BOX3D : BOX);
				}
				return BOX;
			}

			@Override
			public String getEdgeColor(DependencyEdge de) {
				switch (de.type) {
				case DEPENDENCY:
					return "black";
				case MEDIATE_TO:
					return "blue";
				case INTERPRET_AS:
					return "red";
				case DEFINE_STATE:
					return "green";
				case CONDITIONAL_DEPENDENCY:
					return "brown";
				}
				return "black";
			}

			private String edgeType(DependencyEdge e) {
				return e.describeType();
			}

			@Override
			public String getEdgeLabel(DependencyEdge de) {
				return edgeType(de);
			}

		}, false);

		return ziz.getDotSource();
	}

	/**
	 * Remove all disconnected nodes. Only happens when there were errors in
	 * model resolution, still we may want to look at what was actually done for
	 * debugging.
	 * 
	 * Only for non-trivial structures: if there is only one node, leave it.
	 */
	public void cleanup() {

		if (this.vertexSet().size() <= 1) {
			return;
		}

		ArrayList disconnected = new ArrayList();
		for (ProvenanceNode n : this.vertexSet()) {
			if (this.edgesOf(n).size() == 0) {
				disconnected.add(n);
			}
		}

		for (ProvenanceNode n : disconnected) {
			this.removeVertex(n);
		}
	}

	public IMetadata collectMetadata(Object node) {

		Metadata ret = new Metadata();
		return collectMetadataInternal(node, ret);
	}

	private IMetadata collectMetadataInternal(Object o, Metadata dest) {

		if (o instanceof IMetadataHolder) {
			IMetadata md = ((IMetadataHolder) o).getMetadata();
			for (String k : md.getKeys()) {
				if (dest.get(k) == null) {
					dest.put(k, md.get(k));
				}
			}
		}

		if (o instanceof IModel) {
			collectMetadataInternal(((IModel) o).getObservables().get(0).getType(), dest);
		} else if (o instanceof IObserver) {
			collectMetadataInternal(((KIMObserver) o).getTopLevelModel(), dest);
		} else if (o instanceof IState) {
			collectMetadataInternal(((IState) o).getObserver(), dest);
            collectMetadataInternal(((IState) o).getObservable().getType(), dest);
			collectMetadataInternal(((IState) o).getObservable().getSemantics().getType(), dest);
		} else if (o instanceof IConcept) {
			for (IConcept c : ((IConcept) o).getParents()) {
				collectMetadataInternal(c, dest);
			}
		}

		return dest;
	}

	@Override
	public boolean isEmpty() {
		return vertexSet().size() == 0;
	}

	public boolean hasNode(Object o) {
		return _nodes.get(o) != null && vertexSet().contains(_nodes.get(o));
	}

	/**
	 * Create a link between two node, if necessary switching the nodes so that
	 * we ensure that no object is represented by more than one node. With the
	 * current logics it should be largely unnecessary, but for now it can stay.
	 * 
	 * @param source
	 * @param target
	 * @param dlink
	 */
	public void link(ProvenanceNode source, ProvenanceNode target, DependencyEdge dlink) {
		ProvenanceNode s = getNode(source.getObject());
		ProvenanceNode t = getNode(target.getObject());
		addVertex(s);
		addVertex(t);
		addEdge(s, t, dlink);

	}

	@SuppressWarnings("unchecked")
	@Override
	public  T serialize(Class desiredClass) {

		Graph ret = Graph.adapt(this, new Graph.Identifier() {

			@Override
			public String getLabel(Object object) {

				String id = "";

				if (object instanceof ProvenanceNode) {

					ProvenanceNode o = (ProvenanceNode) object;

					id = "?";
					if (o.model != null) {
						id = o.model.getName();
						if (id == null) {
							id = o.toString();
						}
					} else if (o.state != null) {
						id = o.state.toString();
					} else if (o.observer != null) {
						id = o.observer.toString();
					} else if (o.datasource != null) {
						id = o.datasource.toString();
					}

				} else if (object instanceof DependencyEdge) {
					id = ((DependencyEdge) object).describeType();
				}
				return id;
			}

			@Override
			public IMetadata getMetadata(Object object) {
				return null;
			}

			@Override
			public String getType(Object object) {
				if (object instanceof ProvenanceNode) {
					ProvenanceNode o = (ProvenanceNode) object;
					if (o.model != null) {
						return o.model.getObserver() == null ? "amodel" : "dmodel";
					} else if (o.observer != null) {
						return "observer";
					} else if (o.datasource != null) {
						return "datasource";
					} else if (o.state != null) {
						return "state";
					}
				}
				return "node";
			}

			@Override
			public Pair getTopNode() {
				return null;
			}
		});

		ret.setType(Messages.GRAPH_RESOLUTION);

		return (T) ret;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy