workcraft/workcraft

View on GitHub
workcraft/StgPlugin/src/org/workcraft/plugins/stg/VisualStg.java

Summary

Maintainability
C
1 day
Test Coverage
package org.workcraft.plugins.stg;

import org.workcraft.annotations.DisplayName;
import org.workcraft.dom.*;
import org.workcraft.dom.hierarchy.NamespaceHelper;
import org.workcraft.dom.math.MathConnection;
import org.workcraft.dom.math.MathNode;
import org.workcraft.dom.references.Identifier;
import org.workcraft.dom.visual.*;
import org.workcraft.dom.visual.connections.VisualConnection;
import org.workcraft.exceptions.InvalidConnectionException;
import org.workcraft.exceptions.NodeCreationException;
import org.workcraft.gui.properties.ModelProperties;
import org.workcraft.gui.properties.PropertyHelper;
import org.workcraft.gui.tools.CommentGeneratorTool;
import org.workcraft.plugins.petri.*;
import org.workcraft.plugins.petri.utils.ConversionUtils;
import org.workcraft.plugins.stg.tools.*;
import org.workcraft.plugins.stg.utils.ConnectionUtils;
import org.workcraft.types.Pair;
import org.workcraft.utils.Hierarchy;

import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;

@DisplayName("Signal Transition Graph")
public class VisualStg extends AbstractVisualModel {

    public VisualStg(Stg model) {
        this(model, null);
    }

    public VisualStg(Stg model, VisualGroup root) {
        super(model, root);
    }

    @Override
    public void createDefaultStructure() {
        super.createDefaultStructure();
        // Convert dual producer-consumer arcs into read-arcs
        HashSet<Pair<VisualConnection, VisualConnection>> dualArcs = ConversionUtils.getSelectedOrAllDualArcs(this);
        ConversionUtils.convertDualArcsToReadArcs(this, dualArcs);

        // Hide implicit places
        // FIXME: Implicit places should not appear in the first place.
        for (VisualStgPlace vp : getVisualPlaces()) {
            StgPlace place = vp.getReferencedComponent();
            if ((place != null) && place.isImplicit()) {
                makeImplicitIfPossible(vp, false);
            }
        }
    }

    @Override
    public void registerGraphEditorTools() {
        addGraphEditorTool(new StgSelectionTool());
        addGraphEditorTool(new CommentGeneratorTool());
        addGraphEditorTool(new StgConnectionTool());
        addGraphEditorTool(new StgReadArcConnectionTool());
        addGraphEditorTool(new PlaceGeneratorTool());
        addGraphEditorTool(new SignalTransitionGeneratorTool());
        addGraphEditorTool(new DummyTransitionGeneratorTool());
        addGraphEditorTool(new StgSimulationTool());
        addGraphEditorTool(new EncodingConflictAnalyserTool());
    }

    @Override
    public Stg getMathModel() {
        return (Stg) super.getMathModel();
    }

    @Override
    public void validateConnection(VisualNode first, VisualNode second) throws InvalidConnectionException {
        if (first == second) {
            throw new InvalidConnectionException("Self-loops are not allowed.");
        }
        if (((first instanceof VisualStgPlace) || (first instanceof VisualReplicaPlace) || (first instanceof VisualImplicitPlaceArc))
                && ((second instanceof VisualStgPlace) || (second instanceof VisualReplicaPlace) || (second instanceof VisualImplicitPlaceArc))) {
            throw new InvalidConnectionException("Arcs between places are not allowed.");
        }
        if (ConnectionUtils.hasReadArcConnection(this, first, second) || ConnectionUtils.hasReadArcConnection(this, second, first)) {
            throw new InvalidConnectionException("Nodes are already connected by a read-arc.");
        }
        if (ConnectionUtils.hasProducingArcConnection(this, first, second)) {
            throw new InvalidConnectionException("This producing arc already exists.");
        }
        if (ConnectionUtils.hasConsumingArcConnection(this, first, second)) {
            throw new InvalidConnectionException("This consuming arc already exists.");
        }
        if (ConnectionUtils.hasImplicitPlaceArcConnection(this, first, second)) {
            throw new InvalidConnectionException("This implicit place arc already exists.");
        }
    }

