Search in sources :

Example 1 with DagSubgraph

use of com.randomnoun.build.javaToGraphviz.dag.DagSubgraph in project java-to-graphviz by randomnoun.

the class ControlFlowEdger method addMethodDeclarationEdges.

// draw lines from each statement to each other, with an artifical node at the end
// that all the thrown exceptions throw to, and that all the return statements return to.
// 
// methods don't have any edges leaving them, but we add one anyway so that we can layout
// methods inside anonymous classes better in graphviz. ( this edge will be transparent )
private List<ExitEdge> addMethodDeclarationEdges(Dag dag, DagNode method, LexicalScope scope) {
    MethodDeclaration md = (MethodDeclaration) method.astNode;
    // method.label = "method " + md.getName();
    method.gvAttributes.put("methodName", md.getName().toString());
    // last child is the block
    DagNode methodBlock = method.children.get(method.children.size() - 1);
    addEdge(method, methodBlock);
    LexicalScope lexicalScope = new LexicalScope();
    List<ExitEdge> ee = addBlockEdges(dag, methodBlock, lexicalScope);
    // add a node which all the return edges return to
    // this is an artificial node so maybe only construct it based on some gv declaration earlier on ?
    // (whereas all the other nodes are about as concrete as anything else in IT)
    // CompilationUnit cu = methodBlock.astNode.getParent();
    CompilationUnit cu = ASTResolving.findParentCompilationUnit(methodBlock.astNode);
    int endOfMethodLine = cu.getLineNumber(methodBlock.astNode.getStartPosition() + methodBlock.astNode.getLength());
    DagNode returnNode = new DagNode();
    returnNode.keepNode = method.keepNodeMatcher.matches("methodDeclarationEnd");
    // label this 'end' if it's a void method ?
    returnNode.type = "methodDeclarationEnd";
    returnNode.lineNumber = endOfMethodLine;
    // rn.name = dag.getUniqueName("m_" + endOfMethodLine);
    returnNode.classes.add("method");
    returnNode.classes.add("end");
    // rn.label = "return";
    returnNode.astNode = null;
    // keep the return node in the method grouping
    method.children.add(returnNode);
    DagSubgraph sg = dag.dagNodeToSubgraph.get(method);
    dag.addNode(sg, returnNode);
    for (ExitEdge e : lexicalScope.returnEdges) {
        e.n2 = returnNode;
        addEdge(e);
    }
    // and everything that was thrown connects to this node as well
    for (ExitEdge e : lexicalScope.throwEdges) {
        e.n2 = returnNode;
        addEdge(e);
    }
    // and everything flowing out of the first block connects to this node as well
    for (ExitEdge e : ee) {
        e.n2 = returnNode;
        addEdge(e);
    }
    // there's no exit edges out of a method
    // return Collections.emptyList();
    // maybe there is now so we can draw an edge out of anonymous classes
    ExitEdge e = new ExitEdge();
    e.n1 = returnNode;
    return Collections.singletonList(e);
}
Also used : CompilationUnit(org.eclipse.jdt.core.dom.CompilationUnit) DagNode(com.randomnoun.build.javaToGraphviz.dag.DagNode) MethodDeclaration(org.eclipse.jdt.core.dom.MethodDeclaration) ExitEdge(com.randomnoun.build.javaToGraphviz.dag.ExitEdge) DagSubgraph(com.randomnoun.build.javaToGraphviz.dag.DagSubgraph)

Example 2 with DagSubgraph

use of com.randomnoun.build.javaToGraphviz.dag.DagSubgraph in project java-to-graphviz by randomnoun.

the class ControlFlowEdger method addInfixExpressionEdges.

