All Downloads are FREE. Search and download functionalities are using the official Maven repository.
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.rootmethod.RootMethodFinder Maven / Gradle / Ivy
package live.document.mavenplugin.rootmethod;
import live.document.mavenplugin.common.Constants;
import live.document.plsqlscanner.DbAction;
import live.document.plsqlscanner.PlSqlExplained;
import live.document.plsqlscanner.PlSqlObject;
import live.document.scanner.CallGraph;
import live.document.scanner.CallProcedureObject;
import live.document.scanner.ClassObject;
import live.document.scanner.MethodObject;
import live.document.scanner.VisitTableObject;
import org.apache.maven.plugin.logging.Log;
import java.util.ArrayList;
import java.util.Arrays;
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.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
public class RootMethodFinder {
private CallGraph callGraph;
private PlSqlExplained plSqlExplained;
private Log logger;
private List allMethods;
private Map> calleeCallerMap = new HashMap<>();
private Map> rootAllChildrenMap = new HashMap<>();
private Set allRootMethods = new HashSet<>();
private Function isRootMethod;
private Boolean minimizeRootMethod;
private Map> rootMethodsOfEntities;
private Map> rootMethodsOfTables;
public RootMethodFinder(CallGraph callGraph, PlSqlExplained plSqlExplained) {
this.callGraph = callGraph;
this.plSqlExplained = plSqlExplained;
initCalleeCallerMap();
}
protected void findResults(Set entities, Set tables) {
Set finalResults = new HashSet<>();
//rootMethod -> JavaMethod which has entity/table operation
Map> rootMethodAndChildMethodRelation = new HashMap<>();
//entity results
rootMethodsOfEntities = new HashMap<>();
//table results
rootMethodsOfTables = new HashMap<>();
for (String entity : entities) {
List methodObjects = findAllMethodObjectsByEntity(entity);
List methodObjectOperators = getMethodObjectOperators(methodObjects);
List rootMethodOperators = getRootMethodsOperator(methodObjectOperators);
rootMethodsOfEntities.put(entity, rootMethodOperators);
for (RootMethodOperator rootMethodOperator : rootMethodOperators) {
Set childMethods = getOrInitChildMethods(rootMethodOperator, rootMethodAndChildMethodRelation);
childMethods.addAll(findMethodWithinRoot(rootMethodOperator, methodObjects));
}
}
for (String table : tables) {
List methodOperators = findAllMethodOperationsByTable(table);
List rootMethodOperators = getRootMethodsOperator(methodOperators);
rootMethodsOfTables.put(table, rootMethodOperators);
List methodObjects = methodOperators.stream().map(o -> o.methodObject).collect(Collectors.toList());
for (RootMethodOperator rootMethodOperator : rootMethodOperators) {
Set childMethods = getOrInitChildMethods(rootMethodOperator, rootMethodAndChildMethodRelation);
childMethods.addAll(findMethodWithinRoot(rootMethodOperator, methodObjects));
}
}
if (minimizeRootMethod) {
minimizeRootMethod(rootMethodAndChildMethodRelation);
removeDuplicatedRootMethods(rootMethodAndChildMethodRelation.keySet(), rootMethodsOfEntities);
removeDuplicatedRootMethods(rootMethodAndChildMethodRelation.keySet(), rootMethodsOfTables);
}
}
private Set findMethodWithinRoot(RootMethodOperator rootMethodOperator, List methodObjects) {
String rootMethodName = rootMethodOperator.getName();
if (! rootAllChildrenMap.containsKey(rootMethodName)) {
Set childrenMethods = new HashSet<>();
for (MethodObject rootMethod : allRootMethods) {
if (rootMethodName.equals(rootMethod.getFullName())) {
childrenMethods.addAll(findAllCalleeMethod(rootMethod, new HashSet<>()));
}
}
rootAllChildrenMap.put(rootMethodName, childrenMethods);
}
Set allChildMethodObjects = rootAllChildrenMap.get(rootMethodName);
Set results = new HashSet<>(methodObjects);
results.retainAll(allChildMethodObjects);
return results;
}
private void removeDuplicatedRootMethods(Set finalRootMethodNames, Map> allRootMethods) {
for (Map.Entry> entry : allRootMethods.entrySet()) {
Iterator iterator = entry.getValue().iterator();
while (iterator.hasNext()) {
RootMethodOperator next = iterator.next();
if (! finalRootMethodNames.contains(next.getName())) {
iterator.remove();
}
}
}
}
private void minimizeRootMethod(Map> rootAndChildMethodMap) {
info("Before minimize: " + rootAndChildMethodMap.size());
List>> orderedEntries = rootAndChildMethodMap.entrySet()
.stream()
.sorted((m1, m2) -> m2.getValue().size() - m1.getValue().size()) //倒序
.collect(Collectors.toList());
Set keptMethods = new HashSet<>();
for (int i = 0; i < orderedEntries.size(); i++) {
Set temp = new HashSet<>(orderedEntries.get(i).getValue());
if (temp.size() > 0) {
temp.removeAll(keptMethods);
if (temp.size() == 0) {
rootAndChildMethodMap.remove(orderedEntries.get(i).getKey());
} else {
keptMethods.addAll(orderedEntries.get(i).getValue());
}
}
}
info("After minimize: " + rootAndChildMethodMap.size());
}
private Set getOrInitChildMethods(RootMethodOperator rootMethodOperator, Map> rootMethodMap) {
Set results = rootMethodMap.get(rootMethodOperator.getName());
if (results == null) {
results = new HashSet<>();
rootMethodMap.put(rootMethodOperator.getName(), results);
}
return results;
}
public List findByEntity(String entity) {
findResults(new HashSet<>(Arrays.asList(entity)), new HashSet<>());
if (this.rootMethodsOfEntities.containsKey(entity)) {
return this.rootMethodsOfEntities.get(entity);
}
return new ArrayList<>();
}
public List findByTable(String tableName) {
findResults(new HashSet<>(), new HashSet<>(Arrays.asList(tableName)));
if (this.rootMethodsOfTables.containsKey(tableName)) {
return this.rootMethodsOfTables.get(tableName);
}
return new ArrayList<>();
}
private List getRootMethodsOperator(List methodObjectOperators) {
List results = getRootMethodOperators(methodObjectOperators);
DuplicatedRootMethodRemover remover = new DuplicatedRootMethodRemover(callGraph);
return remover.removeDuplicatedRootMethodOperator(results);
}
public void setIsRootMethod(Function isRootMethod) {
this.isRootMethod = isRootMethod;
}
private List getRootMethodOperators(List methodObjectOperators) {
Set rootMethods = findRootMethods(methodObjectOperators);
List results = combineRootMethodOperators(rootMethods);
return results;
}
private List findAllMethodOperationsByTable(String tableName) {
List results = new ArrayList<>();
for (ClassObject aClass : callGraph.getClasses()) {
for (MethodObject method : aClass.getMethods()) {
Optional tableOperation = getTableOperation(method, tableName);
if (tableOperation.isPresent()) {
results.add(new MethodObjectOperator(method, tableOperation.get()));
}
}
}
return results;
}
private Optional getTableOperation(MethodObject method, String tableName) {
Optional operation = method.calleeStream(VisitTableObject.class)
.filter(v -> v.getTableName().equalsIgnoreCase(tableName))
.map(v -> DbAction.WriteTableActions.contains(v.getOperation().getValue()) ? "W" : "R")
.findFirst();
if (operation.isPresent()) {
return operation;
}
return method.calleeStream(CallProcedureObject.class)
.map(procedureCall -> getTableOperationOfProcedure(procedureCall.getProcedure(), tableName, new HashSet<>()))
.filter(Objects::nonNull)
.findFirst();
}
private String getTableOperationOfProcedure(String procedureName, String tableName, HashSet scannedProcedure) {
scannedProcedure.add(procedureName);
PlSqlObject plSqlObject = plSqlExplained.findByName(procedureName);
if (plSqlObject == null) {
return null;
}
if (plSqlObject.getDeleteTables().contains(tableName)) {
return "W";
}
if (plSqlObject.getUpdateTables().contains(tableName)) {
return "W";
}
if (plSqlObject.getInsertTables().contains(tableName)) {
return "W";
}
boolean found = false;
for (String functionCall : plSqlObject.getFunctionCalls()) {
if (! scannedProcedure.contains(functionCall)) {
String tableOperationOfProcedure = getTableOperationOfProcedure(functionCall, tableName, scannedProcedure);
if (tableOperationOfProcedure != null) {
if (tableOperationOfProcedure.equals("W")) {
return "W";
} else {
found = true;
}
}
}
}
if (plSqlObject.getQueryTables().contains(tableName)) {
return "R";
}
if (found) {
return "R";
}
return null;
}
private Set getAllTables(PlSqlObject plSqlObject) {
Set tableNames = new HashSet<>();
tableNames.addAll(plSqlObject.getQueryTables());
tableNames.addAll(plSqlObject.getDeleteTables());
tableNames.addAll(plSqlObject.getUpdateTables());
tableNames.addAll(plSqlObject.getInsertTables());
return tableNames;
}
/**
* 查找方法的所有Callee,包含Callee的Callee等等。
* @param methodObject
* @param scannedMethods
* @return 所有Callee
*/
private List findAllCalleeMethod(MethodObject methodObject, Set scannedMethods) {
List results = new ArrayList<>();
methodObject.calleeStream(MethodObject.class)
.filter(callee -> !scannedMethods.contains(callee))
.forEach(callee -> {
scannedMethods.add(callee);
results.add(callee);
results.addAll(findAllCalleeMethod(callee, scannedMethods));
});
return results;
}
/**
* 获取方法的RW操作
* @param methodObjects
* @return
*/
private List getMethodObjectOperators(List methodObjects) {
List methodObjectOperators = methodObjects.stream()
.map(this::getMethodOperator)
.collect(Collectors.toList());
return methodObjectOperators;
}
/**
* 合并方法的RW操作
* @param rootMethods
* @return
*/
private List combineRootMethodOperators(Set rootMethods) {
Map results = new HashMap<>();
for (MethodObjectOperator mOperator : rootMethods) {
String fullName = mOperator.methodObject.getFullName();
RootMethodOperator rootMethodOperator = new RootMethodOperator(fullName, mOperator.operator);
results.put(fullName, rootMethodOperator.combine(results.get(fullName)));
}
List rootMethodOperators = new ArrayList<>(results.values());
rootMethodOperators.sort(RootMethodOperator::compareTo);
return rootMethodOperators;
}
private void info(String message) {
if (this.logger != null) {
this.logger.info(message);
}
}
/**
* 查找方法的入口方法、RW操作
* @param methodObjectOperators
* @return
*/
private Set findRootMethods(List methodObjectOperators) {
Set rootMethods = methodObjectOperators
.stream()
.flatMap(m -> findRootMethodOperator(m).stream())
.filter(m -> ! m.methodObject.getMethodName().contains("lambda$")) //root method去除lambda表达式
.collect(Collectors.toSet());
return rootMethods;
}
private List findRootMethodOperator(MethodObjectOperator m) {
List results = new ArrayList<>();
List rootMethods = findRootMethods(m.methodObject, new HashSet<>());
allRootMethods.addAll((rootMethods));
if (rootMethods.isEmpty()) {
if (isRootMethod != null && isRootMethod.apply(m.methodObject)) {
results.add(m);
}
}
results.addAll(rootMethods.stream()
.map(r -> new MethodObjectOperator(r, m.operator))
.collect(Collectors.toList()));
return results;
}
private List findRootMethods(MethodObject methodObject, HashSet scannedMethodObjects) {
scannedMethodObjects.add(methodObject);
List results = new ArrayList<>();
List parentMethods = calleeCallerMap.get(methodObject);
if (parentMethods == null) {
return results;
}
for (MethodObject parentMethod : parentMethods) {
if (isRootMethod != null && isRootMethod.apply(parentMethod)) {
results.add(parentMethod);
} else {
if (! scannedMethodObjects.contains(parentMethod)) {
results.addAll(findRootMethods(parentMethod, scannedMethodObjects));
}
}
}
return results;
}
private void initCalleeCallerMap() {
calleeCallerMap = new HashMap<>();
for (MethodObject method : getAllMethods()) {
method.calleeStream(MethodObject.class)
.forEach(callee -> {
List methodObjects = calleeCallerMap.get(callee);
if (methodObjects == null) {
methodObjects = new ArrayList<>();
}
methodObjects.add(method);
calleeCallerMap.put(callee, methodObjects);
});
}
}
private List getAllMethods() {
if (allMethods == null) {
allMethods = callGraph.getClasses()
.stream().flatMap(c -> c.getMethods().stream())
.collect(Collectors.toList());
}
return allMethods;
}
/**
* 根据Entity查找所有Jvm方法
* @param entity
* @return
*/
private List findAllMethodObjectsByEntity(String entity) {
List results = new ArrayList<>();
for (ClassObject aClass : callGraph.getClasses()) {
for (MethodObject method : aClass.getMethods()) {
if (hasEntityOperation(method, entity)) {
results.add(method);
}
}
}
return results;
}
private MethodObjectOperator getMethodOperator(MethodObject methodObject) {
String operator = "R";
boolean hasWriteOperation = methodObject.calleeStream(MethodObject.class)
.filter(c -> isEntityWriteOperation(c.getMethodName()))
.findAny()
.isPresent();
if (hasWriteOperation) {
operator = "W";
}
return new MethodObjectOperator(methodObject, operator);
}
private boolean hasEntityOperation(MethodObject methodObject, String entity) {
return methodObject.calleeStream(MethodObject.class)
.filter(callee -> callee.getClassName().equalsIgnoreCase(entity))
.findFirst()
.isPresent();
}
private boolean isEntityWriteOperation(String methodName) {
for (String readonlyName : Constants.READONLY_METHOD_NAME) {
if (methodName.startsWith(readonlyName)) {
return false;
}
}
return true;
}
public void setLogger(Log logger) {
this.logger = logger;
}
public void setMinimizeRootMethod(Boolean minimizeRootMethod) {
this.minimizeRootMethod = minimizeRootMethod;
}
class MethodObjectOperator {
MethodObject methodObject;
String operator;
public MethodObjectOperator(MethodObject methodObject, String operator) {
this.methodObject = methodObject;
this.operator = operator;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MethodObjectOperator that = (MethodObjectOperator) o;
if (!methodObject.equals(that.methodObject)) return false;
return operator.equals(that.operator);
}
@Override
public int hashCode() {
int result = methodObject.hashCode();
result = 31 * result + operator.hashCode();
return result;
}
}
}