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

net.kemitix.dependency.digraph.maven.plugin.AbstractDotFileFormat Maven / Gradle / Ivy

There is a newer version: 0.9.1
Show newest version
package net.kemitix.dependency.digraph.maven.plugin;

import lombok.AccessLevel;
import lombok.Getter;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import net.kemitix.dependency.digraph.maven.plugin.digraph.Digraph;
import net.kemitix.dependency.digraph.maven.plugin.digraph.EdgeElement;
import net.kemitix.dependency.digraph.maven.plugin.digraph.EdgeEndpoint;
import net.kemitix.dependency.digraph.maven.plugin.digraph.ElementContainer;
import net.kemitix.dependency.digraph.maven.plugin.digraph.GraphElement;
import net.kemitix.dependency.digraph.maven.plugin.digraph.NodeElement;
import net.kemitix.dependency.digraph.maven.plugin.digraph.NodeProperties;
import net.kemitix.dependency.digraph.maven.plugin.digraph.PropertyElement;
import net.kemitix.dependency.digraph.maven.plugin.digraph.Subgraph;
import net.kemitix.node.Node;

/**
 * Abstract base for {@link DotFileFormat} implementations.
 *
 * @author pcampbell
 */
public abstract class AbstractDotFileFormat implements DotFileFormat {

    @Getter(AccessLevel.PROTECTED)
    private final Node base;

    private final NodePathGenerator nodePathGenerator;

    private final NodePackageDataComparator nodePackageDataComparator
            = new NodePackageDataComparator();

    private Map, GraphElement> graphElements
            = new HashMap<>();

    /**
     * Constructor.
     *
     * @param base              the base package
     * @param nodePathGenerator the node path generator
     */
    public AbstractDotFileFormat(
            final Node base,
            final NodePathGenerator nodePathGenerator) {
        this.base = base;
        this.nodePathGenerator = nodePathGenerator;
    }

    protected String getClusterId(final Node node) {
        return getPath(node, "_");
    }

    protected String getPath(
            final Node headNode, final String delimiter) {
        return nodePathGenerator.getPath(headNode, getBase(), delimiter);
    }

    protected Digraph createDigraph() {
        Digraph digraph = new Digraph();
        digraph.add(new PropertyElement("compound", "true"));
        final NodeProperties nodeProperties = new NodeProperties();
        nodeProperties.add(new PropertyElement("shape", "box"));
        digraph.add(nodeProperties);
        return digraph;
    }

    @Override
    public String renderReport() {
        Digraph digraph = createDigraph();
        getNodeInjector().injectNodes(digraph, base);
        getUsageInjector().injectUsages(digraph, base);
        return render(digraph);
    }

    protected GraphUsageInjector getUsageInjector() {
        return (container, node) -> node.getChildren()
                                        .stream()
                                        .sorted(nodePackageDataComparator)
                                        .forEach(injectUsagesByChildren(
                                                container));
    }

    private Consumer> injectUsagesByChildren(
            final ElementContainer container) {
        return (Node childNode) -> {
            childNode.getData()
                     .ifPresent(data -> data.getUses()
                                            .stream()
                                            .filter(n -> n.isDescendantOf(
                                                    getBase()))
                                            .sorted(nodePackageDataComparator)
                                            .map(usedNode -> createEdgeElement(
                                                    childNode, usedNode))
                                            .forEach(container::add));
            getUsageInjector().injectUsages(container, childNode);
        };
    }

    EdgeEndpoint findEdgeEndpoint(final Node node) {
        if (node.getChildren().isEmpty()) {
            return findNodeElement(node);
        }
        return findSubgraph(node);
    }

    NodeElement createNodeElement(
            final Node packageDataNode) {
        return new NodeElement(packageDataNode, getNodeId(packageDataNode),
                NodeHelper.getRequiredData(packageDataNode).getName());
    }

    EdgeElement createEdgeElement(
            final Node tail, final Node head) {
        return new EdgeElement(findEdgeEndpoint(tail), findEdgeEndpoint(head));
    }

    protected NodeElement findNodeElement(final Node node) {
        if (!graphElements.containsKey(node)) {
            graphElements.put(node, createNodeElement(node));
        }
        return (NodeElement) graphElements.get(node);
    }

    Subgraph findSubgraph(final Node node) {
        if (!graphElements.containsKey(node)) {
            graphElements.put(node, createSubgraph(node));
        }
        return (Subgraph) graphElements.get(node);
    }

    private Subgraph createSubgraph(final Node node) {
        return new Subgraph(node, getClusterId(node),
                NodeHelper.getRequiredData(node).getName());
    }

    protected GraphNodeInjector getNodeInjector() {
        return new GraphNodeInjector() {
            @Override
            public void injectNodes(
                    final ElementContainer container,
                    final Node node) {
                final Set> children = node.getChildren();
                if (children.isEmpty()) {
                    container.add(findNodeElement(node));
                } else {
                    Subgraph subgraph = findSubgraph(node);
                    children.stream()
                            .sorted(nodePackageDataComparator)
                            .forEach(c -> this.injectNodes(subgraph, c));
                    container.add(subgraph);
                }
            }
        };
    }

    protected String getNodeId(final Node node) {
        return getPath(node, ".");
    }

    String render(final GraphElement graphElement) {
        if (graphElement instanceof Digraph) {
            return render((Digraph) graphElement);
        }
        if (graphElement instanceof Subgraph) {
            return render((Subgraph) graphElement);
        }
        if (graphElement instanceof NodeProperties) {
            return render((NodeProperties) graphElement);
        }
        if (graphElement instanceof NodeElement) {
            return render((NodeElement) graphElement);
        }
        if (graphElement instanceof EdgeElement) {
            return render((EdgeElement) graphElement);
        }
        if (graphElement instanceof PropertyElement) {
            return render((PropertyElement) graphElement);
        }
        return "(graph-element)";
    }

    String render(final Digraph digraph) {
        return "digraph{\n" + renderElements(digraph.getElements()) + "}\n";
    }

    abstract String render(final Subgraph subgraph);

    String render(
            final NodeProperties nodeProperties) {
        return "node[" + renderProperties(nodeProperties.getProperties()) + "]";
    }

    String render(
            final NodeElement nodeElement) {
        final String id = nodeElement.getId();
        final String label = nodeElement.getLabel();
        if (id.equals(label)) {
            return quoted(id);
        } else {
            return quoted(id) + "[label=" + quoted(label) + "]";
        }
    }

    abstract String render(final EdgeElement edgeElement);

    String render(
            final PropertyElement propertyElement) {
        return propertyElement.getName() + "=" + quoted(
                propertyElement.getValue());
    }

    String renderElements(final Collection elements) {
        return elements.stream()
                       .map(this::render)
                       .collect(Collectors.joining("\n"));
    }

    String renderProperties(final Set properties) {
        return properties.stream()
                         .map(this::render)
                         .collect(Collectors.joining(";\n"));
    }

    protected String quoted(final String text) {
        return "\"" + text + "\"";
    }

    /**
     * Functional Interface for rendering a node.
     */
    @FunctionalInterface
    protected interface GraphNodeInjector {

        void injectNodes(
                final ElementContainer container, final Node node);

    }

    /**
     * Functional Interface for rendering a usage.
     */
    @FunctionalInterface
    protected interface GraphUsageInjector {

        void injectUsages(
                final ElementContainer container, final Node node);

    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy