Package org.locationtech.geogig.api.plumbing.diff

Source Code of org.locationtech.geogig.api.plumbing.diff.MutableTree

/* Copyright (c) 2013-2014 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.api.plumbing.diff;

import static org.locationtech.geogig.api.NodeRef.ROOT;
import static org.locationtech.geogig.api.NodeRef.depth;
import static org.locationtech.geogig.api.NodeRef.split;

import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.annotation.Nullable;

import org.locationtech.geogig.api.Node;
import org.locationtech.geogig.api.NodeRef;
import org.locationtech.geogig.api.ObjectId;
import org.locationtech.geogig.api.RevObject.TYPE;
import org.locationtech.geogig.api.RevTree;
import org.locationtech.geogig.api.RevTreeBuilder;
import org.locationtech.geogig.repository.SpatialOps;
import org.locationtech.geogig.storage.ObjectDatabase;

import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Ordering;
import com.vividsolutions.jts.geom.Envelope;

/**
* A mutable data structure representing the state of a tree and its subtrees
*/
public class MutableTree implements Cloneable {

    private Node node;

    private Map<String, MutableTree> childTrees;

    public static final Ordering<NodeRef> DEEPEST_LAST_COMPARATOR = new Ordering<NodeRef>() {
        @Override
        public int compare(NodeRef o1, NodeRef o2) {

            int depth = Integer.valueOf(depth(o1.path())).compareTo(
                    Integer.valueOf(depth(o2.path())));

            if (depth != 0) {
                return depth;
            }
            return o1.path().compareTo(o2.path());
        }
    };

    public static final Ordering<NodeRef> DEEPEST_FIRST_COMPARATOR = DEEPEST_LAST_COMPARATOR
            .reverse();

    private MutableTree(String name) {
        this(Node.tree(name, RevTree.EMPTY_TREE_ID, ObjectId.NULL));
    }

    private MutableTree(Node node) {
        this.node = node;
        this.childTrees = Maps.newTreeMap();
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        toString(this, sb, 0);
        return sb.toString();
    }

    private void toString(MutableTree tree, StringBuilder sb, int indent) {
        Node node = tree.getNode();
        append(sb, node, indent);

        for (MutableTree c : tree.childTrees.values()) {
            toString(c, sb, indent + 1);
        }

    }

    private void append(StringBuilder sb, Node node, int indent) {
        sb.append(Strings.repeat("    ", indent)).append(node.getName()).append("->")
                .append(node.getObjectId()).append(" (").append(node.getMetadataId()).append(")\n");
    }

    @Override
    public boolean equals(Object o) {
        if (o == this) {
            return true;
        }
        if (!(o instanceof MutableTree)) {
            return false;
        }
        MutableTree other = (MutableTree) o;
        return node.equals(other.node) && node.getMetadataId().equals(other.node.getMetadataId())
                && childTrees.equals(other.childTrees);
    }

    public static MutableTree createFromRefs(final ObjectId rootId,
            final Supplier<Iterator<NodeRef>> refs) {
        return createFromRefs(rootId, refs.get());
    }

    public static MutableTree createFromRefs(final ObjectId rootId,
            @Nullable final NodeRef... treeRefs) {
        Iterator<NodeRef> refs = Iterators.emptyIterator();
        if (treeRefs != null) {
            refs = Lists.newArrayList(treeRefs).iterator();
        }
        return createFromRefs(rootId, refs);
    }

    public static MutableTree createFromRefs(final ObjectId rootId, final Iterator<NodeRef> treeRefs) {

        Function<NodeRef, String> keyFunction = new Function<NodeRef, String>() {

            @Override
            public String apply(@Nullable NodeRef input) {
                return input.path();
            }
        };

        ImmutableMap<String, NodeRef> treesByPath = Maps.uniqueIndex(treeRefs, keyFunction);

        return createFromPaths(rootId, treesByPath);
    }

    public static MutableTree createFromPaths(final ObjectId rootId,
            final Map<String, NodeRef> entries) {

        List<NodeRef> refsByDepth = Lists.newArrayList(entries.values());
        Collections.sort(refsByDepth, DEEPEST_LAST_COMPARATOR);

        Node rootNode = Node.create(ROOT, rootId, ObjectId.NULL, TYPE.TREE, null);
        MutableTree root = new MutableTree(rootNode);

        Envelope bounds = new Envelope();

        for (NodeRef entry : refsByDepth) {
            Node node = entry.getNode();
            node.expand(bounds);
            String parentPath = entry.getParentPath();
            root.setChild(parentPath, node);
        }
        // recreate root node with the appropriate bounds
        rootNode = Node.create(ROOT, rootId, ObjectId.NULL, TYPE.TREE, bounds);
        root.setNode(rootNode);

        return root;
    }

