All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.jqwik.engine.execution.reporting.ValueReport Maven / Gradle / Ivy

There is a newer version: 1.9.1
Show newest version
package net.jqwik.engine.execution.reporting;

import java.util.*;
import java.util.stream.*;

import net.jqwik.api.*;

public abstract class ValueReport {

	interface ReportingFormatFinder {
		SampleReportingFormat find(Object value);
	}

	// For Testing only
	static ValueReport of(Object value) {
		return of(value, SampleReportingFormats.getReportingFormats());
	}

	public static ValueReport of(Object value, Collection availableFormats) {
		ReportingFormatFinder formatFinder = formatFinder(availableFormats);
		return of(value, formatFinder);
	}

	private static ValueReport of(Object value, ReportingFormatFinder formatFinder) {
		final Set visited = visitedSet(Collections.emptySet());
		return of(value, formatFinder, visited);
	}

	private static Set visitedSet(Set from) {
		Set objects = Collections.newSetFromMap(new IdentityHashMap<>());
		objects.addAll(from);
		return objects;
	}

	@SuppressWarnings("unchecked")
	private static ValueReport of(Object value, ReportingFormatFinder formatFinder, Set visited) {
		SampleReportingFormat format = formatFinder.find(value);
		if (visited.contains(value)) {
			return new CircularDependencyReport(format.label(value), value);
		} else {
			visited.add(value);
		}
		Object reportedValue = format.report(value);
		if (reportedValue instanceof Collection) {
			return createCollectionReport(format.label(value), (Collection) reportedValue, formatFinder, visited);
		}
		if (reportedValue instanceof Map) {
			return createMapReport(format.label(value), (Map) reportedValue, formatFinder, visited);
		}
		if (reportedValue instanceof Tuple) {
			return createTupleReport(format.label(value), (Tuple) reportedValue, formatFinder, visited);
		}
		visited.remove(value);
		return new ObjectValueReport(format.label(value), reportedValue);
	}

	private static ValueReport createTupleReport(
		Optional label,
		Tuple tuple,
		ReportingFormatFinder formatFinder,
		final Set visited
	) {
		List tupleReports =
			tuple.items()
				 .stream()
				 .map(value -> of(value, formatFinder, visitedSet(visited)))
				 .collect(Collectors.toList());

		return new TupleValueReport(label, tupleReports);
	}

	private static ValueReport createMapReport(
		final Optional label,
		final Map map,
		final ReportingFormatFinder formatFinder,
		final Set visited
	) {
		List> reportEntries =
			map.entrySet()
			   .stream()
			   .map(entry -> {
				   ValueReport keyReport = of(entry.getKey(), formatFinder, visitedSet(visited));
				   ValueReport valueReport = of(entry.getValue(), formatFinder, visitedSet(visited));
				   return new Map.Entry() {
					   @Override
					   public ValueReport getKey() {
						   return keyReport;
					   }

					   @Override
					   public ValueReport getValue() {
						   return valueReport;
					   }

					   @Override
					   public ValueReport setValue(ValueReport value) {
						   throw new UnsupportedOperationException();
					   }
				   };
			   })
			   .collect(Collectors.toList());
		return new MapValueReport(label, reportEntries);
	}

	private static ValueReport createCollectionReport(
		Optional label,
		Collection collection,
		ReportingFormatFinder formatFinder,
		final Set visited
	) {
		List reportCollection =
			collection
				.stream()
				.map(element -> of(element, formatFinder, visitedSet(visited)))
				.collect(Collectors.toList());
		return new CollectionValueReport(label, reportCollection);
	}

	private static ReportingFormatFinder formatFinder(Collection unsortedFormats) {
		List formats = new ArrayList<>(unsortedFormats);
		Collections.sort(formats);
		return targetValue ->
				   formats.stream()
						  .filter(format -> {
							  try {
								  return format.appliesTo(targetValue);
							  } catch (NullPointerException npe) {
								  return false;
							  }
						  })
						  .findFirst().orElse(new NullReportingFormat());
	}

	final Optional label;

	protected ValueReport(Optional label) {
		this.label = label;
	}

	int singleLineLength() {
		return singleLineReport().length();
	}

	public abstract String singleLineReport();

	public abstract void report(LineReporter lineReporter, int indentLevel, String appendix);
}