private List<ExitEdge> addInfixExpressionEdges(Dag dag, DagNode infixNode, LexicalScope scope) {
    InfixExpression ie = (InfixExpression) infixNode.astNode;
    DagNode leftDag = getDagChild(infixNode.children, ie.getLeftOperand(), null);
    DagNode rightDag = getDagChild(infixNode.children, ie.getRightOperand(), null);
    List<DagNode> extendedDags = getDagChildren(infixNode.children, ie.extendedOperands(), null);
    // Operator is a class, not an enum (!)
    Operator op = ie.getOperator();
    infixNode.gvAttributes.put("operatorToken", op.toString());
    // @TODO camelcase
    infixNode.gvAttributes.put("operatorName", Text.getLastComponent(op.getClass().getName()));
    // a + b      becomes a -> b -> +
    // a + b + c  should becomes a -> b -> + -> c -> + , which has two + nodes, even though there's only one in the AST. because you know. eclipse.
    // move infixExpression node after the a and b nodes
    Rejigger rejigger = hoistNode(dag, infixNode, leftDag);
    List<ExitEdge> prevNodes = null;
    if (op == Operator.CONDITIONAL_AND || op == Operator.CONDITIONAL_OR) {
        // InfixExpressions also include shortcut || which doesn't evaluate the second parameter if the first is true
        // so it should be a something a bit more complicated, control-flow wise
        // a
        // true? -N->  b
        // :Y         :
        // v          v
        infixNode.classes.add("infixConditional");
        prevNodes = addExpressionEdges(dag, leftDag, scope);
        prevNodes = rejigger.unhoistNode(dag, prevNodes);
        // graphviz diagram is a bit mong unless we swap the false and true edge orders. maybe.
        ExitEdge trueEdge = prevNodes.get(0);
        trueEdge.classes.add("infixConditional");
        trueEdge.classes.add(op == Operator.CONDITIONAL_OR ? "true" : "false");
        DagEdge falseEdge = addEdge(infixNode, rightDag);
        // well this is the non-shortcut branch, but hey
        falseEdge.classes.add("infixConditional");
        falseEdge.classes.add(op == Operator.CONDITIONAL_OR ? "false" : "true");
        List<ExitEdge> lastPrevNodes = addExpressionEdges(dag, rightDag, scope);
        for (int i = 0; i < extendedDags.size(); i++) {
            // actually probably need to add a new node here
            DagNode n = extendedDags.get(i);
            DagNode extInfixNode = new DagNode();
            extInfixNode.keepNode = infixNode.keepNodeMatcher.matches("infixExpressionCondition");
            // even though it isn't
            extInfixNode.type = "infixExpressionCondition";
            // even though it isn't
            extInfixNode.lineNumber = n.lineNumber;
            extInfixNode.classes.add("infixExpression");
            extInfixNode.classes.add("infixConditional");
            extInfixNode.astNode = null;
            extInfixNode.gvAttributes.put("operatorToken", op.toString());
            // @TODO camelcase
            extInfixNode.gvAttributes.put("operatorName", Text.getLastComponent(op.getClass().getName()));
            DagSubgraph sg = dag.dagNodeToSubgraph.get(infixNode);
            dag.addNode(sg, extInfixNode);
            // needs to be a child of ieNode as well so it's moved to subgraphs when that node moves
            infixNode.children.add(extInfixNode);
            for (ExitEdge e : lastPrevNodes) {
                e.n2 = extInfixNode;
                dag.edges.add(e);
            }
            trueEdge = new ExitEdge();
            trueEdge.n1 = extInfixNode;
            trueEdge.classes.add("infixConditional");
            trueEdge.classes.add(op == Operator.CONDITIONAL_OR ? "true" : "false");
            prevNodes.add(0, trueEdge);
            falseEdge = addEdge(extInfixNode, n);
            // well this is the non-shortcut branch, but hey
            falseEdge.classes.add("infixConditional");
            falseEdge.classes.add(op == Operator.CONDITIONAL_OR ? "false" : "true");
            lastPrevNodes = addExpressionEdges(dag, n, scope);
        }
        prevNodes.addAll(lastPrevNodes);
    } else {
        // non-shortcut e.g. +, just evaluate in order
        for (DagNode a : infixNode.children) {
            if (prevNodes != null) {
                for (ExitEdge e : prevNodes) {
                    e.n2 = a;
                    addEdge(e);
                }
            }
            prevNodes = addExpressionEdges(dag, a, scope);
        }
        prevNodes = rejigger.unhoistNode(dag, prevNodes);
    }
    return prevNodes;
}
Also used : Operator(org.eclipse.jdt.core.dom.InfixExpression.Operator) DagNode(com.randomnoun.build.javaToGraphviz.dag.DagNode) ExitEdge(com.randomnoun.build.javaToGraphviz.dag.ExitEdge) InfixExpression(org.eclipse.jdt.core.dom.InfixExpression) DagSubgraph(com.randomnoun.build.javaToGraphviz.dag.DagSubgraph) DagEdge(com.randomnoun.build.javaToGraphviz.dag.DagEdge)

