org.locationtech.geogig.model.internal.DAG.java Source code

Java tutorial

Introduction

Here is the source code for org.locationtech.geogig.model.internal.DAG.java

Source

/* Copyright (c) 2015-2016 Boundless and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Distribution License v1.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/org/documents/edl-v10.html
 *
 * Contributors:
 * Gabriel Roldan (Boundless) - initial implementation
 */
package org.locationtech.geogig.model.internal;

import static com.google.common.base.Objects.equal;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;

import org.locationtech.geogig.model.ObjectId;
import org.locationtech.geogig.model.RevTree;
import org.locationtech.geogig.storage.datastream.Varint;

import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;

class DAG implements Cloneable, Serializable {

    private static final long serialVersionUID = 1L;

    public static enum STATE {
        /**
         * The DAG is just created and has not been mirrored to the original RevTree
         */
        INITIALIZED,
        /**
         * The DAG structre mirrors the original RevTree
         */
        MIRRORED,
        /**
         * The structure of the DAG has changed wrt it's original RevTree {@link DAG#treeId}
         */
        CHANGED;
    }

    /**
     * Direct children feature and tree nodes that can be promoted to buckets if need be, keyed by
     * featureid
     */
    private Map<String, NodeId> children;

    /**
     * Bucket tree ids. At any point in time, a DAG has either buckets or feature/tree nodes.
     */
    private Set<TreeId> buckets;

    /**
     * The object identifier for the {@link RevTree} this DAG was created from, or
     * {@link RevTree#EMPTY} if created from scratch.
     */
    public final ObjectId originalTreeId;

    private STATE state;

    /**
     * Total (recursive) number of children. Differs from tree size (computed at RevTree built time)
     * in that tree size includes the size of any child tree node.
     */
    private long childCount;

    private final transient TreeId id;

    /**
     * Not to be changed directly, but through {@link #setMutated}
     */
    private transient boolean mutated;

    @Override
    public DAG clone() {
        DAG c = new DAG(id, originalTreeId);
        if (!children.isEmpty()) {
            c.children = new HashMap<>(children);
        }
        if (!buckets.isEmpty()) {
            c.buckets = new HashSet<>(buckets);
        }
        c.state = state;
        c.setChildCount(childCount);
        c.mutated = this.mutated;
        return c;
    }

    public DAG(TreeId id) {
        this.id = id;
        this.children = new HashMap<>();
        this.originalTreeId = RevTree.EMPTY_TREE_ID;
        this.state = STATE.INITIALIZED;
    }

    DAG(final TreeId id, final ObjectId originalTreeId, long childCount, STATE state, Set<NodeId> children,
            Set<TreeId> buckets) {
        this.id = id;
        this.originalTreeId = originalTreeId;
        this.setChildCount(childCount);
        this.state = state;
        this.children = new HashMap<>(Maps.uniqueIndex(children, (n) -> n.name()));
        this.buckets = buckets;
    }

    public DAG(TreeId id, ObjectId originalTreeId) {
        this.id = id;
        this.children = new HashMap<>();
        this.originalTreeId = originalTreeId;
        this.state = STATE.INITIALIZED;
        this.children = ImmutableMap.of();
        this.buckets = ImmutableSet.of();
    }

    public TreeId getId() {
        return id;
    }

    public void setMirrored() {
        Preconditions.checkState(state == STATE.INITIALIZED);
        setMutated();
        this.state = STATE.MIRRORED;
    }

    public void setChanged() {
        Preconditions.checkState(state == STATE.MIRRORED || state == STATE.CHANGED);
        if (this.state != STATE.CHANGED) {
            setMutated();
        }
        this.state = STATE.CHANGED;
    }

    public STATE getState() {
        return this.state;
    }

    public void clearChildren() {
        if (!children.isEmpty()) {
            setMutated();
            this.children = ImmutableMap.of();
        }
    }

    public boolean addChild(NodeId nodeId) {
        if (this.children.isEmpty()) {
            this.children = new HashMap<>();
        }
        NodeId oldVal = this.children.put(nodeId.name(), nodeId);
        boolean changed = !Objects.equal(oldVal, nodeId);
        if (changed) {
            setMutated();
        }
        return changed;
    }

