DifferencesReport.java
/**
* Copyright (c) 2017-2018, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* SPDX-License-Identifier: MPL-2.0
*/
package com.powsybl.cgmes.conversion.test.network.compare;
import java.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.powsybl.iidm.network.Identifiable;
/**
* @author Luma Zamarre��o {@literal <zamarrenolm at aia.es>}
*/
public class DifferencesReport implements Differences {
public DifferencesReport() {
unexpecteds = new HashMap<>();
missings = new HashMap<>();
matches = new HashMap<>();
diffs = new HashMap<>();
logDetails = true;
maxDetailDiffs = 3;
// Pad size for each category of differences
padSizes = new int[]{25, 25, 25, 50};
}
@Override
public void current(Identifiable i) {
this.current = i;
}
@Override
public void end() {
report(summary());
}
@Override
public void compare(String context, double expected, double actual, double tolerance) {
if (Math.abs(expected - actual) > tolerance) {
record(context, expected, actual);
}
}
@Override
public void compare(String context, Object expected, Object actual) {
boolean equals = false;
if (expected == null) {
if (actual == null) {
equals = true;
}
} else {
equals = expected.equals(actual);
}
if (!equals) {
record(context, expected, actual);
}
}
@Override
public void unexpected(Identifiable i) {
String context = Comparison.className(i);
record(unexpecteds, context, i);
}
@Override
public void missing(Identifiable i) {
String context = Comparison.className(i);
record(missings, context, i);
}
@Override
public void match(Identifiable i) {
String context = Comparison.className(i);
record(matches, context, i);
}
@Override
public void unexpected(String property) {
String context = Comparison.className(current) + "." + property;
record(unexpecteds, context, null);
}
@Override
public void missing(String property) {
String context = Comparison.className(current) + "." + property;
record(missings, context, null);
}
@Override
public void notEquivalent(String context, Identifiable expected, Identifiable actual) {
record(context, expected, actual);
}
@Override
public void notSameIdentifier(String context, Identifiable expected, Identifiable actual) {
record(context, expected, actual);
}
private static class Diff {
Diff(Object expected, Object actual) {
this.expected = expected;
this.actual = actual;
}
Object expected;
Object actual;
}
private void record(String context, Object expected, Object actual) {
String ccontext = Comparison.className(current) + "." + context;
if (!diffs.containsKey(ccontext)) {
diffs.put(ccontext, new HashMap<>());
}
if (!diffs.get(ccontext).containsKey(current)) {
diffs.get(ccontext).put(current, new HashSet<>());
}
diffs.get(ccontext).get(current).add(new Diff(expected, actual));
}
private void record(Map<String, Set<Identifiable>> is, String className, Identifiable i) {
if (!is.containsKey(className)) {
is.put(className, new HashSet<>());
}
is.get(className).add(i);
}
private enum Category {
UNEXPECTED, MISSING, MATCHES, DIFFS
}
private static class Summary {
void inc(Category cat, String context, int size) {
Map<String, Integer> s = data.get(cat);
if (s == null) {
s = new HashMap<>();
data.put(cat, s);
}
Integer value = s.get(context);
if (value == null) {
value = 0;
}
s.put(context, value + size);
}
private final Map<Category, Map<String, Integer>> data = new EnumMap<>(Category.class);
}
private void report(Summary s) {
for (Category cat : Category.values()) {
LOG.info("{}", cat);
Map<String, Integer> dataCat = s.data.get(cat);
if (dataCat != null) {
dataCat.keySet().stream().sorted().forEach(c -> {
int padSize = padSizes[cat.ordinal()];
LOG.info(" {} : {}", padr(c, padSize), s.data.get(cat).get(c));
});
}
}
}
private Summary summary() {
Summary summary = new Summary();
summary(Category.UNEXPECTED, unexpecteds, summary);
summary(Category.MISSING, missings, summary);
summary(Category.MATCHES, matches, summary);
if (!diffs.isEmpty()) {
diffs.keySet().forEach(category -> {
if (logDetails) {
LOG.warn("Differences for {} ({})", category, diffs.get(category).size());
}
diffs.get(category).keySet().stream()
.sorted(Comparator.comparing(Identifiable::getId))
.limit(maxDetailDiffs)
.forEach(id -> {
Set<Diff> ds = diffs.get(category).get(id);
summary.inc(Category.DIFFS, category, diffs.get(category).size());
if (logDetails) {
if (ds.size() > 1) {
LOG.warn(" {}", id);
diffs.get(category).get(id).stream()
.limit(maxDetailDiffs)
.forEach(diff -> {
LOG.warn(" {} {}",
diff.expected,
diff.actual);
});
} else {
Diff diff = ds.iterator().next();
LOG.warn(" {} {} {}", id, diff.expected, diff.actual);
}
}
});
});
}
return summary;
}
private void summary(
Category category,
Map<String, Set<Identifiable>> iByContext,
Summary s) {
if (iByContext.isEmpty()) {
return;
}
iByContext.keySet()
.forEach(context -> {
int size = iByContext.get(context).size();
s.inc(category, context, size);
if (logDetails) {
LOG.warn("{} {} ({})", category, context, size);
iByContext.get(context).forEach(id -> LOG.warn(" {}", id));
}
});
}
private String padr(String s0, int size) {
String format = String.format("%%-%ds", size);
String s = String.format(format, s0);
int len = s.length();
if (len > size) {
int keepr = 5;
int dots = 3;
if (size > keepr + dots) {
String left = s.substring(0, size - keepr - dots);
String right = s.substring(len - keepr);
return String.format("%s...%s", left, right);
} else {
return s.substring(0, size);
}
}
return s;
}
private Identifiable current;
private final Map<String, Set<Identifiable>> unexpecteds;
private final Map<String, Set<Identifiable>> missings;
private final Map<String, Set<Identifiable>> matches;
private final Map<String, Map<Identifiable, HashSet<Diff>>> diffs;
private final boolean logDetails;
private final long maxDetailDiffs;
private final int[] padSizes;
private static final Logger LOG = LoggerFactory.getLogger(DifferencesReport.class);
}