org.locationtech.geogig.api.plumbing.DeepCopy.java Source code

Java tutorial

Introduction

Here is the source code for org.locationtech.geogig.api.plumbing.DeepCopy.java

Source

/* Copyright (c) 2012-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;

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

import org.locationtech.geogig.api.AbstractGeoGigOp;
import org.locationtech.geogig.api.Bucket;
import org.locationtech.geogig.api.Node;
import org.locationtech.geogig.api.NodeRef;
import org.locationtech.geogig.api.ObjectId;
import org.locationtech.geogig.api.RevObject;
import org.locationtech.geogig.api.RevObject.TYPE;
import org.locationtech.geogig.api.RevTree;
import org.locationtech.geogig.api.plumbing.LsTreeOp.Strategy;
import org.locationtech.geogig.storage.ObjectDatabase;

import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.AbstractIterator;
import com.google.common.collect.Iterators;

/**
 * Copies the {@link #setObjectRef(Supplier) specified object} from the
 * {@link #setFrom(ObjectDatabase) provided} object database this command's repository
 * {@link ObjectDatabase object database}, including any child reference.
 */
public class DeepCopy extends AbstractGeoGigOp<ObjectId> {

    private Supplier<Node> objectRef;

    private Supplier<ObjectId> objectId;

    private Supplier<Iterator<Node>> nodesToCopy;

    private ObjectDatabase from;

    /**
     * @param id the id of the object to move, mutually exclusive with
     *        {@link #setObjectRef(Supplier)}
     * @return
     */
    public DeepCopy setObject(Supplier<ObjectId> id) {
        this.objectId = id;
        this.objectRef = null;
        this.nodesToCopy = 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 DeepCopy setObjectRef(Supplier<Node> objectRef) {
        this.objectRef = objectRef;
        this.objectId = null;
        this.nodesToCopy = null;
        return this;
    }

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

    /**
     * Executes a deep copy 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
    protected ObjectId _call() {
        Preconditions.checkState(from != null, "No from databse specified");
        ObjectDatabase from = this.from;
        ObjectDatabase to = objectDatabase();

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

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

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

        return ret;
    }

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

        Iterable<ObjectId> ids = new Iterable<ObjectId>() {

            final Function<Node, ObjectId> asId = new Function<Node, ObjectId>() {
                @Override
                public ObjectId apply(Node input) {
                    Optional<ObjectId> metadataId = input.getMetadataId();
                    if (metadataId.isPresent()) {
                        metadataIds.add(input.getMetadataId().get());
                    }
                    ObjectId id = input.getObjectId();
                    return id;
                }
            };

            @Override
            public Iterator<ObjectId> iterator() {
                Iterator<Node> iterator = nodesToMove.get();
                Iterator<ObjectId> ids = Iterators.transform(iterator, asId);

                return ids;
            }
        };

        // store objects into the target db and remove them from the origin db in one shot
        to.putAll(from.getAll(ids));
    }

    /**
     * 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 deepCopy(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())) {
            copyTree(objectId, from, to, metadataIds);
        } else {
            copyObject(objectId, from, to);
        }
    }

    private void copyTree(final ObjectId treeId, final ObjectDatabase from, final ObjectDatabase to,
            final Set<ObjectId> metadataIds) {
        if (to.exists(treeId)) {
            return;
        }
        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
        copyObjects(from, to, nodes, metadataIds);

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

        to.putAll(allSubtreesAndBuckets);
    }

    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 copyObject(RevObject object, ObjectDatabase from, ObjectDatabase to) {
        to.put(object);
    }

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

        RevObject object = from.get(objectId);

        if (object instanceof RevTree) {
            Set<ObjectId> metadataIds = new HashSet<ObjectId>();
            copyTree(object.getId(), from, to, metadataIds);
            for (ObjectId metadataId : metadataIds) {
                copyObject(metadataId, from, to);
            }
        } else {
            copyObject(object, from, to);
        }
    }

    public DeepCopy setFrom(ObjectDatabase odb) {
        this.from = odb;
        return this;
    }

}