package com.google.jstestdriver.idea.rt.coverage; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.jstestdriver.idea.rt.util.PathConverter; import org.jetbrains.annotations.NotNull; import java.io.File; import java.util.*; /** * @author Sergey Simonchik */ public class CoverageReport { private final Map<String, List<LineHits>> myInfo = Maps.newHashMap(); @NotNull public Map<String, List<LineHits>> getInfo() { return myInfo; } public void mergeReport(@NotNull CoverageReport report) { for (Map.Entry<String, List<LineHits>> entry : report.getInfo().entrySet()) { mergeFileReport(entry.getKey(), entry.getValue()); } } public void mergeFileReport(@NotNull String filePath, @NotNull List<LineHits> report) { String normalizedFilePath = PathConverter.getNormalizedPath(new File(filePath)); normalizeLineHitsList(report); List<LineHits> old = myInfo.get(normalizedFilePath); if (old != null) { doMerge(old, report); } else { myInfo.put(normalizedFilePath, report); } } public void clearReportByFilePath(@NotNull String filePath) { myInfo.remove(filePath); } private static List<LineHits> doMerge(@NotNull List<LineHits> aList, @NotNull List<LineHits> bList) { PeekingIterator<LineHits> ai = new PeekingIterator<>(aList.iterator()); PeekingIterator<LineHits> bi = new PeekingIterator<>(bList.iterator()); List<LineHits> out = Lists.newArrayList(); while (ai.hasNext() && bi.hasNext()) { final LineHits x; LineHits a = ai.peek(); LineHits b = bi.peek(); if (a.getLineNumber() < b.getLineNumber()) { x = ai.next(); } else if (a.getLineNumber() > b.getLineNumber()) { x = bi.next(); } else { a.addHits(b.getHits()); x = a; ai.next(); bi.next(); } out.add(x); } addRestItems(out, ai); addRestItems(out, bi); return out; } private static <T> void addRestItems(@NotNull List<T> out, Iterator<T> iterator) { while (iterator.hasNext()) { out.add(iterator.next()); } } private static void normalizeLineHitsList(@NotNull List<LineHits> lineHitsList) { makeSortedByLineNumber(lineHitsList); makeUniqueByLineNumber(lineHitsList); } private static void makeSortedByLineNumber(@NotNull List<LineHits> report) { LineHits prev = null; for (LineHits cur : report) { if (prev != null && prev.getLineNumber() > cur.getLineNumber()) { Collections.sort(report); return; } prev = cur; } } private static void makeUniqueByLineNumber(@NotNull List<LineHits> report) { boolean unique = checkForLineUniqueness(report); if (unique) { return; } List<LineHits> out = new ArrayList<>(report.size()); LineHits prev = null; for (LineHits cur : report) { if (prev != null && prev.getLineNumber() == cur.getLineNumber()) { prev.addHits(cur.getHits()); } else { out.add(cur); prev = cur; } } report.clear(); report.addAll(out); } private static boolean checkForLineUniqueness(@NotNull List<LineHits> lineHitsList) { LineHits prev = null; for (LineHits cur : lineHitsList) { if (prev != null && prev.getLineNumber() == cur.getLineNumber()) { return false; } prev = cur; } return true; } public static class LineHits implements Comparable<LineHits> { private final int myLineNumber; private int myHits; public LineHits(int lineNumber, int hits) { myLineNumber = lineNumber; myHits = hits; } public int getLineNumber() { return myLineNumber; } public int getHits() { return myHits; } @Override public int compareTo(LineHits o) { return myLineNumber - o.myLineNumber; } public void addHits(int hitCount) { myHits += hitCount; } } private static class PeekingIterator<T> implements Iterator<T> { private final Iterator<T> myIterator; private T myValue = null; private boolean myValidValue = false; private PeekingIterator(Iterator<T> iterator) { myIterator = iterator; advance(); } @Override public boolean hasNext() { return myValidValue; } @Override public T next() { if (myValidValue) { T save = myValue; advance(); return save; } throw new NoSuchElementException(); } public T peek() { if (myValidValue) { return myValue; } throw new NoSuchElementException(); } @Override public void remove() { throw new UnsupportedOperationException(); } private void advance() { myValidValue = myIterator.hasNext(); myValue = myValidValue ? myIterator.next() : null; } } }