org.graphwalker.io.factory.yed.YEdContextFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.graphwalker.io.factory.yed.YEdContextFactory.java

Source

package org.graphwalker.io.factory.yed;

/*
 * #%L
 * GraphWalker Input/Output
 * %%
 * Copyright (C) 2005 - 2014 GraphWalker
 * %%
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * #L%
 */

import com.yworks.xml.graphml.ArcEdgeDocument;
import com.yworks.xml.graphml.BezierEdgeDocument;
import com.yworks.xml.graphml.EdgeLabelType;
import com.yworks.xml.graphml.GenericEdgeDocument;
import com.yworks.xml.graphml.GenericGroupNodeDocument;
import com.yworks.xml.graphml.GenericNodeDocument;
import com.yworks.xml.graphml.GroupNodeDocument;
import com.yworks.xml.graphml.ImageNodeDocument;
import com.yworks.xml.graphml.NodeLabelType;
import com.yworks.xml.graphml.PolyLineEdgeDocument;
import com.yworks.xml.graphml.QuadCurveEdgeDocument;
import com.yworks.xml.graphml.ShapeNodeDocument;
import com.yworks.xml.graphml.SplineEdgeDocument;
import com.yworks.xml.graphml.TableNodeDocument;
import com.yworks.xml.graphml.impl.EdgeLabelTypeImpl;
import com.yworks.xml.graphml.impl.NodeLabelTypeImpl;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.apache.commons.io.FilenameUtils;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.graphdrawing.graphml.xmlns.DataType;
import org.graphdrawing.graphml.xmlns.GraphType;
import org.graphdrawing.graphml.xmlns.GraphmlDocument;
import org.graphdrawing.graphml.xmlns.NodeType;
import org.graphdrawing.graphml.xmlns.impl.DataTypeImpl;
import org.graphwalker.core.machine.Context;
import org.graphwalker.core.model.Action;
import org.graphwalker.core.model.Edge;
import org.graphwalker.core.model.Guard;
import org.graphwalker.core.model.Model;
import org.graphwalker.core.model.Requirement;
import org.graphwalker.core.model.Vertex;
import org.graphwalker.dsl.antlr.yed.YEdDescriptiveErrorListener;
import org.graphwalker.dsl.yed.YEdEdgeParser;
import org.graphwalker.dsl.yed.YEdLabelLexer;
import org.graphwalker.dsl.yed.YEdVertexParser;
import org.graphwalker.io.common.ResourceNotFoundException;
import org.graphwalker.io.common.ResourceUtils;
import org.graphwalker.io.factory.ContextFactory;
import org.graphwalker.io.factory.ContextFactoryException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author Nils Olsson
 */
public final class YEdContextFactory implements ContextFactory {

    private static final Logger logger = LoggerFactory.getLogger(YEdContextFactory.class);
    private static final String NAMESPACE = "declare namespace xq='http://graphml.graphdrawing.org/xmlns';";
    private static final String FILE_TYPE = "graphml";
    private static final Set<String> SUPPORTED_TYPE = new HashSet<>(Arrays.asList("**/*.graphml"));

    @Override
    public Set<String> getSupportedFileTypes() {
        return SUPPORTED_TYPE;
    }

    @Override
    public boolean accept(java.nio.file.Path path) {
        return FilenameUtils.getExtension(path.toString()).equalsIgnoreCase(FILE_TYPE);
    }

    @Override
    public Context create(Path path) {
        return create(path, new YEdContext());
    }

    @Override
    public List<Context> createMultiple(Path path) {
        return null;
    }

