Package org.tmatesoft.hg.internal

Source Code of org.tmatesoft.hg.internal.RevisionDescendants

/*
* Copyright (c) 2012-2013 TMate Software Ltd
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* For information on how to redistribute this software under
* the terms of a license other than GNU General Public License
* contact TMate Software at support@hg4j.com
*/
package org.tmatesoft.hg.internal;

import java.util.ArrayList;
import java.util.BitSet;

import org.tmatesoft.hg.core.Nodeid;
import org.tmatesoft.hg.repo.HgChangelog;
import org.tmatesoft.hg.repo.HgInvalidStateException;
import org.tmatesoft.hg.repo.HgRepository;
import org.tmatesoft.hg.repo.HgRuntimeException;

/**
* Represent indicators which revisions are descendants of the supplied root revision
* This is sort of lightweight alternative to ParentWalker#childrenOf
*
* @author Artem Tikhomirov
* @author TMate Software Ltd.
*/
public class RevisionDescendants {

  private final HgRepository repo;
  private final int rootRevIndex;
  private final int tipRevIndex; // this is the last revision we cache to
  private final BitSet descendants;
  private RevisionSet revset;

  // in fact, may be refactored to deal not only with changelog, but any revlog (not sure what would be the usecase, though)
  public RevisionDescendants(HgRepository hgRepo, int revisionIndex) throws HgRuntimeException {
    repo = hgRepo;
    rootRevIndex = revisionIndex;
    // even if tip moves, we still answer correctly for those isCandidate()
    tipRevIndex = repo.getChangelog().getLastRevision();
    if (revisionIndex < 0 || revisionIndex > tipRevIndex) {
      String m = "Revision to build descendants for shall be in range [%d,%d], not %d";
      throw new IllegalArgumentException(String.format(m, 0, tipRevIndex, revisionIndex));
    }
    descendants = new BitSet(tipRevIndex - rootRevIndex + 1);
  }
 
  public void build() throws HgRuntimeException {
    final BitSet result = descendants;
    result.set(0);
    if (rootRevIndex == tipRevIndex) {
      return;
    }
    repo.getChangelog().indexWalk(rootRevIndex+1, tipRevIndex, new HgChangelog.ParentInspector() {
      // TODO ParentRevisionInspector, with no parent nodeids, just indexes?

      private int i = 1; // above we start with revision next to rootRevIndex, which is at offset 0
      public void next(int revisionIndex, Nodeid revision, int parent1, int parent2, Nodeid nidParent1, Nodeid nidParent2) {
        int p1x = parent1 - rootRevIndex;
        int p2x = parent2 - rootRevIndex;
        boolean p1IsDescendant = false, p2IsDescendant = false;
        if (p1x >= 0) { // parent1 is among descendants candidates
          assert p1x < result.size();
          p1IsDescendant = result.get(p1x);
        }
        if (p2x >= 0) {
          assert p2x < result.size();
          p2IsDescendant = result.get(p2x);
        }
        //
        int rx = revisionIndex - rootRevIndex;
        if (rx != i) {
          throw new HgInvalidStateException(String.format("Sanity check failed. Revision %d. Expected:%d, was:%d", revisionIndex, rx, i));
        }
        // current revision is descendant if any of its parents is descendant
        result.set(rx, p1IsDescendant || p2IsDescendant);
        i++;
      }
    });
  }

  // deliberately doesn't allow TIP
  public boolean isCandidate(int revIndex) {
    return (revIndex >= rootRevIndex && revIndex <= tipRevIndex) ;
  }

  public boolean hasDescendants() { // isEmpty is better name?
    // bit at rootRevIndex is always set
    return descendants.nextSetBit(rootRevIndex+1) != -1;
  }

  /**
   * Tells whether specified revision is on a descent line from the root revision.
   * <p>NOTE, root revision itself is considered to be its own descendant.
   *
   * @param revisionIndex revision index to check, shall pass {@link #isCandidate(int)}
   * @return <code>true</code> if revision is descendant of or is the same as root revision
   */
  public boolean isDescendant(int revisionIndex) {
    assert isCandidate(revisionIndex);
    int ix = revisionIndex - rootRevIndex;
    assert ix < descendants.size();
    return descendants.get(ix);
  }

  public RevisionSet asRevisionSet() {
    if (revset == null) {
      final ArrayList<Nodeid> revisions = new ArrayList<Nodeid>(descendants.cardinality());
      repo.getChangelog().indexWalk(rootRevIndex, tipRevIndex, new HgChangelog.RevisionInspector() {

        public void next(int revisionIndex, Nodeid revision, int linkedRevisionIndex) throws HgRuntimeException {
          if (isDescendant(revisionIndex)) {
            revisions.add(revision);
          }
        }
      });
      assert revisions.size() == descendants.cardinality();
      revset = new RevisionSet(revisions);
    }
    return revset;
  }
}
TOP

Related Classes of org.tmatesoft.hg.internal.RevisionDescendants

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.