Example 3 with DagSubgraph

use of com.randomnoun.build.javaToGraphviz.dag.DagSubgraph in project java-to-graphviz by randomnoun.

the class ControlFlowEdger method addClassInstanceCreationEdges.

private List<ExitEdge> addClassInstanceCreationEdges(Dag dag, DagNode node, LexicalScope scope) {
    ClassInstanceCreation cic = (ClassInstanceCreation) node.astNode;
    // maybe we evaluate the index first ? not sure. reckon it's probably the array ref
    DagNode expressionDag = getDagChild(node.children, cic.getExpression(), null);
    List<DagNode> argumentDags = getDagChildren(node.children, cic.arguments(), null);
    DagNode anonClassDag = getDagChild(node.children, cic.getAnonymousClassDeclaration(), "anonymousClass");
    // AnonymousClassDeclaration
    node.gvAttributes.put("type", cic.getType().toString());
    // DagNode indexDag = getDagChild(node.children, fa.getIndex(), null);
    // DagNode fieldDag = getDagChild(node.children, fa.getName(), null); // will put the name on the FA node
    List<ExitEdge> prevNodes = null;
    if (expressionDag != null || argumentDags.size() > 0) {
        // move methodInvocation node after the expression & argument nodes
        Rejigger rejigger = hoistNode(dag, node, expressionDag != null ? expressionDag : argumentDags.get(0));
        // expression is null for method calls within the same object
        if (expressionDag != null) {
            prevNodes = addExpressionEdges(dag, expressionDag, scope);
        }
        for (DagNode a : argumentDags) {
            if (prevNodes != null) {
                for (ExitEdge e : prevNodes) {
                    e.n2 = a;
                    e.classes.add("invocationArgument");
                    addEdge(e);
                }
            }
            prevNodes = addExpressionEdges(dag, a, scope);
        }
        prevNodes = rejigger.unhoistNode(dag, prevNodes);
    } else {
        ExitEdge e = new ExitEdge();
        e.n1 = node;
        prevNodes = Collections.singletonList(e);
    }
    // jam the anonymous class in here as if it was a lambda, but with methods like a class
    if (anonClassDag != null) {
        for (ExitEdge e : prevNodes) {
            e.n2 = anonClassDag;
            addEdge(e);
        }
        AnonymousClassDeclaration acdNode = (AnonymousClassDeclaration) anonClassDag.astNode;
        List<DagNode> bodyDeclarationDags = getDagChildren(anonClassDag.children, acdNode.bodyDeclarations(), null);
        LexicalScope lexicalScope = scope.newTypeScope();
        // add edges for everything in the class
        // @TODO check fields don't appear though. unless they initalise things, then maybe. Or maybe that goes into the constructors.
        // or not.
        // yeesh what about the static initializers. what about them indeed.
        List<ExitEdge> ees = new ArrayList<>();
        for (DagNode n : bodyDeclarationDags) {
            // add a transparent edge to each thing defined in this class so that the 'AnonymousClassDeclaration' node appears above them
            DagEdge e = addEdge(anonClassDag, n);
            e.classes.add("anonymousClassDeclarationBegin");
            ees.addAll(addEdges(dag, n, lexicalScope));
        }
        // add an artificial node so we can create an edge out of this thing
        CompilationUnit cu = ASTResolving.findParentCompilationUnit(acdNode);
        int endOfAnonClassLine = cu.getLineNumber(acdNode.getStartPosition() + acdNode.getLength());
        DagNode returnNode = new DagNode();
        returnNode.keepNode = anonClassDag.keepNodeMatcher.matches("anonymousClassDeclarationEnd");
        returnNode.type = "anonymousClassDeclarationEnd";
        returnNode.lineNumber = endOfAnonClassLine;
        // rn.name = dag.getUniqueName("m_" + endOfMethodLine);
        returnNode.classes.add("anonymousClassDeclaration");
        returnNode.classes.add("end");
        // rn.label = "return";
        returnNode.astNode = null;
        // include the artificial return inside the lambda grouping
        anonClassDag.children.add(returnNode);
        DagSubgraph sg = dag.dagNodeToSubgraph.get(anonClassDag);
        dag.addNode(sg, returnNode);
        for (ExitEdge ee : ees) {
            // add a transparent edge from each thing defined in this class to the artifical node
            // so that it appears underneath them
            ee.n2 = returnNode;
            ee.classes.add("anonymousClassDeclarationEnd");
            addEdge(ee);
        }
        // there's no exit edges out of an anonymous class
        // this gets truncated to the subgraph boundary in some css
        ExitEdge e = new ExitEdge();
        e.n1 = returnNode;
        prevNodes = Collections.singletonList(e);
    }
    return prevNodes;
}
Also used : ClassInstanceCreation(org.eclipse.jdt.core.dom.ClassInstanceCreation) CompilationUnit(org.eclipse.jdt.core.dom.CompilationUnit) AnonymousClassDeclaration(org.eclipse.jdt.core.dom.AnonymousClassDeclaration) ArrayList(java.util.ArrayList) DagSubgraph(com.randomnoun.build.javaToGraphviz.dag.DagSubgraph) DagNode(com.randomnoun.build.javaToGraphviz.dag.DagNode) ExitEdge(com.randomnoun.build.javaToGraphviz.dag.ExitEdge) DagEdge(com.randomnoun.build.javaToGraphviz.dag.DagEdge)

