package org.spotter.ext.detection.edc.utils; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TreeSet; /** * This class represents a special set for storing and managing * {@link MethodCall MethodCalls}. * * @author Henning Schulz * @see MethodCall * */ public class MethodCallSet { private final Map<Long, MethodCallSetPerTid> methodCallsPerThreadId = new HashMap<>(); private final long min; private final long max; private final long avgNumOfCallsPerThread; /** * Constructor. * * @param min * Minimum of used timestamps * @param max * Maximum of used timestamps * @param avgNumOfCallsPerThread * Estimated number of calls per thread id */ public MethodCallSet(long min, long max, long avgNumOfCallsPerThread) { super(); this.min = min; this.max = max; this.avgNumOfCallsPerThread = avgNumOfCallsPerThread; } /** * Adds a new method call recursively. * * @param call * {@link MethodCall} to be added */ public void addCall(MethodCall call) { MethodCallSetPerTid callsForThisId = methodCallsPerThreadId.get(call.getThreadId()); if (callsForThisId == null) { callsForThisId = new MethodCallSetPerTid(min, max, avgNumOfCallsPerThread, call.getThreadId()); methodCallsPerThreadId.put(call.getThreadId(), callsForThisId); } callsForThisId.insert(call); } public boolean addCallIfNested(MethodCall call) { MethodCallSetPerTid callsForThisId = methodCallsPerThreadId.get(call.getThreadId()); if (callsForThisId == null) { return false; } return callsForThisId.insertIfNested(call); } /** * Returns all stored method calls. * * @param callSet * All stored method calls */ public void addAllCalls(Collection<? extends MethodCall> callSet) { for (MethodCall call : callSet) { addCall(call); } } /** * Returns all stored method calls. * * @return All stored method calls */ public Set<MethodCall> getMethodCalls() { Set<MethodCall> callSet = new HashSet<>(); for (long tid : methodCallsPerThreadId.keySet()) { callSet.addAll(methodCallsPerThreadId.get(tid).getAllCalls()); } return callSet; } /** * Returns all unique method names in this {@code MethodCallSet}. * * @return All unique method names */ public Set<String> getUniqueMethods() { Set<String> uniqueMethods = new TreeSet<>(); for (MethodCall call : getMethodCalls()) { uniqueMethods.add(call.getOperation()); } return uniqueMethods; } /** * Returns all unique method names of the given layer. * * @return All unique method names of the given layer */ public Set<String> getUniqueMethodsOfLayer(int layer) { return getSubsetAtLayer(layer).getUniqueMethods(); } /** * Returns a new {@code MethodCallSet} rooting at the given layer. * * @param layer * Layer of which the new {@code MethodCallSet} is to be created * @return New {@code MethodCallSet} rooting at the given layer */ public MethodCallSet getSubsetAtLayer(int layer) { if (layer <= 0) { return this; } Set<MethodCall> callsOfLayer = getMethodCalls(); for (int i = 0; i < layer; i++) { Set<MethodCall> tmp = new HashSet<>(); for (MethodCall subcall : callsOfLayer) { tmp.addAll(subcall.getCalledOperations()); } callsOfLayer = tmp; } MethodCallSet subset = new MethodCallSet(min, max, avgNumOfCallsPerThread); subset.addAllCalls(callsOfLayer); return subset; } public MethodCallSet getSubsetOfLowestLayer() { MethodCallSet finalSet = new MethodCallSet(min, max, avgNumOfCallsPerThread); for (MethodCall call : getMethodCalls()) { finalSet.addAllCalls(call.getFinalCalls()); } return finalSet; } /** * Generates a new {@code MethodCallSet} with all calls of the given layer * but without recursive calls. * * @param layer * Layer of which the calls are to be taken * @return New {@code MethodCallSet} with all calls of the given layer but * without recursive calls * @see MethodCallSet#getIsolatedCallsOfLayer(int) */ public MethodCallSet getFlatSubsetAtLayer(int layer) { MethodCallSet setAtLayer = new MethodCallSet(min, max, avgNumOfCallsPerThread); setAtLayer.addAllCalls(getIsolatedCallsOfLayer(layer)); return setAtLayer; } /** * Returns all method calls of the given operation which are in the given * layer. * * @param methodName * Name of the operation * @param layer * Layer at which the call is to be searched * @return All method calls of the given operation in the given layer */ public Set<MethodCall> getCallsOfMethodAtLayer(String methodName, int layer) { MethodCallSet setAtLayer = getSubsetAtLayer(layer); Set<MethodCall> callsOfMethod = new HashSet<>(); for (MethodCall call : setAtLayer.getMethodCalls()) { if (call.getOperation().equals(methodName)) { callsOfMethod.add(call); } } return callsOfMethod; } /** * Returns all calls in the given layer but removes all recursive calls. * * @param layer * Layer of which the calls are to be returned * @return All calls in the given layer without recursive calls */ public Set<MethodCall> getIsolatedCallsOfLayer(int layer) { Set<MethodCall> isolatedCalls = new HashSet<>(); for (MethodCall call : getSubsetAtLayer(layer).getMethodCalls()) { isolatedCalls.add(new MethodCall(call.getOperation(), call.getEnterTime(), call.getExitTime(), call .getThreadId())); } return isolatedCalls; } /** * Removes a call. * * @param call * Call to be removed */ public void removeCall(MethodCall call) { MethodCallSetPerTid callsPerTid = methodCallsPerThreadId.get(call.getThreadId()); if (callsPerTid == null) { return; } else { callsPerTid.remove(call); } } /** * Removes all given calls. * * @param calls * Calls to be removed */ public void removeAllCalls(Collection<? extends MethodCall> calls) { for (MethodCall call : calls) { removeCall(call); } } public void removeAllCallsWithName(String name) { for (Entry<Long, MethodCallSetPerTid> callsPerTidEntry : methodCallsPerThreadId.entrySet()) { callsPerTidEntry.getValue().removeAllCallsWithName(name); } } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("["); boolean first = true; for (MethodCall call : getMethodCalls()) { if (first) { first = false; } else { builder.append(", "); } builder.append(call); } builder.append("]"); return builder.toString(); } }