Package org.tmatesoft.hg.core

Source Code of org.tmatesoft.hg.core.HgFileRevision

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

import org.tmatesoft.hg.repo.HgDataFile;
import org.tmatesoft.hg.repo.HgManifest;
import org.tmatesoft.hg.repo.HgManifest.Flags;
import org.tmatesoft.hg.repo.HgRepository;
import org.tmatesoft.hg.repo.HgRuntimeException;
import org.tmatesoft.hg.util.ByteChannel;
import org.tmatesoft.hg.util.CancelledException;
import org.tmatesoft.hg.util.Pair;
import org.tmatesoft.hg.util.Path;

/**
* Keeps together information about specific file revision
*
* @author Artem Tikhomirov
* @author TMate Software Ltd.
*/
public final class HgFileRevision {
  private final HgRepository repo;
  private final Nodeid revision;
  private final Path path;
  private Path origin;
  private Boolean isCopy = null; // null means not yet known
  private Pair<Nodeid, Nodeid> parents;
  private Flags flags; // null unless set/extracted

  /**
   * New description of a file revision from a specific repository.
   *
   * <p>Although this constructor is public, and clients can use it to construct own file revisions to pass e.g. to commands, its use is discouraged. 
   *
   * @param hgRepo repository
   * @param rev file revision
   * @param manifestEntryFlags file flags at this revision (optional, may be null)
   * @param p path of the file at the given revision
   */
  public HgFileRevision(HgRepository hgRepo, Nodeid rev, HgManifest.Flags manifestEntryFlags, Path p) {
    if (hgRepo == null || rev == null || p == null) {
      // since it's package local, it is our code to blame for non validated arguments
      throw new IllegalArgumentException();
    }
    repo = hgRepo;
    revision = rev;
    flags = manifestEntryFlags;
    path = p;
  }

  // this cons shall be used when we know whether p was a copy. Perhaps, shall pass Map<Path,Path> instead to stress orig argument is not optional 
  HgFileRevision(HgRepository hgRepo, Nodeid rev, HgManifest.Flags flags, Path p, Path orig) {
    this(hgRepo, rev, flags, p);
    isCopy = Boolean.valueOf(orig == null);
    origin = orig;
  }
 
  HgFileRevision(HgDataFile fileNode, Nodeid fileRevision, Path origin) {
    this(fileNode.getRepo(), fileRevision, null, fileNode.getPath(), origin);
  }
 
  public Path getPath() {
    return path;
  }

  /**
   * Revision of the file
   * @return never <code>null</code>
   */
  public Nodeid getRevision() {
    return revision;
  }
 
  /**
   * Extract flags of the file as recorded in the manifest for this file revision
   * @return whether regular file, executable or a symbolic link
   * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em>
   */
  public HgManifest.Flags getFileFlags() throws HgRuntimeException {
    if (flags == null) {
      /*
       * Note, for uses other than HgManifestCommand or HgChangesetFileSneaker, when no flags come through the cons,
       * it's possible to face next shortcoming:
       * Imagine csetA and csetB, with corresponding manifestA and manifestB, the file didn't change (revision/nodeid is the same)
       * but flag of the file has changed (e.g. became executable). Since HgFileRevision doesn't keep reference to
       * an actual manifest revision, but only file's, and it's likely the flags returned from this method would
       * yield result as from manifestA (i.e. no flag change in manifestB ever noticed).
       */
      HgDataFile df = repo.getFileNode(path);
      int revIdx = df.getRevisionIndex(revision);
      flags = df.getFlags(revIdx);
    }
    return flags;
  }

  /**
   * @return <code>true</code> if this file revision was created as a result of a copy/rename
   * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em>
   */
  public boolean wasCopied() throws HgRuntimeException {
    if (isCopy == null) {
      checkCopy();
    }
    return isCopy.booleanValue();
  }
  /**
   * @return <code>null</code> if {@link #wasCopied()} is <code>false</code>, name of the copy source otherwise.
   * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em>
   */
  public Path getOriginIfCopy() throws HgRuntimeException {
    if (wasCopied()) {
      return origin;
    }
    return null;
  }

  /**
   * Access revisions this file revision originates from.
   * Note, these revisions are records in the file history, not that of the whole repository (aka changeset revisions)
   * In most cases, only one parent revision would be present, only for merge revisions one can expect both.
   *
   * @return parent revisions of this file revision, with {@link Nodeid#NULL} for missing values.
   * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em>
   */
  public Pair<Nodeid, Nodeid> getParents() throws HgRuntimeException {
    if (parents == null) {
      HgDataFile fn = repo.getFileNode(path);
      int revisionIndex = fn.getRevisionIndex(revision);
      int[] pr = new int[2];
      byte[] p1 = new byte[20], p2 = new byte[20];
      // XXX Revlog#parents is not the best method to use here
      // need smth that gives Nodeids (piped through Pool<Nodeid> from repo's context)
      fn.parents(revisionIndex, pr, p1, p2);
      parents = new Pair<Nodeid, Nodeid>(Nodeid.fromBinary(p1, 0), Nodeid.fromBinary(p2, 0));
    }
    return parents;
  }

  /**
   * Pipe content of this file revision into the sink
   * @param sink accepts file revision content
   * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em>
   * @throws CancelledException if execution of the operation was cancelled
   */
  public void putContentTo(ByteChannel sink) throws HgRuntimeException, CancelledException {
    HgDataFile fn = repo.getFileNode(path);
    int revisionIndex = fn.getRevisionIndex(revision);
    fn.contentWithFilters(revisionIndex, sink);
  }
 
  @Override
  public String toString() {
    return String.format("HgFileRevision(%s, %s)", getPath().toString(), revision.shortNotation());
  }

  private void checkCopy() throws HgRuntimeException {
    HgDataFile df = repo.getFileNode(path);
    int revIdx = df.getRevisionIndex(revision);
    if (df.isCopy(revIdx)) {
      isCopy = Boolean.TRUE;
      origin = df.getCopySource(revIdx).getPath();
      return;
    }
    isCopy = Boolean.FALSE;
  }
}
TOP

Related Classes of org.tmatesoft.hg.core.HgFileRevision

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.