org.apache.airavata.workflow.model.graph.impl.GraphImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.airavata.workflow.model.graph.impl.GraphImpl.java

Source

/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 */

package org.apache.airavata.workflow.model.graph.impl;

import java.awt.Point;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import org.apache.airavata.common.utils.XMLUtil;
import org.apache.airavata.workflow.model.exceptions.WorkflowRuntimeException;
import org.apache.airavata.workflow.model.graph.ControlPort;
import org.apache.airavata.workflow.model.graph.DataEdge;
import org.apache.airavata.workflow.model.graph.DataPort;
import org.apache.airavata.workflow.model.graph.Edge;
import org.apache.airavata.workflow.model.graph.Graph;
import org.apache.airavata.workflow.model.graph.GraphException;
import org.apache.airavata.workflow.model.graph.GraphFactory;
import org.apache.airavata.workflow.model.graph.GraphSchema;
import org.apache.airavata.workflow.model.graph.Node;
import org.apache.airavata.workflow.model.graph.Port;
import org.apache.airavata.workflow.model.graph.Port.Kind;
import org.apache.airavata.workflow.model.graph.system.InputNode;
import org.apache.airavata.workflow.model.graph.system.OutputNode;
import org.apache.airavata.workflow.model.graph.system.StreamSourceNode;
import org.apache.airavata.workflow.model.graph.system.SystemDataPort;
import org.apache.airavata.workflow.model.graph.util.GraphUtil;
import org.apache.airavata.workflow.model.graph.ws.WSPort;
import org.apache.airavata.workflow.model.utils.ApplicationVersion;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmlpull.infoset.XmlElement;

public abstract class GraphImpl implements Graph {

    private static final Logger logger = LoggerFactory.getLogger(GraphImpl.class);

    /**
     * Unique ID of this workflow
     */
    private String id;

    /**
     * Name of the workflow. It has to have one.
     */
    private String name = "Workflow";

    /**
     * Default to empty string to avoid null check.
     */
    private String description = "";

    private List<NodeImpl> nodes = new LinkedList<NodeImpl>();

    private List<PortImpl> ports = new LinkedList<PortImpl>();

    private List<EdgeImpl> edges = new LinkedList<EdgeImpl>();

    private GraphFactory factory;

    /**
     * @param factory
     */
    public GraphImpl(GraphFactory factory) {
        this.factory = factory;
    }

    /**
     * @see org.apache.airavata.workflow.model.graph.Graph#getID()
    */
    public String getID() {
        if (this.id == null) {
            this.id = this.name;
            // If its still null
            if (null == this.id) {
                throw new WorkflowRuntimeException("The workflow ID is null");
            }
        }
        return this.id;
    }

    /**
     * This will only be done for the ODE
     * 
     * @param id
     */
    public void setID(String id) {
        this.id = id;
    }

    /**
     * @see org.apache.airavata.workflow.model.graph.Graph#setName(java.lang.String)
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * @see org.apache.airavata.workflow.model.graph.Graph#getName()
     */
    public String getName() {
        return this.name;
    }

    /**
     * @see org.apache.airavata.workflow.model.graph.Graph#getDescription()
     */
    public String getDescription() {
        return this.description;
    }

    /**
     * @see org.apache.airavata.workflow.model.graph.Graph#setDescription(java.lang.String)
     */
    public void setDescription(String description) {
        this.description = description;
    }

    /**
     * @see org.apache.airavata.workflow.model.graph.Graph#getNodes()
     */
    public List<NodeImpl> getNodes() {
        return this.nodes;
    }

    /**
     * @see org.apache.airavata.workflow.model.graph.Graph#getPorts()
     */
    public List<PortImpl> getPorts() {
        return this.ports;
    }

    /**
     * @see org.apache.airavata.workflow.model.graph.Graph#getEdges()
     */
    public List<EdgeImpl> getEdges() {
        return this.edges;
    }

