/**
* Copyright (c) 2011-2014, OpenIoT
*
* This file is part of OpenIoT.
*
* OpenIoT is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* OpenIoT 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
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with OpenIoT. If not, see <http://www.gnu.org/licenses/>.
*
* Contact: OpenIoT mailto: info@openiot.eu
*/
package org.openiot.ui.request.commons.models;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.openiot.ui.request.commons.factory.GraphFactory;
import org.openiot.ui.request.commons.interfaces.GraphModel;
import org.openiot.ui.request.commons.logging.LoggerService;
import org.openiot.ui.request.commons.nodes.base.DefaultGraphNodeConnection;
import org.openiot.ui.request.commons.nodes.enums.EndpointType;
import org.openiot.ui.request.commons.nodes.interfaces.GraphNode;
import org.openiot.ui.request.commons.nodes.interfaces.GraphNodeConnection;
import org.openiot.ui.request.commons.nodes.interfaces.GraphNodeEndpoint;
import org.openiot.ui.request.commons.nodes.interfaces.GraphNodeEventListener;
/**
*
* @author Achilleas Anagnostopoulos (aanag) email: aanag@sensap.eu
*/
public class DefaultGraphModel implements GraphModel, Serializable {
private static final long serialVersionUID = 1L;
private String UID = "graph_" + System.nanoTime();
private GraphNode selectedNode;
private List<GraphNode> nodes;
private List<GraphNodeConnection> connections;
private Map<String, GraphNodePosition> positions;
public DefaultGraphModel() {
this.nodes = new ArrayList<GraphNode>();
this.connections = new ArrayList<GraphNodeConnection>();
this.positions = new HashMap<String, GraphNodePosition>();
}
public String getUID() {
return UID;
}
public void setUID(String UID) {
this.UID = UID;
}
public List<GraphNode> getNodes() {
return nodes;
}
public List<GraphNodeConnection> getConnections() {
return connections;
}
public void setNodes(List<GraphNode> nodes) {
this.nodes = nodes;
}
public void setConnections(List<GraphNodeConnection> connections) {
this.connections = connections;
}
public GraphNode getSelectedNode() {
return selectedNode;
}
public void setSelectedNode(GraphNode selectedNode) {
this.selectedNode = selectedNode;
}
public void clear() {
selectedNode = null;
nodes.clear();
connections.clear();
positions.clear();
}
public void insert(GraphNode node, double x, double y) {
node.setGraphModel(this);
nodes.add(node);
GraphNodePosition position = new GraphNodePosition(x, y);
positions.put(node.getUID(), position);
}
public void remove(GraphNode node) {
// Remove all connections that point to deleted node
Iterator<GraphNodeConnection> connectionIt = connections.iterator();
while (connectionIt.hasNext()) {
GraphNodeConnection connection = connectionIt.next();
if (node.getUID().equals(connection.getSourceNode().getUID())
|| node.getUID().equals(connection.getDestinationNode().getUID())) {
connectionIt.remove();
// Notify nodes about the disconnect
if (connection.getSourceNode() instanceof GraphNodeEventListener) {
((GraphNodeEventListener) connection.getSourceNode()).onNodeDisconnected(this, connection.getDestinationNode(), connection.getDestinationEndpoint(), connection.getSourceEndpoint());
}
if (connection.getDestinationNode() instanceof GraphNodeEventListener) {
((GraphNodeEventListener) connection.getDestinationNode()).onNodeDisconnected(this, connection.getSourceNode(), connection.getSourceEndpoint(), connection.getDestinationEndpoint());
}
LoggerService.log(Level.FINE, "[DefaultGraphModel] Deleted connection connection [" + connection.getSourceNode().getUID() + ":" + connection.getSourceEndpoint().getUID() + "] -> [" + connection.getDestinationNode().getUID() + ":" + connection.getDestinationEndpoint().getUID() + "] [connection id " + connection.getUID() + "]");
}
}
nodes.remove(node);
positions.remove(node.getUID());
// Inform node about deletion
if (node instanceof GraphNodeEventListener) {
((GraphNodeEventListener) node).onNodeDeleted(this);
}
LoggerService.log(Level.FINE, "[DefaultGraphModel] Deleted node [" + node.getUID() + "]");
}
public void connect(String connectionId, GraphNode sourceNode, GraphNodeEndpoint sourceEndpoint, GraphNode destinationNode, GraphNodeEndpoint destinationEndpoint) {
GraphNodeConnection connection = new DefaultGraphNodeConnection(sourceNode, sourceEndpoint, destinationNode, destinationEndpoint);
if (connectionId != null) {
connection.setUID(connectionId);
}
connections.add(connection);
// Notify nodes about the new connection
if (connection.getSourceNode() instanceof GraphNodeEventListener) {
((GraphNodeEventListener) connection.getSourceNode()).onNodeConnected(this, connection.getDestinationNode(), connection.getDestinationEndpoint(), connection.getSourceEndpoint());
}
if (connection.getDestinationNode() instanceof GraphNodeEventListener) {
((GraphNodeEventListener) connection.getDestinationNode()).onNodeConnected(this, connection.getSourceNode(), connection.getSourceEndpoint(), connection.getDestinationEndpoint());
}
LoggerService.log(Level.FINE, "[DefaultGraphModel] Established connection [" + connection.getSourceNode().getUID() + ":" + connection.getSourceEndpoint().getUID() + "] -> [" + connection.getDestinationNode().getUID() + ":" + connection.getDestinationEndpoint().getUID() + "] [connection id " + connection.getUID() + "]");
}
public void disconnect(GraphNodeConnection connection) {
connections.remove(connection);
// Notify nodes about the disconnect
if (connection.getSourceNode() instanceof GraphNodeEventListener) {
((GraphNodeEventListener) connection.getSourceNode()).onNodeDisconnected(this, connection.getDestinationNode(), connection.getDestinationEndpoint(), connection.getSourceEndpoint());
}
if (connection.getDestinationNode() instanceof GraphNodeEventListener) {
((GraphNodeEventListener) connection.getDestinationNode()).onNodeDisconnected(this, connection.getSourceNode(), connection.getSourceEndpoint(), connection.getDestinationEndpoint());
}
LoggerService.log(Level.FINE, "[DefaultGraphModel] Deleted connection connection [" + connection.getSourceNode().getUID() + ":" + connection.getSourceEndpoint().getUID() + "] -> [" + connection.getDestinationNode().getUID() + ":" + connection.getDestinationEndpoint().getUID() + "] [connection id " + connection.getUID() + "]");
}
public List<GraphNodeConnection> findGraphEndpointConnections(GraphNodeEndpoint endpoint) {
List<GraphNodeConnection> matchingConnections = new ArrayList<GraphNodeConnection>();
if (endpoint.getType().equals(EndpointType.Input)) {
for (GraphNodeConnection connection : this.connections) {
if (connection.getDestinationEndpoint().getUID().equals(endpoint.getUID())) {
matchingConnections.add(connection);
}
}
} else {
for (GraphNodeConnection connection : this.connections) {
if (connection.getSourceEndpoint().getUID().equals(endpoint.getUID())) {
matchingConnections.add(connection);
}
}
}
return matchingConnections;
}
public GraphNodeConnection lookupGraphNodeConnection(String connectionUID) {
for (GraphNodeConnection connection : this.connections) {
if (connection.getUID().equals(connectionUID)) {
return connection;
}
}
return null;
}
public GraphNode lookupGraphNode(String nodeUID) {
for (GraphNode node : nodes) {
if (node.getUID().equals(nodeUID)) {
return node;
}
}
return null;
}
public GraphNodePosition lookupGraphNodePosition(String nodeUID) {
return positions.get(nodeUID);
}
public GraphNodeEndpoint lookupGraphEndpoint(GraphNode node, String endpointUID) {
for (GraphNodeEndpoint endpoint : node.getEndpointDefinitions()) {
if (endpoint.getUID().equals(endpointUID)) {
return endpoint;
}
}
return null;
}
public void updatePosition(GraphNode graphNode, double newX, double newY) {
GraphNodePosition position = positions.get(graphNode.getUID());
if (position == null) {
position = new GraphNodePosition(newX, newY);
positions.put(graphNode.getUID(), position);
}
position.setX(newX);
position.setY(newY);
}
public JSONObject toJSON() {
JSONObject spec = new JSONObject();
try{
spec.put("class", this.getClass().getCanonicalName());
spec.put("uid", getUID());
// Encode each node
JSONArray nodes = new JSONArray();
for( GraphNode node : getNodes() ){
nodes.put( node.toJSON() );
}
spec.put("nodes", nodes);
// Encode node positions
JSONObject positions = new JSONObject();
for( Map.Entry<String, GraphNodePosition> entry : this.positions.entrySet() ){
positions.put(entry.getKey(), entry.getValue().toJSON());
}
spec.put("positions", positions);
// Encode node connections
JSONArray connections = new JSONArray();
for( GraphNodeConnection connection : this.connections ){
connections.put( connection.toJSON() );
}
spec.put("connections", connections);
}catch(JSONException ex){
LoggerService.log(ex);
}
return spec;
}
public void importJSON(JSONObject spec) throws JSONException {
setUID(spec.getString("uid"));
// Parse nodes
JSONArray nodes = spec.getJSONArray("nodes");
this.nodes.clear();
for( int index = 0; index< nodes.length(); index++){
GraphNode node = GraphFactory.createGraphNode(nodes.getJSONObject(index));
node.setGraphModel(this);
this.nodes.add(node);
}
// Parse node positions
JSONObject positions = spec.getJSONObject("positions");
Iterator<?> keyIt = positions.keys();
this.positions.clear();
while( keyIt.hasNext() ){
String nodeId = (String)keyIt.next();
GraphNodePosition pos = new GraphNodePosition(0, 0);
pos.importJSON(positions.getJSONObject(nodeId));
this.positions.put( nodeId, pos );
}
// Parse node connections
JSONArray connections = spec.getJSONArray("connections");
this.connections.clear();
for( int index = 0; index< connections.length(); index++){
JSONObject conSpec = connections.getJSONObject(index);
GraphNodeConnection connection = GraphFactory.createGraphNodeConnection(conSpec);
// Lookup connection nodes
GraphNode srcNode = this.lookupGraphNode(conSpec.getString("srcNode"));
GraphNodeEndpoint srcEndpoint = this.lookupGraphEndpoint(srcNode, conSpec.getString("srcEndpoint"));
GraphNode dstNode = this.lookupGraphNode(conSpec.getString("dstNode"));
GraphNodeEndpoint dstEndpoint = this.lookupGraphEndpoint(dstNode, conSpec.getString("dstEndpoint"));
// Setup connection
connection.setSourceNode(srcNode);
connection.setSourceEndpoint(srcEndpoint);
connection.setDestinationNode(dstNode);
connection.setDestinationEndpoint(dstEndpoint);
this.connections.add(connection);
}
}
}