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

es.iti.wakamiti.api.plan.PlanNodeSnapshot Maven / Gradle / Ivy

The newest version!
/*
 * 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 https://mozilla.org/MPL/2.0/.
 */
package es.iti.wakamiti.api.plan;


import es.iti.wakamiti.api.model.ExecutionState;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.atomic.LongAdder;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.stream.Collectors.counting;
import static java.util.stream.Collectors.groupingBy;


/**
 * This class is an immutable, non-executable representation of a
 * {@link PlanNode} in a specific state. It is mainly used for
 * serialization/deserialization operations.
 *
 * @author Luis Iñesta Gelabert - [email protected]
 */
public class PlanNodeSnapshot {

    private String executionID;
    private String snapshotInstant;
    private NodeType nodeType;
    private String id;
    private String name;
    private String keyword;
    private String language;
    private String source;
    private String displayName;
    private List description;
    private List tags;
    private Map properties;
    private String startInstant;
    private String finishInstant;
    private Long duration;
    private String document;
    private String documentType;
    private String[][] dataTable;
    private String errorMessage;
    private String errorTrace;
    private String errorClassifier;
    private Map errorClassifiers;
    private Result result;
    private Map testCaseResults;
    private Map childrenResults;
    private List children;

    public PlanNodeSnapshot() {
    }

    public PlanNodeSnapshot(PlanNode node) {
        this(node, LocalDateTime.now().toString());
    }

    public PlanNodeSnapshot(PlanNode node, String snapshotInstant) {
        this.executionID = node.executionID();
        this.snapshotInstant = snapshotInstant;
        this.nodeType = node.nodeType();
        this.id = node.id();
        this.name = node.name();
        this.keyword = node.keyword();
        this.language = node.language();
        this.source = node.source();
        this.displayName = node.displayName();
        this.description = new LinkedList<>(node.description() == null ? List.of() : node.description());
        this.tags = new LinkedList<>(node.tags() == null ? List.of() : node.tags());
        this.properties = new LinkedHashMap<>(node.properties() == null ? Map.of() : node.properties());
        this.startInstant = node.startInstant().map(this::instantToString).orElse(null);
        this.finishInstant = node.finishInstant().map(this::instantToString).orElse(null);
        this.duration = node.duration().map(Duration::toMillis).orElse(null);
        this.result = node.result().orElse(null);
        this.document = node.data().filter(Document.class::isInstance).map(Document.class::cast)
                .map(Document::getContent).orElse(null);
        this.documentType = node.data().filter(Document.class::isInstance).map(Document.class::cast)
                .map(Document::getContentType).orElse(null);
        this.dataTable = node.data().filter(DataTable.class::isInstance).map(DataTable.class::cast)
                .map(DataTable::getValues).orElse(null);
        this.errorMessage = node.errors().findFirst().map(Throwable::getLocalizedMessage)
                .orElse(null);
        this.errorTrace = node.errors().findFirst().map(this::errorTrace).orElse(null);
        if (node.nodeType == NodeType.STEP && node.executionState().flatMap(ExecutionState::errorClassifier).isPresent()) {
            this.errorClassifier = node.executionState().flatMap(ExecutionState::errorClassifier).orElse(null);
        } else if (node.nodeType == NodeType.STEP_AGGREGATOR || node.nodeType == NodeType.TEST_CASE) {
            this.errorClassifier = node.errorClassifiers().findFirst().orElse(null);
        } else if (node.nodeType == NodeType.AGGREGATOR && node.hasChildren()) {
            this.errorClassifiers = countTestClassifiers(node);
        }
        if (node.hasChildren()) {
            this.children = node.children().map(child -> new PlanNodeSnapshot(child, snapshotInstant))
                    .collect(Collectors.toList());
            this.testCaseResults = countTestCases(node);
            this.childrenResults = countChildren(node);
        }
    }

    /**
     * Creates a new node descriptor as a parent of the specified nodes.
     *
     * @param nodes The nodes to be grouped.
     * @return A new parent node descriptor.
     */
    public static PlanNodeSnapshot group(PlanNodeSnapshot... nodes) {
        if (nodes.length == 1) {
            return nodes[0];
        }
        PlanNodeSnapshot root = new PlanNodeSnapshot();
        root.children = Arrays.asList(nodes);
        root.startInstant = childLocalDateTime(
                root,
                PlanNodeSnapshot::getStartInstant,
                (x, y) -> x.isBefore(y) ? x : y
        );
        root.finishInstant = childLocalDateTime(
                root,
                PlanNodeSnapshot::getFinishInstant,
                (x, y) -> x.isAfter(y) ? x : y
        );
        root.duration = maxChild(root, PlanNodeSnapshot::getDuration);
        root.result = maxChild(root, PlanNodeSnapshot::getResult);
        root.testCaseResults = new LinkedHashMap<>();
        root.children.stream().map(PlanNodeSnapshot::getTestCaseResults).filter(Objects::nonNull)
                .flatMap(map -> map.entrySet().stream()).forEach(entry -> {
                    root.testCaseResults.computeIfAbsent(entry.getKey(), x -> 0L);
                    root.testCaseResults.put(
                            entry.getKey(),
                            root.testCaseResults.get(entry.getKey()) + entry.getValue()
                    );
                });
        root.childrenResults = countChildren(root);
        return root;
    }

