package tc.oc.commons.core.inspect;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.Nullable;
import com.google.common.base.Strings;
import tc.oc.commons.core.util.Chain;
import tc.oc.commons.core.util.Streams;
public class MultiLineTextInspector implements Inspector<Stream<String>> {
private final TextInspector text = new TextInspector();
private Stream<String> indent(Stream<String> lines) {
return lines.map(line -> " " + line);
}
private Stream<String> enclose(String open, Stream<String> lines, String close) {
final List<String> list = lines.collect(Collectors.toList());
return list.isEmpty() ? Stream.of(open + close)
: Streams.concat(Stream.of(open),
indent(list.stream()),
Stream.of(close));
}
private Stream<String> flatten(String delimiter, Stream<Stream<String>> bumpy) {
final List<String> flat = new ArrayList<>();
bumpy.forEachOrdered(lines -> {
if(!flat.isEmpty()) {
final int last = flat.size() - 1;
flat.set(last, flat.get(last) + delimiter);
}
lines.forEachOrdered(flat::add);
});
return flat.stream();
}
@Override
public Stream<String> scalar(@Nullable Object value, Inspection options) {
return Stream.of(text.scalar(value, options));
}
@Override
public <E> Stream<String> collection(Collection<E> collection, Stream<Stream<String>> elements, Inspection options) {
return enclose("[", indent(flatten(",", elements)), "]");
}
private int width(Stream<String> lines) {
return lines.mapToInt(String::length).max().orElse(0);
}
private Stream<String> association(Stream<String> left, String middle, Stream<String> right) {
final List<String> leftList = left.collect(Collectors.toList());
final List<String> rightList = right.collect(Collectors.toList());
final int height = Math.max(leftList.size(), rightList.size());
final int leftWidth = width(leftList.stream());
final String leftPad = leftList.size() < 2 ? "" : Strings.repeat(" ", leftWidth);
final String middlePad = leftList.size() < 2 ? "" : Strings.repeat(" ", middle.length());
final List<String> result = new ArrayList<>();
for(int i = 0; i < height; i++) {
result.add((i < leftList.size() ? Strings.padEnd(leftList.get(i), leftWidth, ' ') : leftPad) +
(i == 0 ? middle : middlePad) +
(i < rightList.size() ? rightList.get(i) : ""));
}
return result.stream();
}
@Override
public <K, V> Stream<String> map(Map<K, V> map, Stream<Map.Entry<Stream<String>, Stream<String>>> entries, Inspection options) {
return enclose("{", indent(flatten(",", entries.map(entry -> association(entry.getKey(), " -> ", entry.getValue())))), "}");
}
@Override
public Stream<String> reference(Inspectable inspectable, Inspection options) {
return Stream.of(text.reference(inspectable, options));
}
@Override
public Stream<String> inspectable(Inspectable inspectable, Stream<Map.Entry<String, Stream<String>>> properties, Inspection options) {
return enclose(inspectable.identify() + "{",
indent(flatten(",", properties.map(property -> association(Stream.of(property.getKey()), " = ", property.getValue())))),
"}");
}
@Override
public Stream<String> exception(InspectionException e, Inspection options) {
return Stream.of(text.exception(e, options));
}
@Override
public Stream<String> cycle(Object value, Chain<Object> path, Inspection options) {
return Stream.of(text.cycle(value, path, options));
}
}