Example 4 with DagSubgraph

use of com.randomnoun.build.javaToGraphviz.dag.DagSubgraph in project java-to-graphviz by randomnoun.

the class ControlFlowEdger method addLambdaExpressionEdges.

private List<ExitEdge> addLambdaExpressionEdges(Dag dag, DagNode lambdaNode, LexicalScope scope) {
    // lambda expression is going to be a big like a method declaration in the middle of a method.
    // but has an exit edge
    LambdaExpression le = (LambdaExpression) lambdaNode.astNode;
    // method.label = "method " + md.getName();
    // method.gvAttributes.put("methodName",  md.getName().toString());
    // we don't create nodes for method parameters yet, so not going to do that for lambdas either
    // (maybe we should ? )
    // when a lambda is defined control flow doesn't pass to the block,
    // so maybe I skip all of that somehow. OK so let's create an edge so it's grouped together but hide it in the diagram.
    DagNode blockNode = lambdaNode.children.get(lambdaNode.children.size() - 1);
    DagEdge lambdaEntryEdge = addEdge(lambdaNode, blockNode);
    lambdaEntryEdge.classes.add("lambdaEntry");
    // @TODO these probably need a new lexical scope
    LexicalScope lexicalScope = scope.newLambdaScope();
    List<ExitEdge> ee;
    if (blockNode.type.equals("Block")) {
        ee = addBlockEdges(dag, blockNode, lexicalScope);
    } else if (blockNode.astNode instanceof Expression) {
        ee = addExpressionEdges(dag, blockNode, lexicalScope);
    } else {
        throw new IllegalStateException("expected Block or Expression in lambda");
    }
    // add a node which all the return edges return to
    // this is an artificial node so maybe only construct it based on some gv declaration earlier on ?
    // (whereas all the other nodes are about as concrete as anything else in IT)
    // CompilationUnit cu = methodBlock.astNode.getParent();
    CompilationUnit cu = ASTResolving.findParentCompilationUnit(le);
    int endOfLambdaLine = cu.getLineNumber(le.getStartPosition() + le.getLength());
    DagNode returnNode = new DagNode();
    returnNode.keepNode = lambdaNode.keepNodeMatcher.matches("lambdaExpressionEnd");
    // label this 'end' if it has no return value ?
    returnNode.type = "lambdaExpressionEnd";
    returnNode.lineNumber = endOfLambdaLine;
    // rn.name = dag.getUniqueName("m_" + endOfMethodLine);
    returnNode.classes.add("lambdaExpression");
    returnNode.classes.add("end");
    // rn.label = "return";
    returnNode.astNode = null;
    // include the artificial return inside the lambda grouping
    lambdaNode.children.add(returnNode);
    DagSubgraph sg = dag.dagNodeToSubgraph.get(lambdaNode);
    dag.addNode(sg, returnNode);
    for (ExitEdge e : lexicalScope.returnEdges) {
        e.n2 = returnNode;
        addEdge(e);
    }
    for (ExitEdge e : ee) {
        e.n2 = returnNode;
        addEdge(e);
    }
    // and everything that was thrown connects to this node as well
    for (ExitEdge e : lexicalScope.throwEdges) {
        e.n2 = returnNode;
        addEdge(e);
    }
    // there's no exit edges out of a method, but let's say there is from a lambda
    // (it's not a real edge, it gets truncated to the subgraph boundary in some css)
    ExitEdge e = new ExitEdge();
    e.n1 = returnNode;
    return Collections.singletonList(e);
}
Also used : CompilationUnit(org.eclipse.jdt.core.dom.CompilationUnit) DagNode(com.randomnoun.build.javaToGraphviz.dag.DagNode) ConditionalExpression(org.eclipse.jdt.core.dom.ConditionalExpression) InstanceofExpression(org.eclipse.jdt.core.dom.InstanceofExpression) ThisExpression(org.eclipse.jdt.core.dom.ThisExpression) Expression(org.eclipse.jdt.core.dom.Expression) InfixExpression(org.eclipse.jdt.core.dom.InfixExpression) CastExpression(org.eclipse.jdt.core.dom.CastExpression) VariableDeclarationExpression(org.eclipse.jdt.core.dom.VariableDeclarationExpression) PostfixExpression(org.eclipse.jdt.core.dom.PostfixExpression) ParenthesizedExpression(org.eclipse.jdt.core.dom.ParenthesizedExpression) LambdaExpression(org.eclipse.jdt.core.dom.LambdaExpression) PrefixExpression(org.eclipse.jdt.core.dom.PrefixExpression) ExitEdge(com.randomnoun.build.javaToGraphviz.dag.ExitEdge) DagSubgraph(com.randomnoun.build.javaToGraphviz.dag.DagSubgraph) LambdaExpression(org.eclipse.jdt.core.dom.LambdaExpression) DagEdge(com.randomnoun.build.javaToGraphviz.dag.DagEdge)

