All Downloads are FREE. Search and download functionalities are using the official Maven repository.

live.document.mavenplugin.networkgraph.CallTree2BriefNetworkGraph Maven / Gradle / Ivy

package live.document.mavenplugin.networkgraph;

import com.fasterxml.jackson.databind.ObjectMapper;
import live.document.generator.model.CallNode;
import live.document.generator.model.CallTree;
import live.document.mavenplugin.common.CallNodeVisibleFunc;
import live.document.mavenplugin.common.Constants;
import live.document.plsqlscanner.PlSqlExplained;
import live.document.scanner.CallGraph;
import live.document.scanner.ClassObject;
import live.document.scanner.DbObjectTypeEnum;
import live.document.scanner.DbOperation;
import live.document.scanner.DbOperationTypeEnum;
import live.document.scanner.MethodObject;
import org.apache.maven.plugin.logging.Log;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

public class CallTree2BriefNetworkGraph {
    private ObjectMapper objectMapper = new ObjectMapper();
    private CallTree callTree;
    private List nodes;
    private List edges;
    private int yStep = 50;
    private int xStep = 100;
    private Log logger;
    Map nodeDependencies = new HashMap<>();
    Map nodeOwner = new HashMap<>();
    private Set specialEntitySet = new HashSet<>();
    private Set specialTableSet = new HashSet<>();
    private Map> allVisibleNodesOfRoot;
    private List allSharedNodes;
    private boolean needRandomX = true;
    private Set ignoreModules = new HashSet<>();

    public CallTree2BriefNetworkGraph(CallGraph callGraph, PlSqlExplained plSqlExplained, Set methods, CallNodeVisibleFunc visibleFunc, Log logger) {
        this.logger = logger;
        info("find method objects.");
        List allMethods = findMethodObjects(callGraph, methods);
        info("create call tree." + System.currentTimeMillis());
        callTree = new CallTree(callGraph, plSqlExplained, allMethods, n -> true);
        info("update call tree node visible.");
        callTree.updateNodesVisible(n -> visibleFunc.isVisible(n));
        info("init node dependencies." + System.currentTimeMillis());
        initNodeDependencies();
    }

    public CallTree2BriefNetworkGraph() {
    }