    /**
     * @see org.apache.airavata.workflow.model.graph.Graph#removeNode(org.apache.airavata.workflow.model.graph.Node)
     */
    public void removeNode(Node node) throws GraphException {
        if (node == null) {
            throw new IllegalArgumentException("null");
        }
        if (!this.nodes.contains(node)) {
            throw new GraphException("The graph doesn't contain the node that is being removed.");
        }

        NodeImpl nodeImpl = (NodeImpl) node;

        // Have to be very careful to remove the node.
        // The extended for loop cannot be used to remove the elements.

        // Remove edges connected to input ports.
        for (Iterator<DataPort> portItr = nodeImpl.getInputPorts().iterator(); portItr.hasNext();) {
            DataPort port = portItr.next();
            for (Iterator<DataEdge> edgeItr = port.getEdges().iterator(); edgeItr.hasNext();) {
                DataEdge edge = edgeItr.next();
                // Remove the edge from from-port.
                DataPort fromPort = edge.getFromPort();
                fromPort.removeEdge(edge);
                // remove the edge from this port. This is necessary so that
                // type update works properly.
                edgeItr.remove();

                // Remove the edge from the graph.
                this.edges.remove(edge);
                fromPort.getNode().edgeWasRemoved(edge);
            }
            // Remove the port from the node.
            portItr.remove();
            // Remove the port from the graph.
            this.ports.remove(port);
        }

        // Remove edges connected to output ports.
        for (Iterator<DataPort> portItr = nodeImpl.getOutputPorts().iterator(); portItr.hasNext();) {
            DataPort port = portItr.next();
            for (Iterator<DataEdge> edgeItr = port.getEdges().iterator(); edgeItr.hasNext();) {
                DataEdge edge = edgeItr.next();
                DataPort toPort = edge.getToPort();
                toPort.removeEdge(edge);
                edgeItr.remove();
                this.edges.remove(edge);
                toPort.getNode().edgeWasRemoved(edge);
            }
            portItr.remove();
            this.ports.remove(port);
        }

        for (Iterator<ControlPort> portItr = nodeImpl.getControlOutPorts().iterator(); portItr.hasNext();) {
            PortImpl port = portItr.next();
            for (Iterator<? extends EdgeImpl> edgeItr = port.getEdges().iterator(); edgeItr.hasNext();) {
                EdgeImpl edge = edgeItr.next();
                PortImpl toPort = edge.getToPort();
                toPort.removeEdge(edge);
                edgeItr.remove();
                this.edges.remove(edge);
                toPort.getNode().edgeWasRemoved(edge);
            }
            portItr.remove();
            this.ports.remove(port);
        }

        PortImpl controlInPort = nodeImpl.getControlInPort();
        if (controlInPort != null) {
            for (Iterator<? extends EdgeImpl> edgeItr = controlInPort.getEdges().iterator(); edgeItr.hasNext();) {
                EdgeImpl edge = edgeItr.next();
                PortImpl fromPort = edge.getFromPort();
                fromPort.removeEdge(edge);
                edgeItr.remove();
                this.edges.remove(edge);
                fromPort.getNode().edgeWasRemoved(edge);
            }
            this.ports.remove(controlInPort);
        }

        PortImpl eprPort = nodeImpl.getEPRPort();
        if (eprPort != null) {
            for (Iterator<? extends EdgeImpl> edgeItr = eprPort.getEdges().iterator(); edgeItr.hasNext();) {
                EdgeImpl edge = edgeItr.next();
                PortImpl toPort = edge.getToPort();
                toPort.removeEdge(edge);
                edgeItr.remove();
                this.edges.remove(edge);
                toPort.getNode().edgeWasRemoved(edge);
            }
            this.ports.remove(eprPort);
        }

        this.nodes.remove(node);
    }

    /**
     * @see org.apache.airavata.workflow.model.graph.Graph#getNode(java.lang.String)
     */
    public NodeImpl getNode(String nodeID) {
        for (NodeImpl node : this.nodes) {
            if (nodeID.equals(node.getID())) {
                return node;
            }
        }
        return null;
    }

    /**
     * @param port
     * @throws GraphException
     */
    public void removePort(Port port) throws GraphException {
        if (port == null) {
            throw new IllegalArgumentException("null");
        }
        if (!this.ports.contains(port)) {
            throw new GraphException("The graph doesn't contain the port that is being removed.");
        }

        // copy it so that we can remove edge without worrying about the
        // iteration issue.
        ArrayList<Edge> edgesToBeRemoved = new ArrayList<Edge>(port.getEdges());
        for (Edge edge : edgesToBeRemoved) {
            removeEdge(edge);
        }

        this.ports.remove(port);
    }

    /**
     * @see org.apache.airavata.workflow.model.graph.Graph#getPort(java.lang.String)
     */
    public PortImpl getPort(String portID) {
        for (PortImpl port : this.ports) {
            if (portID.equals(port.getID())) {
                return port;
            }
        }
        return null;
    }

    /**
     * @see org.apache.airavata.workflow.model.graph.Graph#addEdge(org.apache.airavata.workflow.model.graph.Port,
     *      org.apache.airavata.workflow.model.graph.Port)
     */
    public Edge addEdge(Port fromPort, Port toPort) throws GraphException {
        if (containsEdge(fromPort, toPort)) {
            // The edge already exists. Doesn't create a new one.
            return null;
        }

        if (!this.ports.contains(fromPort) || !this.ports.contains(toPort)) {
            // The specified port doesn't belong to the graph.
            throw new GraphException("The graph doesn't contain the specified port.");
        }

        PortImpl fromPortImpl = (PortImpl) fromPort;
        PortImpl toPortImpl = (PortImpl) toPort;
        NodeImpl fromNode = fromPortImpl.getNode();
        NodeImpl toNode = toPortImpl.getNode();

        EdgeImpl edge = this.factory.createEdge(fromPort, toPort);

        edge.setFromPort(fromPortImpl);
        edge.setToPort(toPortImpl);
        fromPortImpl.addEdge(edge);
        toPortImpl.addEdge(edge);
        addEdge(edge);

        try {
            fromNode.edgeWasAdded(edge);
            toNode.edgeWasAdded(edge);
            return edge;
        } catch (GraphException e) {
            removeEdge(edge);
            throw e;
        }

    }

    /**
     * @throws GraphException
     * @see org.apache.airavata.workflow.model.graph.Graph#removeEdge(org.apache.airavata.workflow.model.graph.Edge)
     */
    public void removeEdge(Edge edge) throws GraphException {
        if (!this.edges.contains(edge)) {
            throw new GraphException("The graph doesn't contain the specified edge.");
        }
        EdgeImpl edgeImpl = (EdgeImpl) edge;

        PortImpl fromPort = edgeImpl.getFromPort();
        PortImpl toPort = edgeImpl.getToPort();

        NodeImpl fromNode = fromPort.getNode();
        NodeImpl toNode = toPort.getNode();

        fromPort.removeEdge(edgeImpl);
        toPort.removeEdge(edgeImpl);

        this.edges.remove(edgeImpl);

        // This has to be after removing edges.
        fromNode.edgeWasRemoved(edge);
        toNode.edgeWasRemoved(edge);
    }

    /**
     * @throws GraphException
     * @see org.apache.airavata.workflow.model.graph.Graph#removeEdge(org.apache.airavata.workflow.model.graph.Port,
     *      org.apache.airavata.workflow.model.graph.Port)
     */
    public void removeEdge(Port fromPort, Port toPort) throws GraphException {
        Collection<? extends Edge> fromEdges = fromPort.getEdges();
        for (Edge fromEdge : fromEdges) {
            if (fromEdge.getToPort() == toPort) {
                // It's OK to remove this way because it will exit the loop
                // right away.
                removeEdge(fromEdge);
                return;
            }
        }
        throw new WorkflowRuntimeException("No edge exist between two ports.");
    }

    /**
     * @see org.apache.airavata.workflow.model.graph.Graph#containsEdge(org.apache.airavata.workflow.model.graph.Port,
     *      org.apache.airavata.workflow.model.graph.Port)
     */
    public boolean containsEdge(Port fromPort, Port toPort) {
        for (Edge fromEdge : fromPort.getEdges()) {
            Collection<? extends Edge> toEdges = toPort.getEdges();
            if (toEdges.contains(fromEdge)) {
                return true;
            }
        }
        return false;
    }

    /**
     * @throws GraphException
     * @see org.apache.airavata.workflow.model.graph.Graph#importGraph(org.apache.airavata.workflow.model.graph.Graph)
     */
    public void importGraph(Graph graph) throws GraphException {

        // Does not support other implementations.
        if (!(graph instanceof GraphImpl)) {
            throw new GraphException("Cannot import this graph implementation");
        }

        GraphImpl graphImpl = (GraphImpl) graph;

        for (NodeImpl node : graphImpl.getNodes()) {
            addNode(node);
            // Recreates the ID so that it doesn't conflict with the existing
            // one.
            node.createID();
            // Changes the position.
            Point position = node.getPosition();
            node.setPosition(new Point(position.x + 5, position.y + 5));
        }

        for (PortImpl port : graphImpl.getPorts()) {
            addPort(port);
        }

        for (EdgeImpl edge : graphImpl.getEdges()) {
            addEdge(edge);
        }
    }