    public Node getNode() {
        return node;
    }

    public void forceChild(final String parentPath, final Node treeNode) {
        ImmutableList<String> parentSteps = NodeRef.split(parentPath);
        MutableTree parent = this;
        for (String name : parentSteps) {
            MutableTree child = parent.childTrees.get(name);
            if (child == null) {
                child = new MutableTree(name);
                parent.childTrees.put(name, child);
            }
            parent = child;
        }

        MutableTree tree = parent.childTrees.get(treeNode.getName());
        if (tree == null) {
            tree = new MutableTree(treeNode);
            parent.childTrees.put(treeNode.getName(), tree);
        } else {
            tree.setNode(treeNode);
        }
    }

    public void setChild(String parentPath, Node node) {
        List<String> parentSteps = split(parentPath);
        setChild(parentSteps, node);
    }

    public void setChild(final List<String> parentPath, final Node node) {
        MutableTree parent;
        MutableTree child;
        if (parentPath.isEmpty()) {
            parent = this;
        } else {
            parent = getChild(parentPath);
        }

        child = parent.childTrees.get(node.getName());
        if (child == null) {
            child = new MutableTree(node);
            parent.childTrees.put(node.getName(), child);
        } else {
            child.setNode(node);
        }
    }

    public MutableTree getChild(String path) throws IllegalArgumentException {
        return getChild(NodeRef.split(path));
    }

    public MutableTree getChild(final List<String> path) throws IllegalArgumentException {
        Preconditions.checkArgument(!path.isEmpty());

        String directChildName = path.get(0);
        MutableTree child = childTrees.get(directChildName);
        if (child == null) {
            throw new IllegalArgumentException(String.format("No child named %s exists: %s",
                    directChildName, childTrees.keySet()));
        }
        if (path.size() == 1) {
            return child;
        }
        return child.getChild(path.subList(1, path.size()));
    }

    public SortedMap<String, MutableTree> getChildrenAsMap() {
        TreeMap<String, MutableTree> map = Maps.newTreeMap();
        asMap("", map);
        return map;
    }

    private void asMap(String parentPath, TreeMap<String, MutableTree> target) {
        for (MutableTree childTree : this.childTrees.values()) {
            String childTreePath = NodeRef.appendChild(parentPath, childTree.getNode().getName());
            target.put(childTreePath, childTree);
            childTree.asMap(childTreePath, target);
        }

    }

    @Nullable
    public MutableTree removeChild(String path) {
        ImmutableList<String> steps = NodeRef.split(path);
        MutableTree tree = this;

        for (Iterator<String> childNames = steps.iterator(); childNames.hasNext();) {
            String childName = childNames.next();
            MutableTree child = tree.childTrees.get(childName);
            if (child == null) {
                return null;
            }
            if (!childNames.hasNext()) {
                MutableTree removed = tree.childTrees.remove(childName);
                return removed;
            } else {
                tree = child;
            }
        }
        return null;
    }

    public void setNode(final Node newNode) {
        this.node = newNode;
    }

    public RevTree build(ObjectDatabase origin, ObjectDatabase target) {
        final ObjectId nodeId = node.getObjectId();
        final RevTree tree = origin.getTree(nodeId);

        RevTreeBuilder builder = tree.builder(target).clearSubtrees();

        for (MutableTree childTree : this.childTrees.values()) {
            String name;
            ObjectId newObjectId;
            ObjectId metadataId;
            Envelope bounds;
            {
                RevTree newChild = childTree.build(origin, target);
                target.put(newChild);
                Node oldNode = childTree.getNode();
                name = oldNode.getName();
                newObjectId = newChild.getId();
                metadataId = oldNode.getMetadataId().or(ObjectId.NULL);
                bounds = SpatialOps.boundsOf(newChild);
            }
            Node newNode = Node.create(name, newObjectId, metadataId, TYPE.TREE, bounds);
            builder.put(newNode);
        }
        RevTree newTree = builder.build();
        if (!this.node.getObjectId().equals(newTree.getId())) {
            target.put(newTree);
            Envelope bounds = SpatialOps.boundsOf(newTree);
            this.node = Node.create(node.getName(), newTree.getId(),
                    node.getMetadataId().or(ObjectId.NULL), TYPE.TREE, bounds);
        }

        return newTree;
    }

    @Override
    public MutableTree clone() {
        MutableTree clone = new MutableTree(node);
        for (MutableTree child : this.childTrees.values()) {
            clone.childTrees.put(child.getNode().getName(), child.clone());
        }
        return clone;
    }
}
TOP

Related Classes of org.locationtech.geogig.api.plumbing.diff.MutableTree

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.