    public String generateJson(boolean showJavaMethod, boolean showDBProcedure) {
        NetworkGraph networkGraph = generate(showJavaMethod, showDBProcedure);
        networkGraph.setxStep(xStep);
        networkGraph.setyStep(yStep);
        networkGraph.setNeedRandom(needRandomX);
        networkGraph.layout();

        try {
            String result = objectMapper.writeValueAsString(networkGraph);
            if (result.length() > 2000) {
                result = formatJson(result);
            }
            return result;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private String formatJson(String json) {
        return json.replace("\"},{\"", "\"}," + System.lineSeparator() + "{\"")
                .replace("},{\"from\"", "}," + System.lineSeparator() + "{\"from\"")
                .replace(",\"dash\":null", "")
                .replace("\"color\":null,", "")
                .replace(",\"normal\":null", "")
                ;
    }

    NetworkGraph generate(boolean showJavaMethod, boolean showDBProcedure) {
        nodes = new ArrayList<>();
        edges = new ArrayList<>();

        allSharedNodes = findAllSharedNodes();
        removeNodes(allSharedNodes, showJavaMethod, showDBProcedure);

        info("found shared nodes: " + allSharedNodes.size());
        Map> allPathNodes = findAllPathNodes(allSharedNodes);

        allVisibleNodesOfRoot = initAllVisibleNodesOfRoot(allSharedNodes, allPathNodes);
        createNetworkGraph();

        createEdgeForGraphNode(allSharedNodes, allPathNodes.values());

        removeDuplicatedNodesOrEdges();
        sortRootNodes();

        return new NetworkGraph(nodes, edges);
    }

    private void removeNodes(List allSharedNodes, boolean showJavaMethod, boolean showDBProcedure) {
        Iterator iterator = allSharedNodes.iterator();
        while (iterator.hasNext()) {
            CallNode node = iterator.next();
            if (! showJavaMethod && node.getMethodObject() != null) {
                iterator.remove();
                continue;
            }

            if (! showDBProcedure && node.getDbOperation() != null) {
                iterator.remove();
                continue;
            }
        }
    }

    private Map> initAllVisibleNodesOfRoot(List allSharedNodes, Map> allPathNodes) {
        Map> results = new HashMap<>();

        Set allNodes = new HashSet<>();
        allNodes.addAll(allSharedNodes);
        allPathNodes.entrySet().stream().forEach(e -> allNodes.addAll(e.getValue()));

        for (CallNode node : allNodes) {
            String owner = nodeOwner.get(node.getId());
            if (! results.containsKey(owner)) {
                results.put(owner, new HashSet<>());
            }

            results.get(owner).add(node);
        }

        //Add all root nodes
        callTree.getNodes().forEach(r -> {
            if (results.containsKey(r.getId())) {
                results.get(r.getId()).add(r);
            }
        });

        return results;
    }

    private void createNetworkGraph() {
        for (CallNode node : callTree.getNodes()) {
            Set allNodes = allVisibleNodesOfRoot.get(node.getId());
            if (allNodes != null && allNodes.size() > 0) {
                Set boundaryNodes = new HashSet<>(findBoundaryNodes(node));
                allNodes.addAll(boundaryNodes);
                createNetworkGraphNode(node, allNodes, null, new ArrayList<>(), boundaryNodes);
            }
        }
    }

    private void createNetworkGraphNode(CallNode node, Set allNodes, CallNode lastNode, List callStack, Set boundaryNodes) {
        if (! node.isVisible()) {
            return;
        }

        callStack.add(getGraphNodeHint(node));
        if (allNodes.contains(node)) {
            if (!node.isLink()) {
                GraphNode graphNode = createGraphNode(node);
                if (allSharedNodes.contains(node)) {
                    String info = "
    " + getLeafNodeCallTree(node) + "
"; graphNode.setCallTree(info); graphNode.setGroup(info.length() > 1000 ? "largeShared" : "shared"); } else if (boundaryNodes.contains(node)) { String info = "
    " + getLeafNodeCallTree(node) + "
"; graphNode.setCallTree(info); } } if (lastNode != null) { if (node.isLink() && node.getLinkedNode() != null) { addGraphEdge(lastNode, node.getLinkedNode(), callStack); } else { addGraphEdge(lastNode, node, callStack); } } lastNode = node; callStack.clear(); callStack.add(getGraphNodeHint(node)); } CallNode oldLastNode = lastNode; List oldCallStack = new ArrayList<>(callStack); for (CallNode child : node.getChildren()) { lastNode = oldLastNode; callStack = new ArrayList<>(oldCallStack); createNetworkGraphNode(child, allNodes, lastNode, callStack, boundaryNodes); } } private void createEdgeForGraphNode(List allSharedNodes, Collection> values) { Set collect = values.stream() .flatMap(s -> s.stream()) .collect(Collectors.toSet()); collect.addAll(allSharedNodes); for (CallNode from : collect) { if (from.isLink()) { from = from.getLinkedNode(); } if (from.getMethodObject() == null) { continue; } for (CallNode to : collect) { if (to.isLink()) { to = to.getLinkedNode(); } if (to.getMethodObject() == null) { continue; } addEdgeForExistsGraphNode(from, to); } } } private void addEdgeForExistsGraphNode(CallNode from, CallNode to) { if (from.getId().equals(to.getId())) { return; } if (isEdgeExists(from, to)) { return; } from.getMethodObject().getCallees().stream() .filter(c -> to.getMethodObject().equals(c)) .map(c -> (MethodObject)c) .forEach(callee -> { List callStack = new ArrayList<>(); callStack.add(getGraphNodeHint(from)); callStack.add(getGraphNodeHint(to)); addGraphEdge(from, to, callStack); }); } private boolean isEdgeExists(CallNode from, CallNode to) { return edges.contains(new GraphEdge(from.getId(), to.getId())); } private String getLeafNodeCallTree(CallNode node) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("
  • ").append(getGraphNodeHint(node)); if (node.getChildren().size() > 0) { stringBuilder.append("
      "); for (CallNode child : node.getChildren()) { if (! child.isVisible()) { continue; } stringBuilder.append(getLeafNodeCallTree(child)); } stringBuilder.append("
    "); } stringBuilder.append("
  • "); return stringBuilder.toString(); } private void removeDuplicatedNodesOrEdges() { nodes = new ArrayList<>(new HashSet<>(nodes)); edges = new ArrayList<>(new HashSet<>(edges)); } private void sortRootNodes() { nodes.sort(Comparator.comparing(GraphNode::getHint)); } Map> findAllPathNodes(List sharedNodes) { Map> results = new HashMap<>(); for (CallNode sharedNode : sharedNodes) { results.put(sharedNode.getId(), new HashSet<>()); } Map> parents = new HashMap<>(); for (CallNode sharedNode : sharedNodes) { parents.put(sharedNode.getId(), new HashSet<>(getItselfAndAllParentNodes(sharedNode))); } for (Map.Entry> entry1 : parents.entrySet()) { for (Map.Entry> entry2 : parents.entrySet()) { if (entry1.getKey().equals(entry2.getKey())) { continue; } if (isSameRootParent(parents, entry1.getKey(), entry2.getKey())) { Set required = findRequiredNodesForSameRoot(parents, entry1.getKey(), entry2.getKey()); results.get(entry1.getKey()).addAll(required); } else { HashSet tmp = new HashSet<>(entry1.getValue()); tmp.retainAll(entry2.getValue()); results.get(entry1.getKey()).addAll(tmp); } } } return results; } private Set findRequiredNodesForSameRoot(Map> parents, String nodeId1, String nodeId2) { CallNode node1 = findNodeInParentPath(nodeId1, parents); CallNode node2 = findNodeInParentPath(nodeId2, parents); List parents1 = getItselfAndAllParentNodes(node1); List parents2 = getItselfAndAllParentNodes(node2); Set result = new HashSet<>(parents.get(nodeId1)); result.retainAll(parents.get(nodeId2)); int max = parents1.size() > parents2.size() ? parents2.size() : parents1.size(); if (max <= 2) { return result; } for (int i = 1; i < max; i++) { int j = parents1.size() - i - 1; int k = parents2.size() - i - 1; CallNode callNode1 = parents1.get(j); if (callNode1.equals(parents2.get(k))) { if (callNode1.equals(node1.getParent()) || callNode1 == node1 || callNode1 == node2) { } else { result.remove(callNode1); } continue; } else { break; } } return result; } private boolean isSameRootParent(Map> parents, String nodeId1, String nodeId2) { CallNode node1 = findNodeInParentPath(nodeId1, parents); CallNode node2 = findNodeInParentPath(nodeId2, parents); List parents1 = getItselfAndAllParentNodes(node1); List parents2 = getItselfAndAllParentNodes(node2); return parents1.get(parents1.size() - 1).equals(parents2.get(parents2.size() - 1)); } private CallNode findNodeInParentPath(String nodeId, Map> parents) { for (CallNode callNode : parents.get(nodeId)) { if (callNode.getId().equals(nodeId)) { return callNode; } } return null; } private List getItselfAndAllParentNodes(CallNode node) { List results = new ArrayList<>(); results.add(node); CallNode it = node; while (it.getParent() != null) { it = it.getParent(); results.add(it); } return results; } private List findBoundaryNodes(CallNode node) { List results = new ArrayList<>(); if (! node.isVisible()) { return new ArrayList<>(); } for (CallNode child : node.getChildren()) { if (! child.isVisible()) { continue; } if (!sameModule(node, child)) { results.add(node); results.add(child); } results.addAll(findBoundaryNodes(child)); } return results; } List findAllSharedNodes() { List results = new ArrayList<>(); for (CallNode node : callTree.getNodes()) { if (node.isVisible()) { results.addAll(findSharedNodes(node)); } } return results; } private List findSharedNodes(CallNode node) { List results = new ArrayList<>(); if (isSelfSharedNode(node)) { results.add(node); } else if (isLinkToOtherOwner(node)) { results.add(node); } for (CallNode child : node.getChildren()) { if (child.isVisible()) { results.addAll(findSharedNodes(child)); } } return results; } private boolean isLinkToOtherOwner(CallNode callNode) { if (callNode.isLink() && callNode.getLinkedNode() != null) { String currentOwner = nodeOwner.get(callNode.getId()); String linkOwner = nodeOwner.get(callNode.getLinkedNode().getId()); return currentOwner != null && ! currentOwner.equals(linkOwner); } return false; } private boolean isSelfSharedNode(CallNode callNode) { if (!nodeDependencies.containsKey(callNode.getId())) { return false; } NodeDependency nodeDependency = nodeDependencies.get(callNode.getId()); return nodeDependency.dependentByOwners.size() > 1 || (nodeDependency.dependentByOwners.size() == 1 && ! nodeDependency.dependentByOwners.contains(nodeOwner.get(callNode.getId()))); } public void setyStep(int yStep) { this.yStep = yStep; } public void setxStep(int xStep) { this.xStep = xStep; } private List findMethodObjects(CallGraph callGraph, Set methods) { List results = new ArrayList<>(); for (String method : methods) { String className = getClassName(method); String methodName = getMethodName(method); ClassObject aClass = callGraph.findClass(className); Objects.requireNonNull(aClass, "Failed to find class: " + className); results.addAll(aClass.findMethod(methodName)); } return results; } private String getMethodName(String classMethodName) { int i = classMethodName.lastIndexOf("."); return classMethodName.substring(i + 1); } private String getClassName(String classMethodName) { int i = classMethodName.lastIndexOf("."); return classMethodName.substring(0, i); } private String getLeafDataObject(CallNode callNode) { if (isLeafDataObject(callNode)) { return callNode.getDbOperation().getName(); } return ""; } private boolean isLeafDataObject(CallNode callNode) { return callNode.getDbOperation() != null && callNode.getDbOperation().getObjectType() == DbObjectTypeEnum.TABLE; } private GraphNode createGraphNode(CallNode callNode) { GraphNode graphNode = new GraphNode( getGraphNodeId(callNode), getGraphNodeText(callNode), getGraphNodeHint(callNode), getRW(callNode), getGroup(callNode), getModule(callNode) ); nodes.add(graphNode); return graphNode; } private String getModule(CallNode callNode) { String result = ""; if (callNode.getMethodObject() == null || callTree.getCallGraph() == null) { return result; } ClassObject aClass = callTree.getCallGraph().findClass(callNode.getMethodObject().getClassName()); if (aClass != null) { result = aClass.getModule(); } if (ignoreModules.contains(result)) { return ""; } return result; } private String getGraphNodeHint(CallNode callNode) { if (callNode.getMethodObject() != null) { String module = getModule(callNode); if (module.length() > 0) { module = "[" + module + "] "; } return module + callNode.getMethodObject().getFullName(); } else if (callNode.getDbOperation() != null){ DbOperation dbOperation = callNode.getDbOperation(); if (dbOperation.getObjectType() == DbObjectTypeEnum.TABLE) { return dbOperation.getName() + " [" + dbOperation.getOperationType().toString() + "]"; } return dbOperation.getName(); } return "Unknown"; } private String getGraphNodeText(CallNode callNode) { if (callNode.getMethodObject() != null) { String module = getModule(callNode); if (module.length() > 0) { module = "[" + module + "]\n"; } String className = callNode.getMethodObject().getClassName(); int i = className.lastIndexOf("."); if (i >= 0) { className = className.substring(i + 1); } return module + className + "." + callNode.getMethodObject().getMethodName(); } else if (callNode.getDbOperation() != null){ return callNode.getDbOperation().getName(); } return "Unknown"; } private void addGraphEdge(CallNode from, CallNode to, List callStack) { CallNode fromNode = getRealNode(from); CallNode toNode = getRealNode(to); GraphEdge edge = new GraphEdge(getGraphNodeId(fromNode), getGraphNodeId(toNode)); String hint = callStack.stream().map(e -> "
  • " + e + "
  • ").collect(Collectors.joining("", "
      ", "
    ")); edge.setHint(hint); String operation = getEdgeTableOperation(to); if (operation.contains("W")) { edge.setNormal(new EdgeStyle(new EdgeStrokeStyle("#ff0000"))); } else if (operation.contains("R")) { edge.setNormal(new EdgeStyle(new EdgeStrokeStyle("##ffa000"))); //8cc640 } if (!sameModule(fromNode, toNode)) { // edge.setNormal(new EdgeStyle(new EdgeStrokeStyle("#ffa000"))); if (edge.getNormal() == null) { edge.setNormal(new EdgeStyle(new EdgeStrokeStyle("#93caf9"))); //default blue } edge.getNormal().getStroke().setDash("10 5"); } edges.add(edge); } private boolean sameModule(CallNode fromNode, CallNode toNode) { String fromModule = getModule(fromNode); String toModule = getModule(toNode); //对于db procedure, 不需要判断module return toModule.equals("") || fromModule.equals(toModule); } private String getEdgeTableOperation(CallNode node) { return getNodeEntityTableOperation(node, new HashSet()); } private String getNodeEntityTableOperation(CallNode node, Set scannedNodes) { String result = ""; if (scannedNodes.contains(node)) { return ""; } scannedNodes.add(node); if (node.isLink() && node.getLinkedNode() != null) { return getNodeEntityTableOperation(node.getLinkedNode(), scannedNodes); } if (node.getMethodObject() != null) { MethodObject methodObject = node.getMethodObject(); if (isSpecifiedEntityClass(methodObject)) { if (isWriteOperation(methodObject)) { return "W"; } else { result = "R"; } } } else if (node.getDbOperation() != null) { if (isSpecifiedDbObject(node.getDbOperation().getName())) { if (node.getDbOperation().getOperationType() == DbOperationTypeEnum.READ_WRITE || node.getDbOperation().getOperationType() == DbOperationTypeEnum.WRITE) { return "W"; } result = "R"; } } for (CallNode child : node.getChildren()) { String opr = getNodeEntityTableOperation(child, scannedNodes); if ("R".equals(opr)) { result = "R"; } else if ("W".equals(opr)) { return "W"; } } return result; } private boolean isWriteOperation(MethodObject methodObject) { for (String methodName : Constants.READONLY_METHOD_NAME) { if (methodObject.getMethodName().startsWith(methodName)) { return false; } } return true; } private boolean isSpecifiedDbObject(String name) { return specialTableSet.isEmpty() || specialTableSet.contains(name); } private boolean isSpecifiedEntityClass(MethodObject methodObject) { if (specialEntitySet.isEmpty()) { if (specialTableSet.isEmpty()) { return methodObject.getClassName().endsWith("Entity"); } else { return false; } } for (String entity : specialEntitySet) { if (entity.indexOf(".") > 0) { if (methodObject.getClassName().equalsIgnoreCase(entity)) { return true; } } else { if (methodObject.getClassName().endsWith("." + entity)) { return true; } } } return false; } private CallNode getRealNode(CallNode node) { return node.isLink()? node.getLinkedNode(): node; } private String getGraphNodeId(CallNode callNode) { if (isLeafDataObject(callNode)) { return getLeafDataObject(callNode); } if (callNode.getId() != null && callNode.getId().length() > 0) { return callNode.getId(); } if (callNode.getMethodObject() != null) { return callNode.getMethodObject().getFullName(); } else if (callNode.getDbOperation() != null){ return callNode.getDbOperation().getName(); } return "Unknown"; } private String getRW(CallNode callNode) { return ""; } private String getGroup(CallNode callNode) { return ""; } private void info(String message) { if (this.logger != null) { this.logger.info(message); } } public void setLogger(Log logger) { this.logger = logger; } void initNodeDependencies() { for (CallNode node : callTree.getNodes()) { initNodeOwner(node, node.getId()); } for (CallNode node : callTree.getNodes()) { initNodeDependencies(node, node.getId()); } } private void initNodeOwner(CallNode node, String owner) { if (node.getId() == null || node.getId().length() == 0) { return; } if (! nodeOwner.containsKey(node.getId())) { nodeOwner.put(node.getId(), owner); for (CallNode child : node.getChildren()) { initNodeOwner(child, owner); } } } private void initNodeDependencies(CallNode node, String owner) { if (node.getId() == null || node.getId().length() == 0) { return; } if (node.isLink() && node.getLinkedNode() != null) { addDependency(node.getLinkedNode().getId(), owner); } else { for (CallNode child : node.getChildren()) { initNodeDependencies(child, owner); } } } private void addDependency(String nodeId, String otherOwner) { NodeDependency nodeDependency = nodeDependencies.get(nodeId); if (nodeDependency == null) { nodeDependency = new NodeDependency(nodeId, nodeOwner.get(nodeId)); nodeDependencies.put(nodeId, nodeDependency); } nodeDependency.addDependentBy(otherOwner); } void setCallTree(CallTree callTree) { this.callTree = callTree; } public void setNeedRandomX(boolean needRandomX) { this.needRandomX = needRandomX; } private class NodeDependency { private String nodeId; private String owner; private Set dependentByOwners = new HashSet<>(); public NodeDependency(String nodeId, String owner) { this.nodeId = nodeId; this.owner = owner; } public Set getDependentByOwners() { return dependentByOwners; } public void addDependentBy(String byWho) { dependentByOwners.add(byWho); } public String getNodeId() { return nodeId; } public String getOwner() { return owner; } } public void addSpecialEntitiesAndTables(String[] entities, String[] tables) { if (entities != null) { this.specialEntitySet.addAll(Arrays.asList(entities)); } if (tables != null) { this.specialTableSet.addAll(Arrays.asList(tables)); } } public void setIgnoreModules(String[] ignoreModules) { if (ignoreModules != null) { this.ignoreModules.addAll(Arrays.asList(ignoreModules)); } } }




    © 2015 - 2024 Weber Informatics LLC | Privacy Policy