tikhomirov@676: /* tikhomirov@676: * Copyright (c) 2013 TMate Software Ltd tikhomirov@676: * tikhomirov@676: * This program is free software; you can redistribute it and/or modify tikhomirov@676: * it under the terms of the GNU General Public License as published by tikhomirov@676: * the Free Software Foundation; version 2 of the License. tikhomirov@676: * tikhomirov@676: * This program is distributed in the hope that it will be useful, tikhomirov@676: * but WITHOUT ANY WARRANTY; without even the implied warranty of tikhomirov@676: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the tikhomirov@676: * GNU General Public License for more details. tikhomirov@676: * tikhomirov@676: * For information on how to redistribute this software under tikhomirov@676: * the terms of a license other than GNU General Public License tikhomirov@676: * contact TMate Software at support@hg4j.com tikhomirov@676: */ tikhomirov@703: package org.tmatesoft.hg.internal.diff; tikhomirov@676: tikhomirov@676: import org.tmatesoft.hg.core.HgAnnotateCommand.Inspector; tikhomirov@676: import org.tmatesoft.hg.core.HgBlameInspector; tikhomirov@676: import org.tmatesoft.hg.core.HgCallbackTargetException; tikhomirov@676: import org.tmatesoft.hg.core.HgIterateDirection; tikhomirov@703: import org.tmatesoft.hg.internal.IntMap; tikhomirov@703: import org.tmatesoft.hg.internal.IntSliceSeq; tikhomirov@703: import org.tmatesoft.hg.internal.IntTuple; tikhomirov@676: import org.tmatesoft.hg.util.CancelSupport; tikhomirov@676: import org.tmatesoft.hg.util.CancelledException; tikhomirov@676: import org.tmatesoft.hg.util.ProgressSupport; tikhomirov@676: tikhomirov@676: /** tikhomirov@676: * Annotate file history iterating from parents to children tikhomirov@676: * tikhomirov@676: * At the moment, doesn't handle start from any revision but 0 tikhomirov@676: * tikhomirov@686: * (+) May report annotate for any revision (with actual file change) in the visited range. tikhomirov@676: * tikhomirov@676: * @see ReverseAnnotateInspector tikhomirov@676: * @author Artem Tikhomirov tikhomirov@676: * @author TMate Software Ltd. tikhomirov@676: */ tikhomirov@676: public class ForwardAnnotateInspector implements HgBlameInspector, HgBlameInspector.RevisionDescriptor.Recipient { tikhomirov@676: final IntMap all = new IntMap(100); tikhomirov@676: // revision->map(lineNumber->lineContent) tikhomirov@676: private final IntMap> lineContent = new IntMap>(100); tikhomirov@676: private IntSliceSeq current; tikhomirov@676: private RevisionDescriptor revDescriptor; tikhomirov@676: tikhomirov@676: /** tikhomirov@676: * @return desired order of iteration for diff tikhomirov@676: */ tikhomirov@676: public HgIterateDirection iterateDirection() { tikhomirov@676: return HgIterateDirection.OldToNew; tikhomirov@676: } tikhomirov@676: tikhomirov@676: public void report(int revision, Inspector insp, ProgressSupport progress, CancelSupport cancel) throws HgCallbackTargetException, CancelledException { tikhomirov@676: int totalLines = 0; tikhomirov@686: if (!all.containsKey(revision)) { tikhomirov@686: throw new IllegalArgumentException(String.format("Revision %d has not been visited", revision)); tikhomirov@686: } tikhomirov@676: for (IntTuple t : all.get(revision)) { tikhomirov@676: totalLines += t.at(0); tikhomirov@676: } tikhomirov@676: progress.start(totalLines); tikhomirov@676: LineImpl li = new LineImpl(); tikhomirov@676: int line = 1; tikhomirov@676: for (IntTuple t : all.get(revision)) { tikhomirov@676: IntMap revLines = lineContent.get(t.at(1)); tikhomirov@676: for (int i = 0, x = t.at(0); i < x; i++) { tikhomirov@676: final int lineInRev = t.at(2) + i; tikhomirov@676: final byte[] lc = revLines.get(lineInRev); tikhomirov@677: li.init(line++, lineInRev+1, t.at(1), lc); tikhomirov@676: insp.next(li); tikhomirov@676: progress.worked(1); tikhomirov@676: cancel.checkCancelled(); tikhomirov@676: } tikhomirov@676: } tikhomirov@676: progress.done(); tikhomirov@676: } tikhomirov@676: tikhomirov@676: public void start(RevisionDescriptor rd) throws HgCallbackTargetException { tikhomirov@676: all.put(rd.targetChangesetIndex(), current = new IntSliceSeq(3)); tikhomirov@676: revDescriptor = rd; tikhomirov@676: } tikhomirov@676: tikhomirov@676: public void done(RevisionDescriptor rd) throws HgCallbackTargetException { tikhomirov@676: revDescriptor = null; tikhomirov@676: } tikhomirov@676: tikhomirov@676: public void same(EqualBlock block) throws HgCallbackTargetException { tikhomirov@676: copyBlock(block.originChangesetIndex(), block.originStart(), block.length()); tikhomirov@676: } tikhomirov@676: tikhomirov@676: public void added(AddBlock block) throws HgCallbackTargetException { tikhomirov@676: if (revDescriptor.isMerge() && block.originChangesetIndex() == revDescriptor.mergeChangesetIndex()) { tikhomirov@676: copyBlock(block.originChangesetIndex(), block.insertedAt(), block.totalAddedLines()); tikhomirov@676: return; tikhomirov@676: } tikhomirov@676: BlockData addedLines = block.addedLines(); tikhomirov@676: IntMap revLines = lineContent.get(block.targetChangesetIndex()); tikhomirov@676: if (revLines == null) { tikhomirov@676: lineContent.put(block.targetChangesetIndex(), revLines = new IntMap(block.totalAddedLines())); tikhomirov@676: } tikhomirov@676: for (int i = 0; i < block.totalAddedLines(); i++) { tikhomirov@676: revLines.put(block.firstAddedLine() + i, addedLines.elementAt(i).asArray()); tikhomirov@676: } tikhomirov@676: current.add(block.totalAddedLines(), block.targetChangesetIndex(), block.firstAddedLine()); tikhomirov@676: } tikhomirov@676: tikhomirov@676: public void changed(ChangeBlock block) throws HgCallbackTargetException { tikhomirov@676: added(block); tikhomirov@676: } tikhomirov@676: tikhomirov@676: public void deleted(DeleteBlock block) throws HgCallbackTargetException { tikhomirov@676: } tikhomirov@676: tikhomirov@677: private void copyBlock(int originChangesetIndex, int blockStart, int length) { tikhomirov@676: IntSliceSeq origin = all.get(originChangesetIndex); tikhomirov@676: assert origin != null; // shall visit parents before came to this child tikhomirov@676: int originPos = 0; tikhomirov@676: int targetBlockLen = length; tikhomirov@676: for (IntTuple t : origin) { tikhomirov@676: int originBlockLen = t.at(0); tikhomirov@676: int originBlockEnd = originPos + originBlockLen; tikhomirov@677: if (originBlockEnd > blockStart) { tikhomirov@677: // part of origin block from blockStart up to originBlockEnd, but not more tikhomirov@677: // than size of the block (when blockStart is out of block start, i.e. < originPos) tikhomirov@677: int originBlockOverlap = Math.min(originBlockLen, originBlockEnd - blockStart); tikhomirov@676: assert originBlockOverlap > 0; tikhomirov@677: // eat as much as there's left in the block being copied tikhomirov@677: int originBlockConsumed = Math.min(originBlockOverlap, targetBlockLen); tikhomirov@676: int originBlockLine = t.at(2); tikhomirov@677: if (originPos < blockStart) { tikhomirov@676: originBlockLine += originBlockLen-originBlockOverlap; tikhomirov@676: } tikhomirov@676: // copy fragment of original block; tikhomirov@677: current.add(originBlockConsumed, t.at(1), originBlockLine); tikhomirov@677: targetBlockLen -= originBlockConsumed; tikhomirov@676: if (targetBlockLen == 0) { tikhomirov@676: break; tikhomirov@676: } tikhomirov@676: } tikhomirov@676: originPos += originBlockLen; tikhomirov@676: } tikhomirov@676: } tikhomirov@676: }