/* 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 static com.google.common.base.Preconditions.checkNotNull; import org.locationtech.geogig.api.AbstractGeoGigOp; 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.storage.ObjectDatabase; import org.locationtech.geogig.storage.StagingDatabase; import com.google.common.base.Optional; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; /** * Finds the subtree of the given tree named after the indicated path, or creates a new one. If a * new one is created, it is still not saved. * <p> * If a child tree of {@code parent} addressed by the given {@code childPath} exists, returns it's * mutable copy, otherwise just returns a new mutable tree without any modification to root or any * intermediate tree between root and the requested tree path. * * @see RevTree */ public class FindOrCreateSubtree extends AbstractGeoGigOp<RevTree> { private Supplier<Optional<RevTree>> parentSupplier; private String childPath; private boolean indexDb; private String parentPath; /** * @param parent a supplier that resolves to the parent tree where to start the search for the * subtree from * @return {@code this} */ public FindOrCreateSubtree setParent(Supplier<Optional<RevTree>> parent) { this.parentSupplier = parent; return this; } public FindOrCreateSubtree setParent(RevTree parent) { this.parentSupplier = Suppliers.ofInstance(Optional.of(parent)); return this; } /** * @param parentPath the parent's path. If not given parent is assumed to be a root tree. * @return {@code this} */ public FindOrCreateSubtree setParentPath(String parentPath) { this.parentPath = parentPath; return this; } /** * @param subtreePath the full path of the subtree to look for * @return {@code this} */ public FindOrCreateSubtree setChildPath(String subtreePath) { this.childPath = subtreePath; return this; } /** * @param indexDb whether to look up in the {@link StagingDatabase index db} ({@code true}) or * on the repository's {@link ObjectDatabase object database} (default) * @return {@code this} */ public FindOrCreateSubtree setIndex(boolean indexDb) { this.indexDb = indexDb; return this; } /** * Executes the command. * * @return the subtree if it was found, or a new one if it wasn't */ @Override protected RevTree _call() { checkNotNull(parentSupplier, "parent"); checkNotNull(childPath, "childPath"); ObjectId subtreeId; if (parentSupplier.get().isPresent()) { RevTree parent = parentSupplier.get().get(); Optional<NodeRef> treeChildRef = command(FindTreeChild.class).setIndex(indexDb) .setParentPath(parentPath).setChildPath(childPath) .setParent(Suppliers.ofInstance(parent)).call(); if (treeChildRef.isPresent()) { NodeRef treeRef = treeChildRef.get(); if (!TYPE.TREE.equals(treeRef.getType())) { throw new IllegalArgumentException("Object exists as child of tree " + parent.getId() + " but is not a tree: " + treeChildRef); } subtreeId = treeRef.objectId(); } else { subtreeId = RevTree.EMPTY_TREE_ID; } } else { subtreeId = RevTree.EMPTY_TREE_ID; } if (RevTree.EMPTY_TREE_ID.equals(subtreeId)) { return RevTree.EMPTY; } ObjectDatabase target = indexDb ? stagingDatabase() : objectDatabase(); RevTree tree = target.getTree(subtreeId); return tree; } }