    /**
     * @see org.apache.airavata.workflow.model.graph.Graph#toXML()
     */
    public XmlElement toXML() {

        XmlElement graphElement = XMLUtil.BUILDER.newFragment(GraphSchema.NS, GraphSchema.GRAPH_TAG);

        graphElement.setAttributeValue(GraphSchema.NS, GraphSchema.XBAYA_VERSION_ATTRIBUTE,
                ApplicationVersion.VERSION.getVersion());

        XmlElement idElement = graphElement.addElement(GraphSchema.NS, GraphSchema.GRAPH_ID_TAG);
        idElement.addChild(getID());

        if (this.name != null) {
            XmlElement nameElement = graphElement.addElement(GraphSchema.NS, GraphSchema.GRAPH_NAME_TAG);
            nameElement.addChild(getName());
        }

        if (this.description != null) {
            XmlElement descriptionElement = graphElement.addElement(GraphSchema.NS,
                    GraphSchema.GRAPH_DESCRIPTION_TAG);
            descriptionElement.addChild(getDescription());
        }

        toXML(graphElement);

        for (NodeImpl node : this.nodes) {
            XmlElement nodeElement = node.toXML();
            graphElement.addChild(nodeElement);
        }

        for (PortImpl port : this.ports) {
            XmlElement portElement = port.toXML();
            graphElement.addChild(portElement);
        }

        for (EdgeImpl edge : this.edges) {
            XmlElement edgeElement = edge.toXML();
            graphElement.addChild(edgeElement);
        }

        return graphElement;
    }

    @Override
    public JsonObject toJSON() {
        JsonObject graphObject = new JsonObject();

        graphObject.addProperty(GraphSchema.XBAYA_VERSION_ATTRIBUTE, ApplicationVersion.VERSION.getVersion());
        graphObject.addProperty(GraphSchema.GRAPH_ID_TAG, getID());
        graphObject.addProperty(GraphSchema.GRAPH_NAME_TAG, getName());
        graphObject.addProperty(GraphSchema.GRAPH_DESCRIPTION_TAG, getDescription());

        JsonArray nodeArray = new JsonArray();
        for (NodeImpl node : this.nodes) {
            nodeArray.add(node.toJSON());
        }
        graphObject.add(GraphSchema.NODE_TAG, nodeArray);

        JsonArray portArray = new JsonArray();
        for (PortImpl port : this.ports) {
            portArray.add(port.toJSON());
        }
        graphObject.add(GraphSchema.PORT_TAG, portArray);

        JsonArray edgeArray = new JsonArray();
        for (EdgeImpl edge : this.edges) {
            edgeArray.add(edge.toJSON());
        }
        graphObject.add(GraphSchema.EDGE_TAG, edgeArray);

        return graphObject;
    }

    /**
     * @param graphElement
     */
    protected void toXML(@SuppressWarnings("unused") XmlElement graphElement) {
        // For subclass to overwrite.
    }

    /**
     * @param graphElement
     * @throws GraphException
     */
    protected void parse(XmlElement graphElement) throws GraphException {
        String version = graphElement.attributeValue(GraphSchema.NS, GraphSchema.XBAYA_VERSION_ATTRIBUTE);
        logger.debug("parsing a workflow created by version " + version);

        XmlElement idElement = graphElement.element(GraphSchema.GRAPH_ID_TAG);
        if (idElement != null) {
            this.id = idElement.requiredText();
        }

        XmlElement nameElement = graphElement.element(GraphSchema.GRAPH_NAME_TAG);
        if (nameElement != null) {
            this.name = nameElement.requiredText();
        }

        XmlElement descriptionElement = graphElement.element(GraphSchema.GRAPH_DESCRIPTION_TAG);
        if (descriptionElement != null) {
            this.description = descriptionElement.requiredText();
        }

        for (XmlElement nodeElement : graphElement.elements(null, GraphSchema.NODE_TAG)) {
            NodeImpl nodeImpl = this.factory.createNode(nodeElement);
            // need to call this to set this graph to the node.
            addNode(nodeImpl);
        }

        for (XmlElement portElement : graphElement.elements(null, GraphSchema.PORT_TAG)) {
            PortImpl port = this.factory.createPort(portElement);
            // need to call this to set this graph to the port.
            this.addPort(port);
        }

        for (XmlElement edgeElement : graphElement.elements(null, GraphSchema.EDGE_TAG)) {
            EdgeImpl edge = this.factory.createEdge(edgeElement);
            // need to call this to set this graph to the edge.
            this.addEdge(edge);
        }

        indexToPointer();
    }