    @Override
    public VisualConnection connect(VisualNode first, VisualNode second, MathConnection mConnection)
            throws InvalidConnectionException {

        validateConnection(first, second);

        VisualConnection connection = null;
        if (first instanceof VisualNamedTransition) {
            if (second instanceof VisualNamedTransition) {
                connection = createImplicitPlaceConnection((VisualNamedTransition) first, (VisualNamedTransition) second);
            } else if (second instanceof VisualImplicitPlaceArc) {
                VisualImplicitPlaceArc con = (VisualImplicitPlaceArc) second;
                VisualStgPlace place = makeExplicit(con);
                connection = connect(first, place);
            } else if ((second instanceof VisualStgPlace) || (second instanceof VisualReplicaPlace)) {
                connection = createSimpleConnection(first, second, mConnection);
            }
        } else if (first instanceof VisualImplicitPlaceArc) {
            if (second instanceof VisualNamedTransition) {
                VisualImplicitPlaceArc con = (VisualImplicitPlaceArc) first;
                VisualStgPlace place = makeExplicit(con);
                connection = connect(place, second);
            }
        } else if ((first instanceof VisualStgPlace) || (first instanceof VisualReplicaPlace)) {
            connection = createSimpleConnection(first, second, mConnection);
        }
        return connection;
    }

    private VisualImplicitPlaceArc createImplicitPlaceConnection(VisualNamedTransition t1, VisualNamedTransition t2)
            throws InvalidConnectionException  {

        ImplicitPlaceConnection c = getMathModel().connect(t1.getReferencedComponent(), t2.getReferencedComponent());

        StgPlace implicitPlace = c.getImplicitPlace();
        MathConnection con1 = c.getFirst();
        MathConnection con2 = c.getSecond();

        VisualImplicitPlaceArc connection = new VisualImplicitPlaceArc(t1, t2, con1, con2, implicitPlace);
        Hierarchy.getNearestContainer(t1, t2).add(connection);
        return connection;
    }

    private VisualConnection createSimpleConnection(VisualNode first, VisualNode second, MathConnection mConnection)
            throws InvalidConnectionException {

        if (mConnection == null) {
            MathNode firstRef = getReferencedComponent(first);
            MathNode secondRef = getReferencedComponent(second);
            mConnection = getMathModel().connect(firstRef, secondRef);
        }
        VisualConnection connection = new VisualConnection(mConnection, first, second);
        Container container = Hierarchy.getNearestContainer(first, second);
        container.add(connection);
        return connection;
    }

    @Override
    public void validateUndirectedConnection(VisualNode first, VisualNode second) throws InvalidConnectionException {
        if (first == second) {
            throw new InvalidConnectionException("Self-loops are not allowed.");
        }
        if (((first instanceof VisualStgPlace) || (first instanceof VisualReplicaPlace) || (first instanceof VisualImplicitPlaceArc))
                && ((second instanceof VisualStgPlace) || (second instanceof VisualReplicaPlace) || (second instanceof VisualImplicitPlaceArc))) {
            throw new InvalidConnectionException("Arcs between places are not allowed.");
        }
        if ((first instanceof VisualTransition) && (second instanceof VisualTransition)) {
            throw new InvalidConnectionException("Read-arcs between transitions are not allowed.");
        }
        if (ConnectionUtils.hasReadArcConnection(this, first, second)
                || ConnectionUtils.hasReadArcConnection(this, second, first)
                || ConnectionUtils.hasProducingArcConnection(this, first, second)
                || ConnectionUtils.hasProducingArcConnection(this, second, first)
                || ConnectionUtils.hasConsumingArcConnection(this, first, second)
                || ConnectionUtils.hasConsumingArcConnection(this, second, first)) {
            throw new InvalidConnectionException("Nodes are already connected.");
        }
    }

    @Override
    public VisualConnection connectUndirected(VisualNode first, VisualNode second) throws InvalidConnectionException {
        validateUndirectedConnection(first, second);

        VisualNode placeOrReplica = null;
        VisualTransition transition = null;
        if (first instanceof VisualTransition) {
            transition = (VisualTransition) first;
            placeOrReplica = (second instanceof VisualImplicitPlaceArc)
                    ? makeExplicit((VisualImplicitPlaceArc) second) : second;
        } else if (second instanceof VisualTransition) {
            transition = (VisualTransition) second;
            placeOrReplica = (first instanceof VisualImplicitPlaceArc)
                    ? makeExplicit((VisualImplicitPlaceArc) first) : first;
        }
        return createReadArcConnection(placeOrReplica, transition);
    }

    private VisualReadArc createReadArcConnection(VisualNode placeOrReplica, VisualTransition transition)
            throws InvalidConnectionException {

        Place mPlace = null;
        if (placeOrReplica instanceof VisualStgPlace) {
            mPlace = ((VisualStgPlace) placeOrReplica).getReferencedComponent();
        } else if (placeOrReplica instanceof VisualReplicaPlace) {
            mPlace = ((VisualReplicaPlace) placeOrReplica).getReferencedPlace();
        }
        Transition mTransition = transition == null ? null : transition.getReferencedComponent();

        VisualReadArc connection = null;
        if ((mPlace != null) && (mTransition != null)) {
            MathConnection mConsumingConnection = getMathModel().connect(mPlace, mTransition);
            MathConnection mProducingConnection = getMathModel().connect(mTransition, mPlace);

            connection = new VisualReadArc(placeOrReplica, transition, mConsumingConnection, mProducingConnection);
            Container container = Hierarchy.getNearestContainer(placeOrReplica, transition);
            container.add(connection);
        }
        return connection;
    }