    @Override
    public <T extends Context> T create(Path path, T context) {
        Edge startEdge;
        Map<String, Vertex> elements = new HashMap<>();
        Model model = new Model();
        GraphmlDocument document = null;
        try {
            document = GraphmlDocument.Factory.parse(ResourceUtils.getResourceAsStream(path.toString()));
        } catch (XmlException e) {
            logger.error(e.getMessage());
            throw new ContextFactoryException("The file appears not to be valid yEd formatted.");
        } catch (IOException e) {
            logger.error(e.getMessage());
            throw new ContextFactoryException("Could not read the file.");
        } catch (ResourceNotFoundException e) {
            logger.error(e.getMessage());
            throw new ContextFactoryException("Could not read the file.");
        }
        try {
            Vertex startVertex = addVertices(model, context, document, elements);
            startEdge = addEdges(model, context, document, elements, startVertex);
        } catch (XmlException e) {
            logger.error(e.getMessage());
            throw new ContextFactoryException("The file seems not to be of valid yEd format.");
        }

        model.setName(FilenameUtils.getBaseName(path.toString()));
        context.setModel(model.build());
        if (null != startEdge) {
            context.setNextElement(startEdge);
        }

        return context;
    }

    @Override
    public <T extends Context> T write(T context, Path path) throws IOException {
        String newLine = System.getProperty("line.separator");
        StringBuilder str = new StringBuilder();

        str.append("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>").append(newLine);
        str.append("<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\"  "
                + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
                + "xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns "
                + "http://www.yworks.com/xml/schema/graphml/1.0/ygraphml.xsd\" "
                + "xmlns:y=\"http://www.yworks.com/xml/graphml\">").append(newLine);
        str.append("  <key id=\"d0\" for=\"node\" yfiles.type=\"nodegraphics\"/>").append(newLine);
        str.append("  <key id=\"d1\" for=\"edge\" yfiles.type=\"edgegraphics\"/>").append(newLine);
        str.append("  <graph id=\"G\" edgedefault=\"directed\">").append(newLine);

        for (Vertex.RuntimeVertex v : context.getModel().getVertices()) {
            str.append("    <node id=\"" + v.getId() + "\">").append(newLine);
            str.append("      <data key=\"d0\" >").append(newLine);
            str.append("        <y:ShapeNode >").append(newLine);
            str.append("          <y:Geometry  x=\"241.875\" y=\"158.701171875\" width=\"95.0\" height=\"30.0\"/>")
                    .append(newLine);
            str.append("          <y:Fill color=\"#CCCCFF\"  transparent=\"false\"/>").append(newLine);
            str.append("          <y:BorderStyle type=\"line\" width=\"1.0\" color=\"#000000\" />").append(newLine);
            str.append("          <y:NodeLabel x=\"1.5\" y=\"5.6494140625\" width=\"92.0\" height=\"18.701171875\" "
                    + "visible=\"true\" alignment=\"center\" fontFamily=\"Dialog\" fontSize=\"12\" "
                    + "fontStyle=\"plain\" textColor=\"#000000\" modelName=\"internal\" modelPosition=\"c\" "
                    + "autoSizePolicy=\"content\">" + v.getName());

            str.append("</y:NodeLabel>").append(newLine);
            str.append("          <y:Shape type=\"rectangle\"/>").append(newLine);
            str.append("        </y:ShapeNode>").append(newLine);
            str.append("      </data>").append(newLine);
            str.append("    </node>").append(newLine);
        }

        for (Edge.RuntimeEdge e : context.getModel().getEdges()) {
            Vertex.RuntimeVertex src = e.getSourceVertex();
            Vertex.RuntimeVertex dest = e.getTargetVertex();

            if (src == null || dest == null) {
                continue;
            }

            str.append("    <edge id=\"" + e.getId() + "\" source=\"" + src.getId() + "\" target=\"" + dest.getId()
                    + "\">").append(newLine);
            str.append("      <data key=\"d1\" >").append(newLine);
            str.append("        <y:PolyLineEdge >").append(newLine);
            str.append("          <y:Path sx=\"-23.75\" sy=\"15.0\" tx=\"-23.75\" ty=\"-15.0\">").append(newLine);
            str.append("            <y:Point x=\"273.3125\" y=\"95.0\"/>").append(newLine);
            str.append("            <y:Point x=\"209.5625\" y=\"95.0\"/>").append(newLine);
            str.append("            <y:Point x=\"209.5625\" y=\"143.701171875\"/>").append(newLine);
            str.append("            <y:Point x=\"265.625\" y=\"143.701171875\"/>").append(newLine);
            str.append("          </y:Path>").append(newLine);
            str.append("          <y:LineStyle type=\"line\" width=\"1.0\" color=\"#000000\" />").append(newLine);
            str.append("          <y:Arrows source=\"none\" target=\"standard\"/>").append(newLine);

            if (!e.getName().isEmpty()) {
                String label = e.getName();

                if (e.hasGuard()) {
                    label += newLine + "[" + e.getGuard().getScript() + "]";
                }
                if (e.hasActions()) {
                    label += newLine + "/";
                    for (Action action : e.getActions()) {
                        label += action.getScript();
                    }
                }

                label = label.replaceAll("&", "&amp;");
                label = label.replaceAll("<", "&lt;");
                label = label.replaceAll(">", "&gt;");
                label = label.replaceAll("'", "&apos;");
                label = label.replaceAll("\"", "&quot;");

                str.append(
                        "          <y:EdgeLabel x=\"-148.25\" y=\"30.000000000000014\" width=\"169.0\" height=\"18.701171875\" "
                                + "visible=\"true\" alignment=\"center\" fontFamily=\"Dialog\" fontSize=\"12\" "
                                + "fontStyle=\"plain\" textColor=\"#000000\" modelName=\"free\" modelPosition=\"anywhere\" "
                                + "preferredPlacement=\"on_edge\" distance=\"2.0\" ratio=\"0.5\">" + label);
                str.append("</y:EdgeLabel>").append(newLine);
            }

            str.append("          <y:BendStyle smoothed=\"false\"/>").append(newLine);
            str.append("        </y:PolyLineEdge>").append(newLine);
            str.append("      </data>").append(newLine);
            str.append("    </edge>").append(newLine);

        }

        str.append("  </graph>").append(newLine);
        str.append("</graphml>").append(newLine);

        Files.newOutputStream(path).write(String.valueOf(str).getBytes());
        return context;
    }

    private Vertex addVertices(Model model, Context context, GraphmlDocument document, Map<String, Vertex> elements)
            throws XmlException {
        Vertex startVertex = null;
        Deque<XmlObject> workQueue = new ArrayDeque<>();
        workQueue.addAll(Arrays.asList(document.selectPath(NAMESPACE + "$this/xq:graphml/xq:graph/xq:node")));
        while (!workQueue.isEmpty()) {
            XmlObject object = workQueue.pop();
            if (object instanceof NodeType) {
                NodeType node = (NodeType) object;
                if (0 < node.getGraphArray().length) {
                    for (GraphType subgraph : node.getGraphArray()) {
                        workQueue.addAll(Arrays.asList(subgraph.getNodeArray()));
                    }
                } else {
                    String description = "";
                    for (DataType data : node.getDataArray()) {
                        if (0 < data.getDomNode().getChildNodes().getLength()) {
                            if (data.getKey().equals("d5")) {
                                description = ((DataTypeImpl) data).getStringValue();
                            }
                            if (isSupportedNode(data.xmlText())) {
                                StringBuilder label = new StringBuilder();
                                for (NodeLabelType nodeLabel : getSupportedNode(data.xmlText())
                                        .getNodeLabelArray()) {
                                    label.append(((NodeLabelTypeImpl) nodeLabel).getStringValue());
                                }
                                YEdVertexParser parser = new YEdVertexParser(getTokenStream(label.toString()));
                                parser.removeErrorListeners();
                                parser.addErrorListener(YEdDescriptiveErrorListener.INSTANCE);
                                YEdVertexParser.ParseContext parseContext = parser.parse();
                                Vertex vertex = new Vertex();
                                if (!description.isEmpty()) {
                                    vertex.setProperty("description", description);
                                }
                                vertex.setProperty("x", getSupportedNode(data.xmlText()).getGeometry().getX());
                                vertex.setProperty("y", getSupportedNode(data.xmlText()).getGeometry().getY());
                                boolean blocked = false;
                                if (null != parseContext.start()) {
                                    elements.put(node.getId(), vertex);
                                    vertex.setId(node.getId());
                                    startVertex = vertex;
                                } else {
                                    for (YEdVertexParser.FieldContext field : parseContext.field()) {
                                        if (null != field.names()) {
                                            vertex.setName(field.names().getText());
                                        }
                                        if (null != field.shared() && null != field.shared().Identifier()) {
                                            vertex.setSharedState(field.shared().Identifier().getText());
                                        }
                                        if (null != field.reqtags()) {
                                            vertex.setRequirements(convertVertexRequirement(
                                                    field.reqtags().reqtagList().reqtag()));
                                        }
                                        if (null != field.actions()) {
                                            model.addActions(convertVertexAction(field.actions().action()));
                                        }
                                        if (null != field.blocked()) {
                                            blocked = true;
                                        }
                                    }
                                    if (!blocked) {
                                        elements.put(node.getId(), vertex);
                                        vertex.setId(node.getId());
                                        model.addVertex(vertex);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        return startVertex;
    }

    private boolean isSupportedNode(String xml) {
        return xml.contains("GenericNode") || xml.contains("ShapeNode") || xml.contains("GenericGroupNode")
                || xml.contains("GroupNode") || xml.contains("ImageNode") || xml.contains("TableNode");
    }

    private com.yworks.xml.graphml.NodeType getSupportedNode(String xml) throws XmlException {
        if (xml.contains("GenericNode")) {
            return GenericNodeDocument.Factory.parse(xml).getGenericNode();
        } else if (xml.contains("ShapeNode")) {
            return ShapeNodeDocument.Factory.parse(xml).getShapeNode();
        } else if (xml.contains("GenericGroupNode")) {
            return GenericGroupNodeDocument.Factory.parse(xml).getGenericGroupNode();
        } else if (xml.contains("GroupNode")) {
            return GroupNodeDocument.Factory.parse(xml).getGroupNode();
        } else if (xml.contains("ImageNode")) {
            return ImageNodeDocument.Factory.parse(xml).getImageNode();
        } else if (xml.contains("TableNode")) {
            return TableNodeDocument.Factory.parse(xml).getTableNode();
        }
        throw new ContextFactoryException("Unsupported node type: " + xml);
    }

    private Edge addEdges(Model model, Context context, GraphmlDocument document, Map<String, Vertex> elements,
            Vertex startVertex) throws XmlException {
        Edge startEdge = null;
        for (XmlObject object : document.selectPath(NAMESPACE + "$this/xq:graphml/xq:graph/xq:edge")) {
            if (object instanceof org.graphdrawing.graphml.xmlns.EdgeType) {
                org.graphdrawing.graphml.xmlns.EdgeType edgeType = (org.graphdrawing.graphml.xmlns.EdgeType) object;
                String description = "";
                for (DataType data : edgeType.getDataArray()) {
                    if (0 < data.getDomNode().getChildNodes().getLength()) {
                        if (data.getKey().equals("d9")) {
                            description = ((DataTypeImpl) data).getStringValue();
                        }
                        if (isSupportedEdge(data.xmlText())) {
                            StringBuilder label = new StringBuilder();
                            for (EdgeLabelType edgeLabel : getSupportedEdge(data.xmlText()).getEdgeLabelArray()) {
                                label.append(((EdgeLabelTypeImpl) edgeLabel).getStringValue());
                            }
                            YEdEdgeParser parser = new YEdEdgeParser(getTokenStream(label.toString()));
                            parser.removeErrorListeners();
                            parser.addErrorListener(YEdDescriptiveErrorListener.INSTANCE);
                            YEdEdgeParser.ParseContext parseContext = parser.parse();

                            Edge edge = new Edge();
                            if (!description.isEmpty()) {
                                edge.setProperty("description", description);
                            }
                            if (null != elements.get(edgeType.getSource())) {
                                edge.setSourceVertex(elements.get(edgeType.getSource()));
                            }
                            if (null != elements.get(edgeType.getTarget())) {
                                edge.setTargetVertex(elements.get(edgeType.getTarget()));
                            }
                            boolean blocked = false;
                            for (YEdEdgeParser.FieldContext field : parseContext.field()) {
                                if (null != field.names()) {
                                    edge.setName(field.names().getText());
                                }
                                if (null != field.guard()) {
                                    // TODO: Fix this in the parser
                                    String text = field.guard().getText().trim();
                                    edge.setGuard(new Guard(text.substring(1, text.length() - 1)));
                                }
                                if (null != field.actions()) {
                                    edge.addActions(convertEdgeAction(field.actions().action()));
                                }
                                if (null != field.reqtags()) {
                                    edge.setRequirements(
                                            convertEdgeRequirement(field.reqtags().reqtagList().reqtag()));
                                }
                                if (null != field.blocked()) {
                                    blocked = true;
                                }
                            }
                            if (!blocked && null != edge.getTargetVertex()) {
                                if (null != startVertex && edgeType.getSource().equals(startVertex.getId())) {
                                    edge.setSourceVertex(null);
                                    edge.setId(edgeType.getId());
                                    model.addEdge(edge);
                                    startEdge = edge;
                                } else if (null != edge.getSourceVertex()) {
                                    edge.setId(edgeType.getId());
                                    model.addEdge(edge);
                                }
                            }
                        }
                    }
                }
            }
        }
        return startEdge;
    }

    private boolean isSupportedEdge(String xml) {
        return xml.contains("PolyLineEdge") || xml.contains("GenericEdge") || xml.contains("ArcEdge")
                || xml.contains("QuadCurveEdge") || xml.contains("SplineEdge") || xml.contains("BezierEdge");
    }

    private com.yworks.xml.graphml.EdgeType getSupportedEdge(String xml) throws XmlException {
        if (xml.contains("GenericEdge")) {
            return GenericEdgeDocument.Factory.parse(xml).getGenericEdge();
        } else if (xml.contains("PolyLineEdge")) {
            return PolyLineEdgeDocument.Factory.parse(xml).getPolyLineEdge();
        } else if (xml.contains("ArcEdge")) {
            return ArcEdgeDocument.Factory.parse(xml).getArcEdge();
        } else if (xml.contains("QuadCurveEdge")) {
            return QuadCurveEdgeDocument.Factory.parse(xml).getQuadCurveEdge();
        } else if (xml.contains("SplineEdge")) {
            return SplineEdgeDocument.Factory.parse(xml).getSplineEdge();
        } else if (xml.contains("BezierEdge")) {
            return BezierEdgeDocument.Factory.parse(xml).getBezierEdge();
        }
        throw new ContextFactoryException("Unsupported edge type: " + xml);
    }

    private List<Action> convertEdgeAction(List<YEdEdgeParser.ActionContext> actionContexts) {
        List<Action> actions = new ArrayList<>();
        for (YEdEdgeParser.ActionContext actionContext : actionContexts) {
            actions.add(new Action(actionContext.getText()));
        }
        return actions;
    }

    private List<Action> convertVertexAction(List<YEdVertexParser.ActionContext> actionContexts) {
        List<Action> actions = new ArrayList<>();
        for (YEdVertexParser.ActionContext actionContext : actionContexts) {
            actions.add(new Action(actionContext.getText()));
        }
        return actions;
    }

    private Set<Requirement> convertEdgeRequirement(List<YEdEdgeParser.ReqtagContext> reqtagContexts) {
        Set<Requirement> requirements = new HashSet<>();
        for (YEdEdgeParser.ReqtagContext reqtagContext : reqtagContexts) {
            requirements.add(new Requirement(reqtagContext.getText()));
        }
        return requirements;
    }

    private Set<Requirement> convertVertexRequirement(List<YEdVertexParser.ReqtagContext> reqtagContexts) {
        Set<Requirement> requirements = new HashSet<>();
        for (YEdVertexParser.ReqtagContext reqtagContext : reqtagContexts) {
            requirements.add(new Requirement(reqtagContext.getText()));
        }
        return requirements;
    }

    private CommonTokenStream getTokenStream(String label) {
        ANTLRInputStream inputStream = new ANTLRInputStream(label);
        YEdLabelLexer lexer = new YEdLabelLexer(inputStream);
        lexer.removeErrorListeners();
        lexer.addErrorListener(YEdDescriptiveErrorListener.INSTANCE);
        return new CommonTokenStream(lexer);
    }

}