Newer
Older
MagnetRON / src / org / ntlab / deltaViewer / CollaborationAliasCollector.java
package org.ntlab.deltaViewer;

import java.util.AbstractMap;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import org.ntlab.deltaExtractor.Alias;
import org.ntlab.deltaExtractor.IAliasCollector;
import org.ntlab.featureExtractor.Extract;
import org.ntlab.trace.MethodExecution;
import org.ntlab.trace.MethodInvocation;
import org.ntlab.trace.Statement;
import org.ntlab.trace.TracePoint;

/**
 * CollaborationAliasCollector is IAliasCollector implementation class to merge aliasList in time stamp order.
 * 
 * @author Nitta Lab.
 */
public class CollaborationAliasCollector implements IAliasCollector {
	// Execution order.
	private List<Alias> aliasList = new ArrayList<>();

	public CollaborationAliasCollector(IAliasCollector dac) {
		aliasList.addAll(dac.getAliasList());
		aliasList = sortAliasListByTimeStamp(aliasList);
	}
	
	/*
	 * Don't write anything here.
	 */
	@Override
	public void addAlias(Alias alias) {
	}

	@Override
	public List<Alias> getAliasList() {
		return aliasList;
	}

	/**
	 * Merge other into aliasList(this) in time stamp order.
	 * @param other IAliasCollector to be merged into the aliasList.
	 * @param extract 
	 * @param newToOldMethodExecMap 
	 */
	public void merge(IAliasCollector other, Extract extract, Map<MethodExecution, Set<MethodExecution>> newToOldMethodExecMap) {
		List<Alias> otherAliasList = other.getAliasList();
		otherAliasList = sortAliasListByTimeStamp(otherAliasList);
		
		int otherIdx = 0; // Index in otherAliasList
		int thisIdx = 0; // Index in thisAliasList
		int thisOrgIdx = 0; // Index in the original thisAliasList
		MethodExecution newCallee = null;
		while(otherIdx < otherAliasList.size()) {
			Alias otherAlias = otherAliasList.get(otherIdx);
			if (newCallee != null && newToOldMethodExecMap.get(newCallee).contains(otherAlias.getMethodExecution())) {
				Statement st = otherAlias.getOccurrencePoint().getStatement();
				if (st instanceof MethodInvocation) {
					// Caution!!: The internal structure of the trace is modified.
					MethodExecution calleeCallee = ((MethodInvocation) st).getCalledMethodExecution();
					calleeCallee.setCaller(newCallee, calleeCallee.getCallerStatementExecution());					
				}
				DummyTracePoint dummyTp = new DummyTracePoint(otherAlias.getOccurrencePoint(), newCallee);
				Alias newAlias = new Alias(otherAlias.getAliasType(), otherAlias.getIndex(), otherAlias.getObjectId(), dummyTp);
				otherAliasList.set(otherIdx, newAlias);				
			}
			if (thisIdx >= aliasList.size()) {
				if (extract != null && extract.isToConnect() && otherIdx == 0) {
					Alias thisPrevAlias = aliasList.get(aliasList.size() - 1);
					if (!otherAlias.getMethodExecution().isStatic() && otherAlias.getMethodExecution().getCallerMethodExecution() != thisPrevAlias.getMethodExecution()) {
						// Add a dummy alias to connect disjunct call hierarchies. (thisPrevAlias -> otherAlias)
						MethodExecution caller = thisPrevAlias.getMethodExecution();
						MethodExecution oldCallee = otherAlias.getMethodExecution();
						MethodExecution callee = null;
						if (oldCallee instanceof DummyMethodExecution) {
							callee = oldCallee;
						} else {
							callee = getNewMethodExec(newToOldMethodExecMap, oldCallee);
							if (callee == null) {
								callee = new DummyMethodExecution(oldCallee);
								Set<MethodExecution> oldExecs =  new HashSet<>();
								oldExecs.add(oldCallee);
								newToOldMethodExecMap.put(callee, oldExecs);
								newCallee = callee;
							}
						}
						callee.setCaller(caller, caller.getStatements().indexOf(thisPrevAlias.getOccurrencePoint().getStatement()));
						DummyMethodInvocation dummyInv = new DummyMethodInvocation(callee, caller.getThisClassName(), caller.getThisObjId(), 0, thisPrevAlias.getOccurrencePoint().getStatement().getThreadNo());
						DummyTracePoint dummyTp = new DummyTracePoint(caller, dummyInv);
						aliasList.add(new Alias(Alias.AliasType.RECEIVER, 0, callee.getThisObjId(), dummyTp));
						thisIdx++;
					}
				}
				aliasList.add(otherAlias);
				otherIdx++; thisIdx++;
				continue;
			}
			
			Alias thisAlias = aliasList.get(thisIdx);
			Alias newThisAlias = getUpdatedAlias(thisAlias, newToOldMethodExecMap);
			if (newThisAlias != null) {
				aliasList.set(thisIdx, newThisAlias);
				thisAlias = newThisAlias;
			}
			if (otherAlias.equals(thisAlias)) {
				otherIdx++; thisIdx++; thisOrgIdx++;
			} else {
				long otherAliasTs = otherAlias.getTimeStamp();
				long thisAliasTs = thisAlias.getTimeStamp();
				
				if (otherAliasTs < thisAliasTs) {
					if (extract != null && extract.isToConnect() && otherIdx == 0 && thisIdx > 0) {
						Alias thisPrevAlias = aliasList.get(thisIdx - 1);
						if (!otherAlias.getMethodExecution().isStatic() && otherAlias.getMethodExecution().getCallerMethodExecution() != thisPrevAlias.getMethodExecution()) {
							// Add a dummy alias to connect disjunct call hierarchies. (thisPrevAlias -> otherAlias)
							MethodExecution caller = thisPrevAlias.getMethodExecution();
							MethodExecution oldCallee = otherAlias.getMethodExecution();
							MethodExecution callee = null;
							if (oldCallee instanceof DummyMethodExecution) {
								callee = oldCallee;
							} else {
								callee = getNewMethodExec(newToOldMethodExecMap, oldCallee);
								if (callee == null) {
									callee = new DummyMethodExecution(oldCallee);
									Set<MethodExecution> oldExecs =  new HashSet<>();
									oldExecs.add(oldCallee);
									newToOldMethodExecMap.put(callee, oldExecs);
									newCallee = callee;
								}
							}
							callee.setCaller(caller, caller.getStatements().indexOf(thisPrevAlias.getOccurrencePoint().getStatement()));
							DummyMethodInvocation dummyInv = new DummyMethodInvocation(callee, caller.getThisClassName(), caller.getThisObjId(), 0, thisPrevAlias.getOccurrencePoint().getStatement().getThreadNo());
							DummyTracePoint dummyTp = new DummyTracePoint(caller, dummyInv);
							aliasList.add(new Alias(Alias.AliasType.RECEIVER, 0, callee.getThisObjId(), dummyTp));
							thisIdx++;
						}
					}
					aliasList.add(thisIdx, otherAlias);
					otherIdx++; thisIdx++;
				} else if (otherAliasTs > thisAliasTs) {
					if (extract != null && extract.isToConnect() && thisOrgIdx == 0 && otherIdx > 0) {
						Alias otherPrevAlias = otherAliasList.get(otherIdx - 1);
						if (!thisAlias.getMethodExecution().isStatic() && thisAlias.getMethodExecution().getCallerMethodExecution() != otherPrevAlias.getMethodExecution()) {
							// Add a dummy alias to connect disjunct call hierarchies. (otherPrevAlias -> thisAlias)
							MethodExecution caller = otherPrevAlias.getMethodExecution();
							MethodExecution oldCallee = thisAlias.getMethodExecution();
							MethodExecution callee = null;
							if (oldCallee instanceof DummyMethodExecution) {
								callee = oldCallee;
							} else {
								callee = getNewMethodExec(newToOldMethodExecMap, oldCallee);
								if (callee == null) {
									callee = new DummyMethodExecution(oldCallee);
									Set<MethodExecution> oldExecs =  new HashSet<>();
									oldExecs.add(oldCallee);
									newToOldMethodExecMap.put(callee, oldExecs);
									newCallee = callee;
								}
							}
							callee.setCaller(caller, caller.getStatements().indexOf(otherPrevAlias.getOccurrencePoint().getStatement()));
							DummyMethodInvocation dummyInv = new DummyMethodInvocation(callee, caller.getThisClassName(), caller.getThisObjId(), 0, otherPrevAlias.getOccurrencePoint().getStatement().getThreadNo());
							DummyTracePoint dummyTp = new DummyTracePoint(caller, dummyInv);
							aliasList.add(new Alias(Alias.AliasType.RECEIVER, 0, callee.getThisObjId(), dummyTp));
							thisIdx++;
						}
					}
					thisIdx++; thisOrgIdx++;
				} else {
					if (aliasList.contains(otherAlias)) {
						otherIdx++;
					} else {
						// BUGがあるかもしれません(ACTUAL_ARGUMENTとRECEIVERの出現順)
						aliasList.add(thisIdx, otherAlias);
						otherIdx++; thisIdx++;
					}
				}
			}
		}
		if (extract != null && extract.isToConnect() && thisOrgIdx == 0 && otherIdx > 0 && thisIdx < aliasList.size()) {
			Alias thisAlias = aliasList.get(thisIdx);
			Alias otherPrevAlias = otherAliasList.get(otherIdx - 1);
			if (!thisAlias.getMethodExecution().isStatic() && thisAlias.getMethodExecution().getCallerMethodExecution() != otherPrevAlias.getMethodExecution()) {
				// Add a dummy alias to connect disjunct call hierarchies. (otherPrevAlias -> thisAlias)
				MethodExecution caller = otherPrevAlias.getMethodExecution();
				MethodExecution oldCallee = thisAlias.getMethodExecution();
				MethodExecution callee = null;
				if (oldCallee instanceof DummyMethodExecution) {
					callee = oldCallee;
				} else {
					callee = getNewMethodExec(newToOldMethodExecMap, oldCallee);
					if (callee == null) {
						callee = new DummyMethodExecution(oldCallee);
						Set<MethodExecution> oldExecs =  new HashSet<>();
						oldExecs.add(oldCallee);
						newToOldMethodExecMap.put(callee, oldExecs);
						newCallee = callee;
					}
				}
				callee.setCaller(caller, caller.getStatements().indexOf(otherPrevAlias.getOccurrencePoint().getStatement()));
				DummyMethodInvocation dummyInv = new DummyMethodInvocation(callee, caller.getThisClassName(), caller.getThisObjId(), 0, otherPrevAlias.getOccurrencePoint().getStatement().getThreadNo());
				DummyTracePoint dummyTp = new DummyTracePoint(caller, dummyInv);
				aliasList.add(new Alias(Alias.AliasType.RECEIVER, 0, callee.getThisObjId(), dummyTp));
			}
		}
	}
	