    protected void parse(JsonObject graphObject) throws GraphException {
        JsonPrimitive jsonPrimitive = graphObject.getAsJsonPrimitive(GraphSchema.GRAPH_ID_TAG);
        if (jsonPrimitive != null) {
            this.id = jsonPrimitive.getAsString();
        }
        jsonPrimitive = graphObject.getAsJsonPrimitive(GraphSchema.GRAPH_NAME_TAG);
        if (jsonPrimitive != null) {
            this.name = jsonPrimitive.getAsString();
        }
        jsonPrimitive = graphObject.getAsJsonPrimitive(GraphSchema.GRAPH_DESCRIPTION_TAG);
        if (jsonPrimitive != null) {
            this.description = jsonPrimitive.getAsString();
        }

        JsonArray jArray = graphObject.getAsJsonArray(GraphSchema.NODE_TAG);
        for (JsonElement jsonElement : jArray) {
            addNode(this.factory.createNode((JsonObject) jsonElement));
        }

        jArray = graphObject.getAsJsonArray(GraphSchema.PORT_TAG);
        for (JsonElement jsonElement : jArray) {
            addPort(this.factory.createPort((JsonObject) jsonElement));
        }

        jArray = graphObject.getAsJsonArray(GraphSchema.EDGE_TAG);
        for (JsonElement jsonElement : jArray) {
            addEdge(this.factory.createEdge((JsonObject) jsonElement));
        }

        indexToPointer();
    }

    /**
     * Adds a node.
     * 
     * @param node
     *            the node to add
     */
    protected void addNode(NodeImpl node) {
        node.setGraph(this);
        // if this is a Stream now put it at the begining
        if (node instanceof StreamSourceNode) {
            this.nodes.add(0, node);
        } else {
            this.nodes.add(node);
        }
    }

    /**
     * @param port
     */
    protected void addPort(PortImpl port) {
        port.setGraph(this);
        this.ports.add(port);
    }

    /**
     * Converts indexes to references. This method is called after reading the graph from an XML file.
     * 
     * @throws GraphException
     */
    protected void indexToPointer() throws GraphException {
        for (NodeImpl node : this.nodes) {
            node.indexToPointer();
        }
        for (PortImpl port : this.ports) {
            port.indexToPointer();
        }
        for (EdgeImpl edge : this.edges) {
            edge.indexToPointer();
        }
    }

    /**
     * @param edge
     */
    private void addEdge(EdgeImpl edge) {
        edge.setGraph(this);
        this.edges.add(edge);
    }

    // private void createID() {
    // Date date = new Date();
    // SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd_HHmmss_S");
    // String time = format.format(date);
    //
    // this.id = StringUtil.convertToJavaIdentifier(this.name) + "_" + time;
    // }

    /**
     * @throws GraphException
     */
    public void fixParameterNodes() {
        // XXX fix the ports of parameter nodes for 2.6.3 or before.
        for (InputNode node : GraphUtil.getNodes(this, InputNode.class)) {
            DataPort oldPort = node.getOutputPort(0);
            if (oldPort instanceof WSPort) {
                node.getOutputPorts().remove(oldPort);
                this.ports.remove(oldPort);
                SystemDataPort newPort = new SystemDataPort();
                this.ports.add(newPort);
                newPort.setKind(Kind.DATA_OUT);
                newPort.setName(oldPort.getName());
                newPort.setGraph(this);
                newPort.setNode(node);
                newPort.createID();
                node.getOutputPorts().add(newPort);
                for (DataEdge edge : oldPort.getEdges()) {
                    edge.setFromPort(newPort);
                    newPort.getEdges().add(edge);
                }
            }
        }
        for (OutputNode node : GraphUtil.getNodes(this, OutputNode.class)) {
            DataPort oldPort = node.getInputPort(0);
            if (oldPort instanceof WSPort) {
                node.getInputPorts().remove(oldPort);
                this.ports.remove(oldPort);
                SystemDataPort newPort = new SystemDataPort();
                this.ports.add(newPort);
                newPort.setKind(Kind.DATA_IN);
                newPort.setName(oldPort.getName());
                newPort.setGraph(this);
                newPort.setNode(node);
                newPort.createID();
                node.getInputPorts().add(newPort);
                for (DataEdge edge : oldPort.getEdges()) {
                    edge.setToPort(newPort);
                    newPort.getEdges().add(edge);
                }
            }
        }
    }

    /**
     * This returns the number of input Nodes, this will be useful when adding unique Id for nodes
     * @return
     */
    public int getCurrentInputNodeCount() {
        int index = 0;
        for (Node node : nodes) {
            if (node instanceof InputNode) {
                index++;
            }
        }
        return index;
    }

    /**
    * This returns the number of input Nodes, this will be useful when adding unique Id for nodes
      * @return
      */
    public int getCurrentOutputNodeCount() {
        int index = 0;
        for (Node node : nodes) {
            if (node instanceof OutputNode) {
                index++;
            }
        }
        return index;
    }

}