    public VisualStgPlace makeExplicit(VisualImplicitPlaceArc connection) {
        Container group = Hierarchy.getNearestAncestor(connection, Container.class);
        Point2D splitPoint = connection.getSplitPoint();
        StgPlace implicitPlace = connection.getImplicitPlace();
        getMathModel().makeExplicit(implicitPlace);
        VisualStgPlace place = new VisualStgPlace(implicitPlace);
        place.setPosition(splitPoint);

        VisualConnection con1 = new VisualConnection(connection.getRefCon1(), connection.getFirst(), place);
        VisualConnection con2 = new VisualConnection(connection.getRefCon2(), place, connection.getSecond());

        group.add(place);
        group.add(con1);
        group.add(con2);

        LinkedList<Point2D> prefixLocationsInRootSpace = ConnectionHelper.getPrefixControlPoints(connection, splitPoint);
        ConnectionHelper.addControlPoints(con1, prefixLocationsInRootSpace);
        LinkedList<Point2D> suffixLocationsInRootSpace = ConnectionHelper.getSuffixControlPoints(connection, splitPoint);
        ConnectionHelper.addControlPoints(con2, suffixLocationsInRootSpace);

        con1.copyStyle(connection);
        con2.copyStyle(connection);

        remove(connection);
        return place;
    }

    public VisualImplicitPlaceArc makeImplicitIfPossible(VisualStgPlace place, boolean preserveConnectionShape) {
        VisualImplicitPlaceArc connection = null;
        Collection<VisualNode> preset = getPreset(place);
        Collection<VisualNode> postset = getPostset(place);
        Collection<Replica> replicas = place.getReplicas();
        if ((preset.size() == 1) && (postset.size() == 1) && replicas.isEmpty()) {
            VisualComponent first = (VisualComponent) preset.iterator().next();
            VisualComponent second = (VisualComponent) postset.iterator().next();
            if (!ConnectionUtils.hasImplicitPlaceArcConnection(this, first, second)) {
                StgPlace explicitPlace = place.getReferencedComponent();
                getMathModel().makeImplicitIfPossible(explicitPlace);

                VisualConnection con1 = null;
                VisualConnection con2 = null;
                Collection<Connection> connections = new ArrayList<>(getConnections(place));
                for (Connection con: connections) {
                    if (con.getFirst() == place) {
                        con2 = (VisualConnection) con;
                    } else if (con.getSecond() == place) {
                        con1 = (VisualConnection) con;
                    }
                }
                if ((con1 != null) && (con2 != null)) {
                    MathConnection refCon1 = con1.getReferencedConnection();
                    MathConnection refCon2 = con2.getReferencedConnection();
                    connection = new VisualImplicitPlaceArc(first, second, refCon1, refCon2, place.getReferencedComponent());
                    Container parent = Hierarchy.getNearestAncestor(Hierarchy.getCommonParent(first, second), Container.class);
                    parent.add(connection);
                    if (preserveConnectionShape) {
                        LinkedList<Point2D> locations = ConnectionHelper.getMergedControlPoints(place, con1, con2);
                        ConnectionHelper.addControlPoints(connection, locations);
                    }
                }
                // Remove explicit place, all its connections will get removed automatically by the hanging connection remover
                remove(place);
            }
        }
        return connection;
    }

    public VisualStgPlace createVisualPlace(String mathName) {
        return createVisualPlace(mathName, getRoot());
    }

    public VisualStgPlace createVisualPlace(String mathName, Container container) {
        if (NamespaceHelper.isHierarchical(mathName)) {
            throw new RuntimeException("Cannot create a place with the hierarchical name '" + mathName + "'.");
        }
        Container mathContainer = NamespaceHelper.getMathContainer(this, container);
        StgPlace mathPlace = getMathModel().createPlace(mathName, mathContainer);
        return createVisualComponent(mathPlace, VisualStgPlace.class, container);
    }

    public VisualDummyTransition createVisualDummyTransition(String mathName) {
        return createVisualDummyTransition(mathName, getRoot());
    }

    public VisualDummyTransition createVisualDummyTransition(String mathName, Container container) {
        if (NamespaceHelper.isHierarchical(mathName)) {
            throw new RuntimeException("Cannot create a dummy with the hierarchical name '" + mathName + "'.");
        }
        Container mathContainer = NamespaceHelper.getMathContainer(this, container);
        DummyTransition mathTransition = getMathModel().createDummyTransition(mathName, mathContainer);
        return createVisualComponent(mathTransition, VisualDummyTransition.class, container);
    }