Example 5 with DagSubgraph

use of com.randomnoun.build.javaToGraphviz.dag.DagSubgraph in project java-to-graphviz by randomnoun.

the class JavaToGraphviz method writeGraphviz.

/**
 * Generate a single graphviz diagram.
 *
 * @param writer
 * @return true if there are more diagrams, false otherwise
 * @throws IOException
 */
public boolean writeGraphviz(Writer writer) throws IOException {
    DagSubgraph rootGraph = dag.rootGraphs.get(rootGraphIdx);
    PrintWriter pw = new PrintWriter(writer);
    // remove edges from previous runs
    dag.edges.clear();
    // a rootGraph can now contain multiple rootNodes
    // but a rootNode can only be in a single rootGraph
    int c = 0;
    for (DagNode rootNode : dag.rootNodes) {
        c++;
        if (rootGraph.nodes.contains(rootNode)) {
            // logger.info("including rootNode " + c);
            String edgerNamesCsv = rootNode.options.get("edgerNamesCsv");
            if (Text.isBlank(edgerNamesCsv)) {
                edgerNamesCsv = "control-flow";
            }
            boolean enableKeepNodeFilter = "true".equals(rootNode.options.get("enableKeepNodeFilter"));
            List<String> edgerNames;
            try {
                edgerNames = Text.parseCsv(edgerNamesCsv);
            } catch (ParseException e1) {
                throw new IllegalArgumentException("edgerNamesCsv is not valid CSV", e1);
            }
            rootNode.options.get("removeNodes");
            LexicalScope lexicalScope = new LexicalScope();
            for (String edgerName : edgerNames) {
                if (edgerName.equals("control-flow")) {
                    ControlFlowEdger edger = new ControlFlowEdger(dag);
                    edger.addEdges(dag, rootNode, lexicalScope);
                } else if (edgerName.equals("ast")) {
                    AstEdger edger = new AstEdger(dag);
                    edger.addEdges(dag, rootNode, lexicalScope);
                // @TODO data-flow
                } else {
                    throw new IllegalArgumentException("unknown edgerName '" + edgerName + "'");
                }
            }
            // going to assume that edges don't cross rootNodes, which they don't yet either.
            for (DagNode n : dag.nodes) {
                n.inEdges = new ArrayList<>();
                n.outEdges = new ArrayList<>();
            }
            for (DagEdge e : dag.edges) {
                e.n1.outEdges.add(e);
                e.n2.inEdges.add(e);
            }
            if (enableKeepNodeFilter) {
                DagNodeFilter filter = new DagNodeFilter(dag);
                if (rootNode.keepNodeMatcher.matches("startNode")) {
                    rootNode.keepNode = true;
                    filter.setLastKeepNode(rootNode, rootNode);
                } else {
                    rootNode.keepNode = false;
                }
                filter.removeNodes(rootNode);
            }
        }
    }
    // subgraphs are now defined in the Dag from the stylesheet
    DagStyleApplier dsa = new DagStyleApplier(dag, rootGraph);
    Document doc = dsa.createDom();
    if (format.equals("dom1")) {
        pw.println(doc.toString());
    } else {
        dsa.inlineStyles(styleSheet);
    }
    if (format.equals("dom1")) {
    // already generated output
    } else if (format.equals("dom2")) {
        doc = dsa.getDocument();
        pw.println(doc.toString());
    } else {
        pw.println(rootGraph.toGraphviz(0));
    }
    rootGraphIdx++;
    return (rootGraphIdx < dag.rootGraphs.size());
}
Also used : LexicalScope(com.randomnoun.build.javaToGraphviz.astToDag.LexicalScope) DagNodeFilter(com.randomnoun.build.javaToGraphviz.astToDag.DagNodeFilter) DagSubgraph(com.randomnoun.build.javaToGraphviz.dag.DagSubgraph) Document(org.jsoup.nodes.Document) DagNode(com.randomnoun.build.javaToGraphviz.dag.DagNode) AstEdger(com.randomnoun.build.javaToGraphviz.astToDag.AstEdger) ParseException(java.text.ParseException) ControlFlowEdger(com.randomnoun.build.javaToGraphviz.astToDag.ControlFlowEdger) DagStyleApplier(com.randomnoun.build.javaToGraphviz.astToDag.DagStyleApplier) DagEdge(com.randomnoun.build.javaToGraphviz.dag.DagEdge) PrintWriter(java.io.PrintWriter)

