org.geogit.api.plumbing.DeepMove.java Source code

Java tutorial

Introduction

Here is the source code for org.geogit.api.plumbing.DeepMove.java

Source

/* Copyright (c) 2013 OpenPlans. All rights reserved.
 * This code is licensed under the BSD New License, available at the root
 * application directory.
 */

package org.geogit.api.plumbing;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.geogit.api.AbstractGeoGitOp;
import org.geogit.api.Bucket;
import org.geogit.api.Node;
import org.geogit.api.NodeRef;
import org.geogit.api.ObjectId;
import org.geogit.api.RevObject;
import org.geogit.api.RevObject.TYPE;
import org.geogit.api.RevTree;
import org.geogit.api.plumbing.LsTreeOp.Strategy;
import org.geogit.repository.StagingArea;
import org.geogit.storage.ObjectDatabase;
import org.geogit.storage.StagingDatabase;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.AbstractIterator;
import com.google.common.collect.Iterators;
import com.google.common.collect.Sets;
import com.google.inject.Inject;

/**
 * Moves the {@link #setObjectRef(Supplier) specified object} from the {@link StagingArea index
 * database} to the permanent {@link ObjectDatabase object database}, including any child reference,
 * or from the repository database to the index database if {@link #setToIndex} is set to
 * {@code true}.
 */
public class DeepMove extends AbstractGeoGitOp<ObjectId> {

    private boolean toIndex;

    private ObjectDatabase odb;

    private StagingDatabase index;

    private Supplier<Node> objectRef;

    private Supplier<ObjectId> objectId;

    private Supplier<Iterator<Node>> nodesToMove;

    /**
     * Constructs a new instance of the {@code DeepMove} operation with the specified parameters.
     * 
     * @param odb the repository object database
     * @param index the staging database
     */
    @Inject
    public DeepMove(ObjectDatabase odb, StagingDatabase index) {
        this.odb = odb;
        this.index = index;
    }

    /**
     * @param toIndex if {@code true} moves the object from the repository's object database to the
     *        index database instead
     * @return {@code this}
     */
    public DeepMove setToIndex(boolean toIndex) {
        this.toIndex = toIndex;
        return this;
    }

    /**
     * @param id the id of the object to move, mutually exclusive with
     *        {@link #setObjectRef(Supplier)}
     * @return
     */
    public DeepMove setObject(Supplier<ObjectId> id) {
        this.objectId = id;
        this.objectRef = null;
        this.nodesToMove = null;
        return this;
    }

    /**
     * @param objectRef the object to move from the origin database to the destination one, mutually
     *        exclusive with {@link #setObject(Supplier)}
     * @return {@code this}
     */
    public DeepMove setObjectRef(Supplier<Node> objectRef) {
        this.objectRef = objectRef;
        this.objectId = null;
        this.nodesToMove = null;
        return this;
    }

    public DeepMove setObjects(Supplier<Iterator<Node>> nodesToMove) {
        this.nodesToMove = nodesToMove;
        this.objectId = null;
        this.objectRef = null;
        return this;
    }

    /**
     * Executes a deep move using the supplied {@link Node}.
     * 
     * @return the {@link ObjectId} of the moved object, or {@code null} if {@link #setObjects} was
     *         used and hence no such information it available
     */
    @Override
    public ObjectId call() {
        ObjectDatabase from = toIndex ? odb : index;
        ObjectDatabase to = toIndex ? index : odb;

        Set<ObjectId> metadataIds = new HashSet<ObjectId>();

        final ObjectId ret;
        if (objectRef != null) {
            Node ref = objectRef.get();
            ret = ref.getObjectId();
            deepMove(ref, from, to, metadataIds);
        } else if (objectId != null) {
            ObjectId id = objectId.get();
            moveObject(id, from, to);
            ret = id;
        } else if (nodesToMove != null) {
            moveObjects(from, to, nodesToMove, metadataIds);
            ret = null;
        } else {
            throw new IllegalStateException("No object supplied to be moved");
        }

        for (ObjectId metadataId : metadataIds) {
            moveObject(metadataId, from, to);
        }

        return ret;
    }

