# GraphModel.java

    package org.thewonderlemming.c4plantuml.graphml.model;

    import java.util.List;

    import javax.xml.bind.annotation.XmlAccessType;
    import javax.xml.bind.annotation.XmlAccessorType;
    import javax.xml.bind.annotation.XmlAttribute;
    import javax.xml.bind.annotation.XmlElement;
    import javax.xml.bind.annotation.XmlRootElement;

    import org.thewonderlemming.c4plantuml.graphml.Build;
    import org.thewonderlemming.c4plantuml.graphml.model.builder.WithDirection;
    import org.thewonderlemming.c4plantuml.graphml.model.builder.WithId;

    /**
     * The &lt;graph&gt; part of the representation of a GraphML stream.
     * <p>
     * It is based on JAXB.
     *
     * @author thewonderlemming
     *
     */
    @XmlRootElement(name = GraphModel.TAG_NAME)
    @XmlAccessorType(XmlAccessType.FIELD)
    public class GraphModel {

        /**
         * The current model XML tag name.
         */
        public static final String TAG_NAME = "graph";

        @XmlAttribute(name = "edgedefault", required = true)
        private String edgeDefault;

        @XmlAttribute(name = "id", required = true)
        private String id;

        @XmlElement(name = DataModel.TAG_NAME)
        private MappedListDecorator<DataModel, String> seq1Data;

        @XmlElement(name = NodeModel.TAG_NAME)
        private MappedListDecorator<NodeModel, String> seq2Nodes;

        @XmlElement(name = EdgeModel.TAG_NAME)
        private MappedListDecorator<EdgeModel, String> seq3Edges;

        /**
         * A builder to the current {@link GraphModel} class.
         *
         * @return a new {@link GraphModel} instance.
         */
        public static WithId<String, WithDirection<WithData<WithNodes<WithEdges<Build<GraphModel>>>>>> builder() {

            return id -> edgeDefault -> data -> nodes -> edges -> () -> {

                final GraphModel graph = new GraphModel();
                graph.setId(id);
                graph.setEdgeDefault(edgeDefault);
                graph.setData(data);
                graph.setNodes(nodes);
                graph.setEdges(edges);

                return graph;
            };
        }

        /**
         * Default constructor.
         */
        private GraphModel() {
            // Does nothing but hiding the current constructor.
        }

        /**
         * Adds the given {@link DataModel} to the list if its {@link DataModel#getKey()} is not already there or replaces
         * it else.
         *
         * @param data the given {@link DataModel} to add/replace.
         */
        public void addOrReplaceData(final DataModel data) {
            this.seq1Data.addOrReplaceData(data);
        }

        /**
         * Adds the given {@link EdgeModel} to the list if its {@link EdgeModel#getId()} is not already there or replaces
         * it else.
         *
         * @param edge the given {@link EdgeModel} to add/replace.
         */
        public void addOrReplaceEdge(final EdgeModel edge) {
            this.seq3Edges.addOrReplaceData(edge);
        }

        /**
         * Adds the given {@link NodeModel} to the list if its {@link NodeModel#getId()} is not already there or replaces
         * it else.
         *
         * @param node the given {@link NodeModel} to add/replace.
         */
        public void addOrReplaceNode(final NodeModel node) {
            this.seq2Nodes.addOrReplaceData(node);
        }

        /**
         * Returns the current graph's properties list.
         *
         * @return the {@link DataModel} list.
         */
        public List<DataModel> getData() {
            return seq1Data;
        }

        /**
         * Returns the graph's &lt;edgedefault&gt; property.
         *
         * @return the &lt;edgedefault&gt; property.
         */
        public String getEdgeDefault() {
            return edgeDefault;
        }

        /**
         * Returns the graph's edges as a list.
         *
         * @return the graph {@link EdgeModel} list.
         */
        public List<EdgeModel> getEdges() {
            return seq3Edges;
        }

        /**
         * Returns the graph ID.
         *
         * @return the graph ID.
         */
        public String getId() {
            return id;
        }

        /**
         * Returns the graph(s nodes as a list.
         *
         * @return the graph {@link NodeModel} list.
         */
        public List<NodeModel> getNodes() {
            return seq2Nodes;
        }

        /**
         * Tells whether or not the current graph is a child of a node (or if it stands alone).
         *
         * @param node the {@link NodeModel} that is supposed to be the the parent node.
         * @return {@code true} if the graph is a child of the given {@code node}, {@code false} else.
         */
        public boolean isChildOfNode(final NodeModel node) {
            return this.id.equals(node.getId() + "::");
        }

        /**
         * Tells whether or not the current graph is empty.
         *
         * @return {@code true} if the graph is empty, {@code false} else.
         */
        public boolean isEmpty() {
            return this.seq1Data.isEmpty() && this.seq2Nodes.isEmpty() && this.seq3Edges.isEmpty();
        }

        /**
         * Tells whether or not the current graph is not empty.
         *
         * @return {@code true} if the graph is not empty, {@code false} else.
         */
        public boolean isNotEmpty() {
            return !this.isEmpty();
        }

        /**
         * Sets the graph's aspect.
         *
         * @param aspect the graph {@code aspect}.
         */
        public void setAspect(final String aspect) {

            this
                .addOrReplaceData(DataModel
                    .builder()
                        .withKey(C4Keys.ASPECT)
                        .withValue(aspect)
                        .build());
        }

        /**
         * Sets the graph's ID.
         *
         * @param id the graph {@code id}
         */
        public void setId(final String id) {
            this.id = id;
        }

        /**
         * Sets a &lt;data&gt; tag of type {@link C4Keys#TITLE} and with the given value.
         *
         * @param title the data value to set.
         */
        public void setTitle(final String title) {

            this
                .addOrReplaceData(DataModel
                    .builder()
                        .withKey(C4Keys.TITLE)
                        .withValue(title)
                        .build());
        }

        private void setData(final List<DataModel> data) {
            this.seq1Data = new MappedListDecorator<>(DataModel.class, data, DataModel::getKey);
        }

        private void setEdgeDefault(final String edgeDefault) {
            this.edgeDefault = edgeDefault;
        }

        private void setEdges(final List<EdgeModel> edges) {
            this.seq3Edges = new MappedListDecorator<>(EdgeModel.class, edges, EdgeModel::getId);
        }

        private void setNodes(final List<NodeModel> nodes) {
            this.seq2Nodes = new MappedListDecorator<>(NodeModel.class, nodes, NodeModel::getId);
        }
    }