Aggregations

DagNode (com.randomnoun.build.javaToGraphviz.dag.DagNode)11 DagSubgraph (com.randomnoun.build.javaToGraphviz.dag.DagSubgraph)11 DagEdge (com.randomnoun.build.javaToGraphviz.dag.DagEdge)6 ExitEdge (com.randomnoun.build.javaToGraphviz.dag.ExitEdge)4 DagElement (com.randomnoun.build.javaToGraphviz.dom.DagElement)3 CompilationUnit (org.eclipse.jdt.core.dom.CompilationUnit)3 CommentText (com.randomnoun.build.javaToGraphviz.comment.CommentText)2 GvComment (com.randomnoun.build.javaToGraphviz.comment.GvComment)2 GvEndGraphComment (com.randomnoun.build.javaToGraphviz.comment.GvEndGraphComment)2 GvEndSubgraphComment (com.randomnoun.build.javaToGraphviz.comment.GvEndSubgraphComment)2 GvGraphComment (com.randomnoun.build.javaToGraphviz.comment.GvGraphComment)2 GvKeepNodeComment (com.randomnoun.build.javaToGraphviz.comment.GvKeepNodeComment)2 GvLiteralComment (com.randomnoun.build.javaToGraphviz.comment.GvLiteralComment)2 GvOptionComment (com.randomnoun.build.javaToGraphviz.comment.GvOptionComment)2 GvSubgraphComment (com.randomnoun.build.javaToGraphviz.comment.GvSubgraphComment)2 ExceptionErrorHandler (com.randomnoun.build.javaToGraphviz.dom.StylesheetApplier.ExceptionErrorHandler)2 CSSOMParser (com.steadystate.css.parser.CSSOMParser)2 ArrayList (java.util.ArrayList)2 InfixExpression (org.eclipse.jdt.core.dom.InfixExpression)2 Node (org.jsoup.nodes.Node)2