/**
* This file is licensed under the University of Illinois/NCSA Open Source License. See LICENSE.TXT for details.
*/
package edu.illinois.codingspectator.logstocsv;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import edu.illinois.codingspectator.refactorings.parser.RefactoringLog;
/**
*
* This class matches up the performed refactorings recorded by CodingSpectator and CodingTracker to
* find the inconsistencies.
*
* @author Mohsen Vakilian
*
*/
public class PerformedRefactoringMatcher {
private Collection<Event> events;
private String csvFileName;
public PerformedRefactoringMatcher(Collection<Event> events, String csvFileName) {
this.events= events;
this.csvFileName= csvFileName;
}
public void reportMatchedPerformedRefactorings() throws IOException {
Collection<MatchedPerformedRefactorings> matchedPerformedRefactorings= matchPerformedRefactorings();
new CodingSpectatorCSVWriter(csvFileName).writeToCSV(matchedPerformedRefactorings);
}
private Collection<MatchedPerformedRefactorings> matchPerformedRefactorings() {
ArrayList<Event> sortedCodingTrackerPerformedRefactorings= sortedByTimestamp(getCodingTrackerPerformedRefactorings());
ArrayList<Event> sortedCodingSpectatorPerformedRefactorings= sortedByTimestamp(getCodingSpectatorPerformedRefactorings());
ArrayList<Event> remainingSortedCodingSpectatorPerformedRefactorings= new ArrayList<Event>();
Collection<MatchedPerformedRefactorings> matchedPerformedRefactorings= new HashSet<MatchedPerformedRefactorings>();
for (Event event : sortedCodingSpectatorPerformedRefactorings) {
RefactoringEvent csEvent= (RefactoringEvent)event;
int index= findClosestMatchingEvent(sortedCodingTrackerPerformedRefactorings, event);
if (index >= 0) {
matchedPerformedRefactorings.add(MatchedPerformedRefactorings.createMatchedPerformedRefactorings(csEvent, sortedCodingTrackerPerformedRefactorings.get(index).getTimestamp()));
sortedCodingTrackerPerformedRefactorings.remove(index);
} else {
matchedPerformedRefactorings.add(MatchedPerformedRefactorings.createMatchedPerformedRefactorings(csEvent, -1));
remainingSortedCodingSpectatorPerformedRefactorings.add(csEvent);
}
}
for (Event event : sortedCodingTrackerPerformedRefactorings) {
UserOperationEvent ctEvent= (UserOperationEvent)event;
int index= findClosestMatchingEvent(remainingSortedCodingSpectatorPerformedRefactorings, event);
if (index >= 0) {
matchedPerformedRefactorings.add(MatchedPerformedRefactorings
.createMatchedPerformedRefactorings(ctEvent, remainingSortedCodingSpectatorPerformedRefactorings.get(index).getTimestamp()));
remainingSortedCodingSpectatorPerformedRefactorings.remove(index);
} else {
matchedPerformedRefactorings.add(MatchedPerformedRefactorings.createMatchedPerformedRefactorings(ctEvent, -1));
}
}
return sorted(matchedPerformedRefactorings);
}
private int findClosestMatchingEvent(ArrayList<Event> sortedEvents, Event event) {
final long MAX_TIMESTAMP_DIFFERENCE= 5 * 60 * 1000; // 5 minutes in milliseconds
long timestampDifference= 500;
int index= -1;
do {
index= Collections.binarySearch(sortedEvents, event, getEventTimestampComparatorForFinding(timestampDifference));
timestampDifference+= 500;
} while (index < 0 && timestampDifference < MAX_TIMESTAMP_DIFFERENCE);
return index;
}
private ArrayList<MatchedPerformedRefactorings> sorted(Collection<MatchedPerformedRefactorings> matched) {
MatchedPerformedRefactorings[] matchedArray= matched.toArray(new MatchedPerformedRefactorings[] {});
Arrays.sort(matchedArray);
return new ArrayList<MatchedPerformedRefactorings>(Arrays.asList(matchedArray));
}
private boolean isCodingSpectatorPerformedRefactoring(Event event) {
if (event == null) {
return false;
} else if (event instanceof RefactoringEvent) {
return RefactoringLog.LogType.PERFORMED == ((RefactoringEvent)event).getRefactoringKind();
} else {
return false;
}
}
private boolean isCodingTrackerPerformedRefactoring(Event event) {
if (event == null) {
return false;
} else if (event instanceof UserOperationEvent) {
return ((UserOperationEvent)event).isStartedPerformedRefactoringOperation();
} else {
return false;
}
}
private Collection<Event> getCodingTrackerPerformedRefactorings() {
Collection<Event> collectedEvents= new ArrayList<Event>();
for (Event event : events) {
if (isCodingTrackerPerformedRefactoring(event)) {
collectedEvents.add(event);
}
}
return collectedEvents;
}
private Collection<Event> getCodingSpectatorPerformedRefactorings() {
Collection<Event> collectedEvents= new ArrayList<Event>();
for (Event event : events) {
if (isCodingSpectatorPerformedRefactoring(event)) {
collectedEvents.add(event);
}
}
return collectedEvents;
}
private ArrayList<Event> sortedByTimestamp(Collection<Event> events) {
Event[] eventsArray= events.toArray(new Event[] {});
Arrays.sort(eventsArray, getEventTimestampComparatorForSorting());
return new ArrayList<Event>(Arrays.asList(eventsArray));
}
private Comparator<Event> getEventTimestampComparatorForSorting() {
return new Comparator<Event>() {
@Override
public int compare(Event e1, Event e2) {
return Long.signum(e1.getTimestamp() - e2.getTimestamp());
}
};
}
private Comparator<Event> getEventTimestampComparatorForFinding(final long maxTimestampDifference) {
return new Comparator<Event>() {
@Override
public int compare(Event e1, Event e2) {
if (Math.abs(e1.getTimestamp() - e2.getTimestamp()) < maxTimestampDifference && Utils.toJavaRefactoringID(e1.toMap().get("id")).equals(Utils.toJavaRefactoringID(e2.toMap().get("id")))) {
return 0;
} else {
return Long.signum(e1.getTimestamp() - e2.getTimestamp());
}
}
};
}
}