Package org.tmatesoft.hg.core

Source Code of org.tmatesoft.hg.core.HgManifestCommand$Mediator

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

import static org.tmatesoft.hg.repo.HgRepository.*;

import java.util.ConcurrentModificationException;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;

import org.tmatesoft.hg.internal.CsetParamKeeper;
import org.tmatesoft.hg.internal.PathPool;
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.CancelSupport;
import org.tmatesoft.hg.util.CancelledException;
import org.tmatesoft.hg.util.Path;
import org.tmatesoft.hg.util.PathRewrite;


/**
* Gives access to list of files in each revision (Mercurial manifest information), 'hg manifest' counterpart.
* @author Artem Tikhomirov
* @author TMate Software Ltd.
*/
public class HgManifestCommand extends HgAbstractCommand<HgManifestCommand> {
 
  private final HgRepository repo;
  private Path.Matcher matcher;
  private int startRev = 0, endRev = TIP;
  private HgManifestHandler visitor;
  private boolean needDirs = false;
 
  private final Mediator mediator = new Mediator();

  public HgManifestCommand(HgRepository hgRepo) {
    repo = hgRepo;
  }

  /**
   * Parameterize command to visit revisions <code>[rev1..rev2]</code>.
   * @param rev1 - revision local index to start from. Non-negative. May be {@link HgRepository#TIP} (rev2 argument shall be {@link HgRepository#TIP} as well, then)
   * @param rev2 - revision local index to end with, inclusive. Non-negative, greater or equal to rev1. May be {@link HgRepository#TIP}.
   * @return <code>this</code> for convenience.
   * @throws IllegalArgumentException if revision arguments are incorrect (see above).
   */
  public HgManifestCommand range(int rev1, int rev2) {
    // XXX if manifest range is different from that of changelog, need conversion utils (external?)
    boolean badArgs = rev1 == BAD_REVISION || rev2 == BAD_REVISION || rev1 == WORKING_COPY || rev2 == WORKING_COPY;
    badArgs |= rev2 != TIP && rev2 < rev1; // range(3, 1);
    badArgs |= rev1 == TIP && rev2 != TIP; // range(TIP, 2), although this may be legitimate when TIP points to 2
    if (badArgs) {
      // TODO [2.0 API break] throw checked HgBadArgumentException instead
      throw new IllegalArgumentException(String.format("Bad range: [%d, %d]", rev1, rev2));
    }
    startRev = rev1;
    endRev = rev2;
    return this;
  }
 
  /**
   * Limit command to visit specific subset of repository revisions
   *
   * @see #range(int, int)
   * @param cset1 range start revision
   * @param cset2 range end revision
   * @return <code>this</code> instance for convenience
   * @throws HgBadArgumentException if revisions are not valid changeset identifiers
   */
  public HgManifestCommand range(Nodeid cset1, Nodeid cset2) throws HgBadArgumentException {
    CsetParamKeeper pk = new CsetParamKeeper(repo);
    int r1 = pk.set(cset1).get();
    int r2 = pk.set(cset2).get();
    return range(r1, r2);
  }
 
  /**
   * Select changeset for the command using revision index
   * @param csetRevisionIndex index of changeset revision
   * @return <code>this</code> for convenience.
   */
  public HgManifestCommand changeset(int csetRevisionIndex) {
    // TODO [2.0 API break] shall throw HgBadArgumentException, like other commands do
    return range(csetRevisionIndex, csetRevisionIndex);
  }
 
  /**
   * Select changeset for the command
   *
   * @param nid changeset revision
   * @return <code>this</code> for convenience
   * @throws HgBadArgumentException if failed to find supplied changeset revision
   */
  public HgManifestCommand changeset(Nodeid nid) throws HgBadArgumentException {
    // XXX also see HgLogCommand#changeset(Nodeid)
    final int csetRevIndex = new CsetParamKeeper(repo).set(nid).get();
    return range(csetRevIndex, csetRevIndex);
  }

  public HgManifestCommand dirs(boolean include) {
    // XXX whether directories with directories only are include or not
    // now lists only directories with files
    needDirs = include;
    return this;
  }
 
  /**
   * Limit manifest walk to a subset of files.
   * @param pathMatcher - filter, pass <code>null</code> to clear.
   * @return <code>this</code> instance for convenience
   */
  public HgManifestCommand match(Path.Matcher pathMatcher) {
    matcher = pathMatcher;
    return this;
  }
 