    private void moveObjects(final ObjectDatabase from, final ObjectDatabase to,
            Supplier<Iterator<Node>> nodesToMove, final Set<ObjectId> metadataIds) {

        Function<Node, ObjectId> asIds = new Function<Node, ObjectId>() {
            @Override
            public ObjectId apply(Node input) {
                return input.getObjectId();
            }
        };
        Function<Node, RevObject> asObjects = new Function<Node, RevObject>() {
            @Override
            public RevObject apply(Node input) {
                if (input.getMetadataId().isPresent()) {
                    metadataIds.add(input.getMetadataId().get());
                }
                return from.get(input.getObjectId());
            }
        };

        Iterator<Node> iterator;
        iterator = nodesToMove.get();
        if (iterator.hasNext()) {
            to.putAll(Iterators.transform(iterator, asObjects));

            iterator = nodesToMove.get();
            from.deleteAll(Iterators.transform(iterator, asIds));
        }
    }

    /**
     * Transfers the object referenced by {@code objectRef} from the given object database to the
     * given objectInserter as well as any child object if {@code objectRef} references a tree.
     */
    private void deepMove(final Node objectRef, final ObjectDatabase from, final ObjectDatabase to,
            Set<ObjectId> metadataIds) {

        if (objectRef.getMetadataId().isPresent()) {
            metadataIds.add(objectRef.getMetadataId().get());
        }

        final ObjectId objectId = objectRef.getObjectId();
        if (TYPE.TREE.equals(objectRef.getType())) {
            moveTree(objectId, from, to, metadataIds);
        } else {
            moveObject(objectId, from, to);
        }
    }

    private void moveTree(final ObjectId treeId, final ObjectDatabase from, final ObjectDatabase to,
            final Set<ObjectId> metadataIds) {

        Supplier<Iterator<NodeRef>> refs = command(LsTreeOp.class).setReference(treeId.toString())
                .setStrategy(Strategy.DEPTHFIRST_ONLY_FEATURES);

        Supplier<Iterator<Node>> nodes = Suppliers.compose(new Function<Iterator<NodeRef>, Iterator<Node>>() {

            @Override
            public Iterator<Node> apply(Iterator<NodeRef> input) {
                return Iterators.transform(input, new Function<NodeRef, Node>() {
                    @Override
                    public Node apply(NodeRef input) {
                        return input.getNode();
                    }
                });
            }
        }, refs);

        // move all features, recursively as given by the LsTreeOp strategy
        moveObjects(from, to, nodes, metadataIds);

        // collect all subtree and bucket ids here to delete them from the origin db
        final Set<ObjectId> alltreeIds = Sets.newTreeSet();
        Predicate<RevTree> collectIds = new Predicate<RevTree>() {
            @Override
            public boolean apply(RevTree input) {
                alltreeIds.add(input.getId());
                return true;
            }
        };

        // iterator that traverses the tree,all its subtrees, an bucket trees
        Iterator<RevTree> allSubtreesAndBuckets = new AllTrees(treeId, from);
        allSubtreesAndBuckets = Iterators.filter(allSubtreesAndBuckets, collectIds);

        to.putAll(allSubtreesAndBuckets);
        from.deleteAll(alltreeIds.iterator());
    }

    private static class AllTrees extends AbstractIterator<RevTree> {

        private RevTree tree;

        private ObjectDatabase from;

        private Iterator<Node> trees;

        private Iterator<Bucket> buckets;

        private Iterator<RevTree> bucketTrees;

        public AllTrees(ObjectId id, ObjectDatabase from) {
            this.from = from;
            this.tree = from.getTree(id);
            this.trees = Iterators.emptyIterator();
            if (tree.trees().isPresent()) {
                trees = tree.trees().get().iterator();
            }
            buckets = Iterators.emptyIterator();
            if (tree.buckets().isPresent()) {
                buckets = tree.buckets().get().values().iterator();
            }
            bucketTrees = Iterators.emptyIterator();
        }

        @Override
        protected RevTree computeNext() {
            if (tree != null) {
                RevTree ret = tree;
                tree = null;
                return ret;
            }
            if (trees.hasNext()) {
                ObjectId objectId = trees.next().getObjectId();
                return from.getTree(objectId);
            }
            if (bucketTrees.hasNext()) {
                return bucketTrees.next();
            }
            if (buckets.hasNext()) {
                bucketTrees = new AllTrees(buckets.next().id(), from);
                return computeNext();
            }
            return endOfData();
        }

    }

    private void moveObject(RevObject object, ObjectDatabase from, ObjectDatabase to) {
        to.put(object);
        from.delete(object.getId());
    }

    private void moveObject(final ObjectId objectId, final ObjectDatabase from, final ObjectDatabase to) {

        RevObject object = from.get(objectId);
        moveObject(object, from, to);

    }

}