tikhomirov@432: /* tikhomirov@628: * Copyright (c) 2011-2013 TMate Software Ltd tikhomirov@432: * tikhomirov@432: * This program is free software; you can redistribute it and/or modify tikhomirov@432: * it under the terms of the GNU General Public License as published by tikhomirov@432: * the Free Software Foundation; version 2 of the License. tikhomirov@432: * tikhomirov@432: * This program is distributed in the hope that it will be useful, tikhomirov@432: * but WITHOUT ANY WARRANTY; without even the implied warranty of tikhomirov@432: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the tikhomirov@432: * GNU General Public License for more details. tikhomirov@432: * tikhomirov@432: * For information on how to redistribute this software under tikhomirov@432: * the terms of a license other than GNU General Public License tikhomirov@432: * contact TMate Software at support@hg4j.com tikhomirov@432: */ tikhomirov@432: package org.tmatesoft.hg.repo; tikhomirov@432: tikhomirov@432: import static org.tmatesoft.hg.repo.HgRepository.TIP; tikhomirov@432: tikhomirov@432: import java.util.Arrays; tikhomirov@432: import java.util.Collection; tikhomirov@432: import java.util.HashSet; tikhomirov@432: import java.util.LinkedList; tikhomirov@432: import java.util.List; tikhomirov@432: tikhomirov@432: import org.tmatesoft.hg.core.Nodeid; tikhomirov@432: import org.tmatesoft.hg.repo.Revlog.ParentInspector; tikhomirov@432: tikhomirov@432: /** tikhomirov@432: * Helper class to deal with parent-child relationship between revisions en masse. tikhomirov@432: * Works in terms of {@link Nodeid nodeids}, there's no need to deal with revision indexes. tikhomirov@432: * For a given revision, answers questions like "who's my parent and what are my immediate children". tikhomirov@432: * tikhomirov@432: *
Comes handy when multiple revisions are analyzed and distinct {@link Revlog#parents(int, int[], byte[], byte[])} tikhomirov@432: * queries are ineffective. tikhomirov@432: * tikhomirov@432: *
Next code snippet shows typical use: tikhomirov@432: *
tikhomirov@432: * HgChangelog clog = repo.getChangelog(); tikhomirov@432: * ParentWalker<HgChangelog> pw = new ParentWalker<HgChangelog>(clog); tikhomirov@432: * pw.init(); tikhomirov@432: * tikhomirov@432: * Nodeid me = Nodeid.fromAscii("..."); tikhomirov@432: * Listtikhomirov@432: * tikhomirov@432: * tikhomirov@432: *immediateChildren = pw.directChildren(me); tikhomirov@432: *
Perhaps, later may add alternative way to access (and reuse) map instance, Revlog#getParentWalker(),
tikhomirov@432: * that instantiates and initializes ParentWalker, and keep SoftReference to allow its reuse.
tikhomirov@432: *
tikhomirov@433: * @see HgRevisionMap
tikhomirov@432: *
tikhomirov@432: * @author Artem Tikhomirov
tikhomirov@432: * @author TMate Software Ltd.
tikhomirov@432: */
tikhomirov@432: public final class HgParentChildMapnull
tikhomirov@432: * @return true
if revision matches any revision in this revlog
tikhomirov@432: */
tikhomirov@432: public boolean knownNode(Nodeid nid) {
tikhomirov@432: return Arrays.binarySearch(sorted, nid) >= 0;
tikhomirov@432: }
tikhomirov@432:
tikhomirov@432: /**
tikhomirov@432: * null if none. only known nodes (as per #knownNode) are accepted as arguments
tikhomirov@432: */
tikhomirov@432: public Nodeid firstParent(Nodeid nid) {
tikhomirov@432: int x = Arrays.binarySearch(sorted, nid);
tikhomirov@432: assertSortedIndex(x);
tikhomirov@432: int i = sorted2natural[x];
tikhomirov@432: return firstParent[i];
tikhomirov@432: }
tikhomirov@432:
tikhomirov@432: // never null, Nodeid.NULL if none known
tikhomirov@432: public Nodeid safeFirstParent(Nodeid nid) {
tikhomirov@432: Nodeid rv = firstParent(nid);
tikhomirov@432: return rv == null ? Nodeid.NULL : rv;
tikhomirov@432: }
tikhomirov@432:
tikhomirov@432: public Nodeid secondParent(Nodeid nid) {
tikhomirov@432: int x = Arrays.binarySearch(sorted, nid);
tikhomirov@432: assertSortedIndex(x);
tikhomirov@432: int i = sorted2natural[x];
tikhomirov@432: return secondParent[i];
tikhomirov@432: }
tikhomirov@432:
tikhomirov@432: public Nodeid safeSecondParent(Nodeid nid) {
tikhomirov@432: Nodeid rv = secondParent(nid);
tikhomirov@432: return rv == null ? Nodeid.NULL : rv;
tikhomirov@432: }
tikhomirov@432:
tikhomirov@432: public boolean appendParentsOf(Nodeid nid, Collectiontrue
if there's any node in this revlog that has specified node as one of its parents.
tikhomirov@432: */
tikhomirov@432: public boolean hasChildren(Nodeid nid) {
tikhomirov@432: int x = Arrays.binarySearch(sorted, nid);
tikhomirov@432: assertSortedIndex(x);
tikhomirov@432: int i = sorted2natural[x];
tikhomirov@432: assert firstParent.length == secondParent.length; // just in case later I implement sparse array for secondParent
tikhomirov@432: assert firstParent.length == sequential.length;
tikhomirov@432: // to use == instead of equals, take the same Nodeid instance we used to fill all the arrays.
tikhomirov@432: final Nodeid canonicalNode = sequential[i];
tikhomirov@432: i++; // no need to check node itself. child nodes may appear in sequential only after revision in question
tikhomirov@432: for (; i < sequential.length; i++) {
tikhomirov@432: // TODO [post 1.0] likely, not very effective.
tikhomirov@432: // May want to optimize it with another (Tree|Hash)Set, created on demand on first use,
tikhomirov@432: // however, need to be careful with memory usage
tikhomirov@432: if (firstParent[i] == canonicalNode || secondParent[i] == canonicalNode) {
tikhomirov@432: return true;
tikhomirov@432: }
tikhomirov@432: }
tikhomirov@432: return false;
tikhomirov@432: }
tikhomirov@659:
tikhomirov@659: /**
tikhomirov@659: * Find out whether a given node is among descendants of another.
tikhomirov@659: *
tikhomirov@659: * @param root revision to check for being (grand-)*parent of a child
tikhomirov@659: * @param wannaBeChild candidate descendant revision
tikhomirov@659: * @return true
if wannaBeChild
is among children of root
tikhomirov@659: */
tikhomirov@659: public boolean isChild(Nodeid root, Nodeid wannaBeChild) {
tikhomirov@659: int x = Arrays.binarySearch(sorted, root);
tikhomirov@659: assertSortedIndex(x);
tikhomirov@659: root = sorted[x]; // canonical instance
tikhomirov@659: final int start = sorted2natural[x];
tikhomirov@659: int y = Arrays.binarySearch(sorted, wannaBeChild);
tikhomirov@659: if (y < 0) {
tikhomirov@659: return false; // not found
tikhomirov@659: }
tikhomirov@659: wannaBeChild = sorted[y]; // canonicalize
tikhomirov@659: final int end = sorted2natural[y];
tikhomirov@659: if (end <= start) {
tikhomirov@659: return false; // potential child was in repository earlier than root
tikhomirov@659: }
tikhomirov@659: HashSet