  /**
   * With all parameters set, execute the command.
   *
   * @param handler - callback to get the outcome
    * @throws HgCallbackTargetException propagated exception from the handler
   * @throws HgException subclass thereof to indicate specific issue with the command arguments or repository state
   * @throws CancelledException if execution of the command was cancelled
   * @throws IllegalArgumentException if handler is <code>null</code>
   * @throws ConcurrentModificationException if this command is already in use (running)
   */
  public void execute(HgManifestHandler handler) throws HgCallbackTargetException, HgException, CancelledException {
    if (handler == null) {
      throw new IllegalArgumentException();
    }
    if (visitor != null) {
      throw new ConcurrentModificationException();
    }
    try {
      visitor = handler;
      mediator.start(getCancelSupport(handler, true));
      repo.getManifest().walk(startRev, endRev, mediator);
      mediator.checkFailure();
    } catch (HgRuntimeException ex) {
      throw new HgLibraryFailureException(ex);
    } finally {
      mediator.done();
      visitor = null;
    }
  }

  // I'd rather let HgManifestCommand implement HgManifest.Inspector directly, but this pollutes API alot
  private class Mediator implements HgManifest.Inspector {
    // file names are likely to repeat in each revision, hence caching of Paths.
    // However, once HgManifest.Inspector switches to Path objects, perhaps global Path pool
    // might be more effective?
    private PathPool pathPool;
    private List<HgFileRevision> manifestContent;
    private Nodeid manifestNodeid;
    private Exception failure;
    private CancelSupport cancelHelper;
   
    public void start(CancelSupport cs) {
      assert cs != null;
      // Manifest keeps normalized paths
      pathPool = new PathPool(new PathRewrite.Empty());
      cancelHelper = cs;
    }
   
    public void done() {
      manifestContent = null;
      pathPool = null;
    }
   
    private void recordFailure(HgCallbackTargetException ex) {
      failure = ex;
    }
    private void recordCancel(CancelledException ex) {
      failure = ex;
    }

    public void checkFailure() throws HgCallbackTargetException, CancelledException {
      // TODO post-1.0 perhaps, can combine this code (record/checkFailure) for reuse in more classes (e.g. in Revlog)
      if (failure instanceof HgCallbackTargetException) {
        HgCallbackTargetException ex = (HgCallbackTargetException) failure;
        failure = null;
        throw ex;
      }
      if (failure instanceof CancelledException) {
        CancelledException ex = (CancelledException) failure;
        failure = null;
        throw ex;
      }
    }
 
    public boolean begin(int manifestRevision, Nodeid nid, int changelogRevision) throws HgRuntimeException {
      if (needDirs && manifestContent == null) {
        manifestContent = new LinkedList<HgFileRevision>();
      }
      try {
        visitor.begin(manifestNodeid = nid);
        cancelHelper.checkCancelled();
        return true;
      } catch (HgCallbackTargetException ex) {
        recordFailure(ex);
        return false;
      } catch (CancelledException ex) {
        recordCancel(ex);
        return false;
      }
    }
    public boolean end(int revision) throws HgRuntimeException {
      try {
        if (needDirs) {
          LinkedHashMap<Path, LinkedList<HgFileRevision>> breakDown = new LinkedHashMap<Path, LinkedList<HgFileRevision>>();
          for (HgFileRevision fr : manifestContent) {
            Path filePath = fr.getPath();
            Path dirPath = pathPool.parent(filePath);
            LinkedList<HgFileRevision> revs = breakDown.get(dirPath);
            if (revs == null) {
              revs = new LinkedList<HgFileRevision>();
              breakDown.put(dirPath, revs);
            }
            revs.addLast(fr);
          }
          for (Path dir : breakDown.keySet()) {
            visitor.dir(dir);
            cancelHelper.checkCancelled();
            for (HgFileRevision fr : breakDown.get(dir)) {
              visitor.file(fr);
            }
          }
          manifestContent.clear();
        }
        visitor.end(manifestNodeid);
        cancelHelper.checkCancelled();
        return true;
      } catch (HgCallbackTargetException ex) {
        recordFailure(ex);
        return false;
      } catch (CancelledException ex) {
        recordCancel(ex);
        return false;
      } finally {
        manifestNodeid = null;
      }
    }
   
    public boolean next(Nodeid nid, Path fname, Flags flags) throws HgRuntimeException {
      if (matcher != null && !matcher.accept(fname)) {
        return true;
      }
      try {
        HgFileRevision fr = new HgFileRevision(repo, nid, flags, fname);
        if (needDirs) {
          manifestContent.add(fr);
        } else {
          visitor.file(fr);
        }
        return true;
      } catch (HgCallbackTargetException ex) {
        recordFailure(ex);
        return false;
      }
    }
  }
}
TOP

Related Classes of org.tmatesoft.hg.core.HgManifestCommand$Mediator

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.