/** * Copyright 2014 SAP AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.spotter.ext.detection.edc.utils; import java.util.HashSet; import java.util.Set; /** * This class represents an instantiation of a method which may call another * method. * * @author Henning Schulz * @see MethodCallSet * */ public class MethodCall { private String operation; private Set<MethodCall> calledOperations; private long enterTime; private long exitTime; private long threadId; /** * Constructor. * * @param operation * Method name * @param enterTime * Enter time * @param exitTime * Exit time * @param threadId * Thread ID */ public MethodCall(String operation, long enterTime, long exitTime, long threadId) { super(); this.operation = operation; this.enterTime = enterTime; this.exitTime = exitTime; this.threadId = threadId; } /** * @return the operation */ public String getOperation() { return operation; } /** * @param operation * the operation to set */ public void setOperation(String operation) { this.operation = operation; } /** * @return the enterTime */ public long getEnterTime() { return enterTime; } /** * @param enterTime * the enterTime to set */ public void setEnterTime(long enterTime) { this.enterTime = enterTime; } /** * @return the exitTime */ public long getExitTime() { return exitTime; } /** * @param exitTime * the exitTime to set */ public void setExitTime(long exitTime) { this.exitTime = exitTime; } /** * @return the threadId */ public long getThreadId() { return threadId; } /** * @param threadId * the threadId to set */ public void setThreadId(long threadId) { this.threadId = threadId; } /** * @return the calledOperations */ public Set<MethodCall> getCalledOperations() { if (calledOperations == null) { calledOperations = new HashSet<>(); } return calledOperations; } /** * Returns the response time. * * @return the response time */ public double getResponseTime() { return getExitTime() - getEnterTime(); } public Set<MethodCall> getFinalCalls() { Set<MethodCall> finalCalls = new HashSet<>(); Set<MethodCall> nonFinalCalls = new HashSet<>(); nonFinalCalls.add(this); while (!nonFinalCalls.isEmpty()) { Set<MethodCall> tmp = new HashSet<>(); for (MethodCall call : nonFinalCalls) { if (call.getCalledOperations() == null || call.getCalledOperations().isEmpty()) { finalCalls.add(call); } else { tmp.addAll(call.getCalledOperations()); } } nonFinalCalls = tmp; } return finalCalls; } /** * Returns iff the given call is a nested call of this. * * @param call * Method call to be tested * @return Iff the given call is a nested call */ public boolean isParentOf(MethodCall call) { if (this.getEnterTime() == call.getEnterTime() && call.getEnterTime() == call.getExitTime()) { return false; } return this.getThreadId() == call.getThreadId() && this.getEnterTime() <= call.getEnterTime() && this.getExitTime() >= call.getExitTime(); } /** * Adds a call as nested method call. * * @param operation * Method name * @param enterTime * Enter time * @param exitTime * Exit time * @param threadId * Thread ID * @return Iff the call could be added as subcall */ public boolean addCall(String operation, long enterTime, long exitTime, long threadId) { MethodCall call = new MethodCall(operation, enterTime, exitTime, threadId); return addCall(call); } /** * Adds a call as nested method call. * * @param newCall * Method call to be added * @return Iff the call could be added as subcall */ public boolean addCall(MethodCall newCall) { if (this.equals(newCall)) { return true; } if (this.isParentOf(newCall)) { if (this.calledOperations == null) { this.calledOperations = new HashSet<>(); } boolean addedInChild = false; for (MethodCall childCall : getCalledOperations()) { addedInChild = childCall.addCall(newCall); if (addedInChild) { break; } } if (!addedInChild) { Set<MethodCall> toRemove = new HashSet<>(); for (MethodCall childCall : getCalledOperations()) { if (newCall.addCall(childCall)) { toRemove.add(childCall); } } getCalledOperations().removeAll(toRemove); this.calledOperations.add(newCall); } return true; } else { return false; } } public boolean removeCall(MethodCall call) { if (this.equals(call)) { return false; } if (this.isParentOf(call)) { for (MethodCall subCall : getCalledOperations()) { if (subCall.equals(call)) { calledOperations.remove(call); return true; } } for (MethodCall subCall : getCalledOperations()) { if (subCall.isParentOf(call)) { return subCall.removeCall(call); } } } return false; } public void removeNestedCallsWithName(String name) { Set<MethodCall> toRemove = new HashSet<>(); for (MethodCall nestedCall : getCalledOperations()) { if (nestedCall.getOperation().equals(name)) { toRemove.add(nestedCall); } else { nestedCall.removeNestedCallsWithName(name); } } getCalledOperations().removeAll(toRemove); } @Override public boolean equals(Object obj) { if (obj == null || !obj.getClass().equals(this.getClass())) { return false; } MethodCall other = (MethodCall) obj; return this.getOperation().equals(other.getOperation()) && this.getEnterTime() == other.getEnterTime() && this.getExitTime() == other.getExitTime() && this.getThreadId() == other.getThreadId(); } @Override public int hashCode() { int hashCode = 11; int multi = 29; hashCode = hashCode * multi + getOperation().hashCode(); hashCode = hashCode * multi + (int) (getEnterTime() & 0xFFFFFFFF); hashCode = hashCode * multi + (int) (getExitTime() & 0xFFFFFFFF); hashCode = hashCode * multi + (int) (getThreadId() & 0xFFFFFFFF); return hashCode; } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append(operation); builder.append("[tid "); builder.append(threadId); builder.append("]: "); builder.append(enterTime); builder.append("-"); builder.append(exitTime); builder.append(" => {"); boolean first = true; for (MethodCall call : getCalledOperations()) { if (first) { first = false; } else { builder.append(", "); } builder.append(call); } builder.append("}"); return builder.toString(); } }