	private Alias getUpdatedAlias(Alias alias, Map<MethodExecution, Set<MethodExecution>> newToOldMethodExecMap) {
		Alias newAlias = null;
		MethodExecution newExec = getNewMethodExec(newToOldMethodExecMap, alias.getMethodExecution());
		Statement st = alias.getOccurrencePoint().getStatement();
		DummyMethodInvocation dummyInvocation = null;
		if (st instanceof MethodInvocation) {
			MethodInvocation mi = ((MethodInvocation) st);
			MethodExecution calledExec = mi.getCalledMethodExecution();
			MethodExecution newCalledExec = getNewMethodExec(newToOldMethodExecMap, calledExec);
			if (newCalledExec != null) {
				dummyInvocation = new DummyMethodInvocation(newCalledExec, mi.getThisClassName(), mi.getThisObjId(), 0, mi.getThreadNo());
			}
		}
		if (newExec != null || dummyInvocation != null) {				
			TracePoint dummyTp = null;
			if (newExec == null) {
				// Only the called method is to be replaced.
				dummyTp = new DummyTracePoint(alias.getMethodExecution(), dummyInvocation);					
			} else {
				if (dummyInvocation == null) {
					// Only this method is to be replaced.
					Statement originalSt = alias.getOccurrencePoint().getStatement();
					dummyTp = new DummyTracePoint(newExec, originalSt);						
				} else {
					// Both the calling and the called methods are to be replaced.
					dummyTp = new DummyTracePoint(newExec, dummyInvocation);
				}
			}
			newAlias = new Alias(alias.getAliasType(), alias.getIndex(), alias.getObjectId(), dummyTp);
		}
		return newAlias;
	}
	
	/**
	 * Sort aliasList in time stamp order.
	 * @param aliasList AliasList to sort.
	 * @return Sorted AliasList.
	 */
	private List<Alias> sortAliasListByTimeStamp(List<Alias> aliasList) {
		List<Alias> cloneAliasList = new ArrayList<>(aliasList);
		List<Alias> sortedAliasList = cloneAliasList.stream().sorted(new Comparator<Alias>() {
	                @Override
	                public int compare(Alias alias1, Alias alias2) {
	                	if (alias1.getTimeStamp() > alias2.getTimeStamp()) return 1;
	                	else if (alias1.getTimeStamp() < alias2.getTimeStamp()) return -1;
	                	return 0;
	                }
	            }
	        ).collect(Collectors.toList());
		return sortedAliasList;
	}
	
	private MethodExecution getNewMethodExec(Map<MethodExecution, Set<MethodExecution>> newToOldMethodExecMap, MethodExecution oldExec) {
		for (MethodExecution newExec: newToOldMethodExecMap.keySet()) {
			if (newToOldMethodExecMap.get(newExec).contains(oldExec)) return newExec;
		}
		return null;
	}
}