    public VisualSignalTransition createVisualSignalTransition(String signalName, Signal.Type type,
            SignalTransition.Direction direction) {

        return createVisualSignalTransition(signalName, type, direction, getRoot());
    }

    public VisualSignalTransition createVisualSignalTransition(String signalName, Signal.Type type,
            SignalTransition.Direction direction, Container container) {

        if (NamespaceHelper.isHierarchical(signalName)) {
            throw new RuntimeException("Cannot create a transition of a signal with the hierarchical name '" + signalName + "'.");
        }

        String transitionName = (signalName == null) || (direction == null) ? null : signalName + direction;
        try {
            SignalTransition mathTransition = NodeFactory.createNode(SignalTransition.class);
            Container mathContainer = NamespaceHelper.getMathContainer(this, container);

            // Set signal type BEFORE adding the transition to its container - this will enable the use
            // of appropriate default name, which is important for the case when signalName is null
            mathTransition.setSignalType(type);
            mathContainer.add(mathTransition);

            if (transitionName != null) {
                setMathName(mathTransition, transitionName);
            }
            return createVisualComponent(mathTransition, VisualSignalTransition.class, container);
        } catch (NodeCreationException e) {
            String msg = "Cannot create " + type + " transition";
            if (transitionName != null) {
                msg += " '" + transitionName + "'";
            }
            String containerRef = getMathReference(container);
            if ((containerRef != null) && !containerRef.isEmpty()) {
                msg += " in page '" + Identifier.truncateNamespaceSeparator(containerRef) + "'";
            }
            throw new RuntimeException(msg);
        }
    }

    public Collection<VisualStgPlace> getVisualPlaces() {
        return Hierarchy.getDescendantsOfType(getRoot(), VisualStgPlace.class);
    }

    public Collection<VisualTransition> getVisualTransitions() {
        return Hierarchy.getDescendantsOfType(getRoot(), VisualTransition.class);
    }

    public Collection<VisualSignalTransition> getVisualSignalTransitions() {
        return Hierarchy.getDescendantsOfType(getRoot(), VisualSignalTransition.class);
    }

    public Collection<VisualDummyTransition> getVisualDummyTransitions() {
        return Hierarchy.getDescendantsOfType(getRoot(), VisualDummyTransition.class);
    }

    public Collection<VisualConnection> getVisualConnections() {
        return Hierarchy.getDescendantsOfType(getRoot(), VisualConnection.class);
    }

    public Collection<VisualImplicitPlaceArc> getVisualImplicitPlaceArcs() {
        return Hierarchy.getDescendantsOfType(getRoot(), VisualImplicitPlaceArc.class);
    }

    public String getSignalReference(VisualSignalTransition transition) {
        String ref = getMathReference(transition);
        String signalName = transition.getSignalName();
        String signalPath = NamespaceHelper.getParentReference(ref);
        return NamespaceHelper.getReference(signalPath, signalName);
    }

    @Override
    public String getMathReference(Node node) {
        if (node instanceof VisualImplicitPlaceArc) {
            VisualImplicitPlaceArc connection = (VisualImplicitPlaceArc) node;
            node = connection.getImplicitPlace();
        }
        return super.getMathReference(node);
    }

    @Override
    public ModelProperties getProperties(VisualNode node) {
        ModelProperties properties = super.getProperties(node);
        Stg stg = getMathModel();
        if (node == null) {
            properties.add(StgPropertyHelper.getRefinementProperty(stg));
            properties.add(PropertyHelper.getSignalSectionProperty(this));
            properties.addAll(StgPropertyHelper.getSignalProperties(this));
        } else if (node instanceof VisualSignalTransition) {
            SignalTransition transition = ((VisualSignalTransition) node).getReferencedComponent();
            properties.removeByName(Model.PROPERTY_NAME);
            properties.add(StgPropertyHelper.getSignalNameProperty(stg, transition));
            properties.add(StgPropertyHelper.getSignalTypeProperty(stg, transition));
            properties.add(StgPropertyHelper.getDirectionProperty(stg, transition));
            if (StgSettings.getShowTransitionInstance()) {
                properties.add(StgPropertyHelper.getInstanceProperty(stg, transition));
            }
        } else if (node instanceof VisualDummyTransition) {
            DummyTransition dummy = ((VisualDummyTransition) node).getReferencedComponent();
            if (StgSettings.getShowTransitionInstance()) {
                properties.add(StgPropertyHelper.getInstanceProperty(stg, dummy));
            }
        }
        return properties;
    }

}