    public void clearBuckets() {
        if (!buckets.isEmpty()) {
            setMutated();
            this.buckets = ImmutableSet.of();
        }
    }

    public boolean addBucket(TreeId treeId) {
        if (this.buckets.isEmpty()) {
            this.buckets = new HashSet<>();
        }
        boolean changed = this.buckets.add(treeId);
        if (changed) {
            setMutated();
        }
        return changed;
    }

    @Override
    public boolean equals(Object o) {
        if (!(o instanceof DAG)) {
            return false;
        }
        DAG d = (DAG) o;
        return equal(originalTreeId, d.originalTreeId) && equal(getChildCount(), d.getChildCount())
                && equal(state, d.state) && equal(children, d.children) && equal(buckets, d.buckets);
    }

    @Override
    public String toString() {
        return "DAG[features: " + children + ", buckets: " + buckets + "]";
    }

    public boolean isEmpty() {
        return children.isEmpty() && buckets.isEmpty();
    }

    public long getChildCount() {
        return childCount;
    }

    public void setChildCount(long childCount) {
        if (this.childCount != childCount) {
            setMutated();
        }
        this.childCount = childCount;
    }

    public boolean removeChild(NodeId nodeId) {
        if (children.isEmpty()) {
            return false;
        }
        NodeId removed = children.remove(nodeId.name());
        boolean changed = removed != null;
        if (changed) {
            setMutated();
        }
        return changed;
    }

    public int numBuckets() {
        return buckets.size();
    }

    public int numChildren() {
        return children.size();
    }

    public void forEachBucket(Consumer<? super TreeId> action) {
        this.buckets.forEach(action);
    }

    public void forEachChild(Consumer<? super NodeId> action) {
        this.children.values().forEach(action);
    }

    boolean isMutated() {
        return mutated;
    }

    Consumer<DAG> changeListener;

    protected void setMutated() {
        this.mutated = true;
        if (changeListener != null) {
            changeListener.accept(this);
        }
    }

    void setMutated(boolean mutated) {
        this.mutated = mutated;
    }

    public static void serialize(DAG dag, DataOutput out) throws IOException {
        final ObjectId treeId = dag.originalTreeId;
        final STATE state = dag.getState();
        final long childCount = dag.getChildCount();
        final Collection<NodeId> children = dag.children.values();
        final Set<TreeId> buckets = dag.buckets;

        treeId.writeTo(out);
        out.writeByte(state.ordinal());
        Varint.writeUnsignedVarLong(childCount, out);

        Varint.writeUnsignedVarInt(children.size(), out);
        Varint.writeUnsignedVarInt(buckets.size(), out);

        for (NodeId nodeid : children) {
            NodeId.write(nodeid, out);
        }
        for (TreeId tid : buckets) {
            byte[] bucketIndicesByDepth = tid.bucketIndicesByDepth;
            Varint.writeUnsignedVarInt(bucketIndicesByDepth.length, out);
            out.write(bucketIndicesByDepth);
        }
    }

    public static DAG deserialize(TreeId id, DataInput in) throws IOException {
        final ObjectId treeId = ObjectId.readFrom(in);
        final STATE state = STATE.values()[in.readByte() & 0xFF];
        final long childCount = Varint.readUnsignedVarLong(in);

        final int childrenSize = Varint.readUnsignedVarInt(in);
        final int bucketSize = Varint.readUnsignedVarInt(in);

        Set<NodeId> children = ImmutableSet.of();
        Set<TreeId> buckets = ImmutableSet.of();

        if (childrenSize > 0) {
            children = new HashSet<>();
            for (int i = 0; i < childrenSize; i++) {
                NodeId nid = NodeId.read(in);
                children.add(nid);
            }
        }
        if (bucketSize > 0) {
            buckets = new HashSet<>();
            for (int i = 0; i < bucketSize; i++) {
                final int len = Varint.readUnsignedVarInt(in);
                final byte[] bucketIndicesByDepth = new byte[len];
                in.readFully(bucketIndicesByDepth);
                buckets.add(new TreeId(bucketIndicesByDepth));
            }
        }

        DAG dag = new DAG(id, treeId, childCount, state, children, buckets);
        return dag;
    }
}