Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
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));
}
}
}