Java tutorial
/* 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.diff; import static org.geogit.api.NodeRef.ROOT; import static org.geogit.api.NodeRef.depth; import static org.geogit.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.geogit.api.Node; import org.geogit.api.NodeRef; import org.geogit.api.ObjectId; import org.geogit.api.RevObject.TYPE; import org.geogit.api.RevTree; import org.geogit.api.RevTreeBuilder; import org.geogit.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; /** * 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, ObjectId.NULL, 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); MutableTree root = new MutableTree(Node.create(ROOT, rootId, ObjectId.NULL, TYPE.TREE)); for (NodeRef entry : refsByDepth) { Node node = entry.getNode(); String parentPath = entry.getParentPath(); root.setChild(parentPath, node); } 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) { RevTree tree = RevTree.EMPTY; if (!node.getObjectId().isNull()) { tree = origin.getTree(node.getObjectId()); } RevTreeBuilder builder = tree.builder(target).clearSubtrees(); for (MutableTree childTree : this.childTrees.values()) { RevTree newChild = childTree.build(origin, target); target.put(newChild); Node oldNode = childTree.getNode(); String name = oldNode.getName(); ObjectId newObjectId = newChild.getId(); ObjectId metadataId = oldNode.getMetadataId().or(ObjectId.NULL); Node newNode = Node.create(name, newObjectId, metadataId, TYPE.TREE); builder.put(newNode); } RevTree newTree = builder.build(); if (!this.node.getObjectId().equals(newTree.getId())) { target.put(newTree); this.node = Node.create(node.getName(), newTree.getId(), node.getMetadataId().or(ObjectId.NULL), TYPE.TREE); } 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; } }