    private static Map countTestCases(PlanNode node) {
        LinkedHashMap results = new LinkedHashMap<>();
        if (node.nodeType() == NodeType.TEST_CASE) {
            node.result().ifPresent(testCaseResult -> results.put(testCaseResult, 1L));
        } else if (node.hasChildren()) {
            node.children().map(PlanNodeSnapshot::countTestCases).filter(Objects::nonNull)
                    .flatMap(map -> map.entrySet().stream())
                    .forEach(entry -> {
                        results.computeIfAbsent(entry.getKey(), x -> 0L);
                        results.put(entry.getKey(), results.get(entry.getKey()) + entry.getValue());
                    });
        }
        return results;
    }


    private static Map countTestClassifiers(PlanNode node) {
        LinkedHashMap results = new LinkedHashMap<>();
        if (node.nodeType() == NodeType.TEST_CASE) {
            node.errorClassifiers().findFirst()
                    .ifPresent(errorClassifier -> results.computeIfAbsent(errorClassifier, x -> new LongAdder()).add(1L));
        } else if (node.nodeType() == NodeType.AGGREGATOR && node.hasChildren()) {
            node.children().map(PlanNodeSnapshot::countTestClassifiers).filter(Objects::nonNull)
                    .flatMap(map -> map.entrySet().stream())
                    .forEach(entry -> results.computeIfAbsent(entry.getKey(), x -> new LongAdder()).add(entry.getValue()));
        }
        return results.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().longValue()));
    }

    private static Map countChildren(PlanNode node) {
        return node.children()
                .filter(it -> it.result().isPresent())
                .collect(groupingBy(it -> it.result().orElseThrow(), counting()));
    }

    private static Map countChildren(PlanNodeSnapshot node) {
        return node.getChildren().stream().collect(groupingBy(PlanNodeSnapshot::getResult, counting()));
    }

    private static String childLocalDateTime(
            PlanNodeSnapshot node,
            Function method,
            BinaryOperator reducer
    ) {
        return node.children.stream().map(method).filter(Objects::nonNull).map(LocalDateTime::parse)
                .reduce(reducer)
                .map(LocalDateTime::toString).orElse(null);
    }

    private static > T maxChild(
            PlanNodeSnapshot node,
            Function mapper
    ) {
        return node.children.stream().map(mapper).filter(Objects::nonNull)
                .max(Comparator.naturalOrder()).orElse(null);
    }

    public Stream flatten(Predicate filter) {
        return Stream.concat(
                Optional.of(this).filter(filter).stream(),
                Optional.ofNullable(this.children).stream().flatMap(Collection::stream)
                        .flatMap(p -> p.flatten(filter))
                );
    }

    /**
     * Creates a new PlanNodeSnapshot without children.
     *
     * @return A new PlanNodeSnapshot without children.
     */
    public PlanNodeSnapshot withoutChildren() {
        PlanNodeSnapshot copy = new PlanNodeSnapshot();
        copy.executionID = this.executionID;
        copy.snapshotInstant = this.snapshotInstant;
        copy.nodeType = this.nodeType;
        copy.id = this.id;
        copy.name = this.name;
        copy.keyword = this.keyword;
        copy.language = this.language;
        copy.source = this.source;
        copy.displayName = this.displayName;
        copy.description = this.description;
        copy.tags = this.tags;
        copy.properties = this.properties;
        copy.startInstant = this.startInstant;
        copy.finishInstant = this.finishInstant;
        copy.duration = this.duration;
        copy.result = this.result;
        copy.document = this.document;
        copy.documentType = this.documentType;
        copy.dataTable = this.dataTable;
        copy.errorMessage = this.errorMessage;
        copy.errorTrace = this.errorTrace;
        copy.testCaseResults = this.testCaseResults;
        copy.childrenResults = this.childrenResults;
        copy.errorClassifiers = this.errorClassifiers;
        copy.errorClassifier = this.errorClassifier;
        return copy;
    }

    private String instantToString(Instant instant) {
        return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()).toString();
    }

    private String errorTrace(Throwable error) {
        StringWriter errorWriter = new StringWriter();
        error.printStackTrace(new PrintWriter(errorWriter));
        return errorWriter.toString();
    }

    public NodeType getNodeType() {
        return nodeType;
    }


    public String getId() {
        return id;
    }


    public String getName() {
        return name;
    }


    public String getKeyword() {
        return keyword;
    }


    public String getLanguage() {
        return language;
    }


    public String getSource() {
        return source;
    }


    public String getDisplayName() {
        return displayName;
    }


    public List getDescription() {
        return description;
    }


    public List getTags() {
        return tags;
    }


    public Map getProperties() {
        return properties;
    }


    public String getStartInstant() {
        return startInstant;
    }


    public String getFinishInstant() {
        return finishInstant;
    }


    public Long getDuration() {
        return duration;
    }


    public String getDocument() {
        return document;
    }


    public String getDocumentType() {
        return documentType;
    }


    public String[][] getDataTable() {
        return dataTable;
    }


    public String getErrorMessage() {
        return errorMessage;
    }


    public String getErrorTrace() {
        return errorTrace;
    }


    public String getErrorClassifier() {
        return errorClassifier;
    }


    public Result getResult() {
        return result;
    }


    public Map getTestCaseResults() {
        return testCaseResults;
    }


    public Map getChildrenResults() {
        return childrenResults;
    }


    public Map getErrorClassifiers() {
        return errorClassifiers;
    }

    public List getChildren() {
        return children;
    }


    public String getExecutionID() {
        return executionID;
    }


    public String getSnapshotInstant() {
        return snapshotInstant;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy