tikhomirov@546: /* tikhomirov@546: * Copyright (c) 2013 TMate Software Ltd tikhomirov@546: * tikhomirov@546: * This program is free software; you can redistribute it and/or modify tikhomirov@546: * it under the terms of the GNU General Public License as published by tikhomirov@546: * the Free Software Foundation; version 2 of the License. tikhomirov@546: * tikhomirov@546: * This program is distributed in the hope that it will be useful, tikhomirov@546: * but WITHOUT ANY WARRANTY; without even the implied warranty of tikhomirov@546: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the tikhomirov@546: * GNU General Public License for more details. tikhomirov@546: * tikhomirov@546: * For information on how to redistribute this software under tikhomirov@546: * the terms of a license other than GNU General Public License tikhomirov@546: * contact TMate Software at support@hg4j.com tikhomirov@546: */ tikhomirov@546: package org.tmatesoft.hg.test; tikhomirov@546: tikhomirov@546: import java.util.LinkedList; tikhomirov@546: tikhomirov@546: import org.tmatesoft.hg.internal.AnnotateFacility; tikhomirov@546: import org.tmatesoft.hg.internal.AnnotateFacility.LineInspector; tikhomirov@546: import org.tmatesoft.hg.internal.IntVector; tikhomirov@546: import org.tmatesoft.hg.internal.AnnotateFacility.AddBlock; tikhomirov@546: import org.tmatesoft.hg.internal.AnnotateFacility.ChangeBlock; tikhomirov@546: import org.tmatesoft.hg.internal.AnnotateFacility.DeleteBlock; tikhomirov@546: import org.tmatesoft.hg.internal.AnnotateFacility.EqualBlock; tikhomirov@546: tikhomirov@546: /** tikhomirov@546: * tikhomirov@546: * @author Artem Tikhomirov tikhomirov@546: * @author TMate Software Ltd. tikhomirov@546: */ tikhomirov@546: public class FileAnnotation implements AnnotateFacility.BlockInspectorEx { tikhomirov@546: // blocks deleted in the target, as reported at the previous step tikhomirov@546: private LinkedList deleted = new LinkedList(); tikhomirov@546: // blocks deleted in the origin, to become deletions in target at the next step tikhomirov@546: private LinkedList newDeleted = new LinkedList(); tikhomirov@546: // keeps of equal blocks, origin to target, from previous step tikhomirov@546: // XXX smth like IntSliceVector to access triples (or slices of any size, in fact) tikhomirov@546: // with easy indexing, e.g. #get(sliceIndex, indexWithinSlice) tikhomirov@546: // and vect.get(7,2) instead of vect.get(7*SIZEOF_SLICE+2) tikhomirov@546: private IntVector identical = new IntVector(20*3, 2*3); tikhomirov@546: // equal blocks of the current iteration, to be recalculated before next step tikhomirov@546: // to track line number (current target to ultimate target) mapping tikhomirov@546: private IntVector newIdentical = new IntVector(20*3, 2*3); tikhomirov@546: tikhomirov@546: private boolean[] knownLines; tikhomirov@546: private final LineInspector delegate; tikhomirov@546: tikhomirov@546: public FileAnnotation(AnnotateFacility.LineInspector lineInspector) { tikhomirov@546: delegate = lineInspector; tikhomirov@546: } tikhomirov@546: tikhomirov@546: public void start(int originLineCount, int targetLineCount) { tikhomirov@546: if (knownLines == null) { tikhomirov@546: knownLines = new boolean[targetLineCount]; tikhomirov@546: } tikhomirov@546: } tikhomirov@546: tikhomirov@546: // private static void ppp(IntVector v) { tikhomirov@546: // for (int i = 0; i < v.size(); i+= 3) { tikhomirov@546: // int len = v.get(i+2); tikhomirov@546: // System.out.printf("[%d..%d) == [%d..%d); ", v.get(i), v.get(i) + len, v.get(i+1), v.get(i+1) + len); tikhomirov@546: // } tikhomirov@546: // System.out.println(); tikhomirov@546: // } tikhomirov@546: tikhomirov@546: public void done() { tikhomirov@546: if (identical.size() > 0) { tikhomirov@546: // update line numbers of the intermediate target to point to ultimate target's line numbers tikhomirov@546: IntVector v = new IntVector(identical.size(), 2*3); tikhomirov@546: for (int i = 0; i < newIdentical.size(); i+= 3) { tikhomirov@546: int originLine = newIdentical.get(i); tikhomirov@546: int targetLine = newIdentical.get(i+1); tikhomirov@546: int length = newIdentical.get(i+2); tikhomirov@546: int startTargetLine = -1, startOriginLine = -1, c = 0; tikhomirov@546: for (int j = 0; j < length; j++) { tikhomirov@546: int lnInFinal = mapLineIndex(targetLine + j); tikhomirov@546: if (lnInFinal == -1 || (startTargetLine != -1 && lnInFinal != startTargetLine + c)) { tikhomirov@546: // the line is not among "same" in ultimate origin tikhomirov@546: // or belongs to another/next "same" chunk tikhomirov@546: if (startOriginLine == -1) { tikhomirov@546: continue; tikhomirov@546: } tikhomirov@546: v.add(startOriginLine); tikhomirov@546: v.add(startTargetLine); tikhomirov@546: v.add(c); tikhomirov@546: c = 0; tikhomirov@546: startOriginLine = startTargetLine = -1; tikhomirov@546: // fall-through to check if it's not complete miss but a next chunk tikhomirov@546: } tikhomirov@546: if (lnInFinal != -1) { tikhomirov@546: if (startOriginLine == -1) { tikhomirov@546: startOriginLine = originLine + j; tikhomirov@546: startTargetLine = lnInFinal; tikhomirov@546: c = 1; tikhomirov@546: } else { tikhomirov@546: assert lnInFinal == startTargetLine + c; tikhomirov@546: c++; tikhomirov@546: } tikhomirov@546: } tikhomirov@546: } tikhomirov@546: if (startOriginLine != -1) { tikhomirov@546: assert c > 0; tikhomirov@546: v.add(startOriginLine); tikhomirov@546: v.add(startTargetLine); tikhomirov@546: v.add(c); tikhomirov@546: } tikhomirov@546: } tikhomirov@546: newIdentical.clear(); tikhomirov@546: identical = v; tikhomirov@546: } else { tikhomirov@546: IntVector li = newIdentical; tikhomirov@546: newIdentical = identical; tikhomirov@546: identical = li; tikhomirov@546: } tikhomirov@546: LinkedList ld = newDeleted; tikhomirov@546: deleted.clear(); tikhomirov@546: newDeleted = deleted; tikhomirov@546: deleted = ld; tikhomirov@546: } tikhomirov@546: tikhomirov@546: public void same(EqualBlock block) { tikhomirov@546: newIdentical.add(block.originStart()); tikhomirov@546: newIdentical.add(block.targetStart()); tikhomirov@546: newIdentical.add(block.length()); tikhomirov@546: } tikhomirov@546: tikhomirov@546: public void added(AddBlock block) { tikhomirov@546: for (int i = 0, ln = block.firstAddedLine(), x = block.totalAddedLines(); i < x; i++, ln++) { tikhomirov@546: int lnInFinal = mapLineIndex(ln); tikhomirov@546: if (lnInFinal != -1 && !knownLines[lnInFinal]) { tikhomirov@546: delegate.line(lnInFinal, block.targetChangesetIndex(), new LineDescriptor()); tikhomirov@546: knownLines[lnInFinal] = true; tikhomirov@546: } tikhomirov@546: } tikhomirov@546: } tikhomirov@546: tikhomirov@546: public void changed(ChangeBlock block) { tikhomirov@546: deleted(block); tikhomirov@546: added(block); tikhomirov@546: } tikhomirov@546: tikhomirov@546: public void deleted(DeleteBlock block) { tikhomirov@546: newDeleted.add(block); tikhomirov@546: } tikhomirov@546: tikhomirov@546: // line - index in the target tikhomirov@546: private boolean isDeleted(int line) { tikhomirov@546: for (DeleteBlock b : deleted) { tikhomirov@546: if (b.firstRemovedLine() > line) { tikhomirov@546: break; tikhomirov@546: } tikhomirov@546: // line >= b.firstRemovedLine tikhomirov@546: if (b.firstRemovedLine() + b.totalRemovedLines() > line) { tikhomirov@546: return true; tikhomirov@546: } tikhomirov@546: } tikhomirov@546: return false; tikhomirov@546: } tikhomirov@546: tikhomirov@546: // map target lines to the lines of the revision being annotated (the one that came first) tikhomirov@546: private int mapLineIndex(int ln) { tikhomirov@546: if (isDeleted(ln)) { tikhomirov@546: return -1; tikhomirov@546: } tikhomirov@546: if (identical.isEmpty()) { tikhomirov@546: return ln; tikhomirov@546: } tikhomirov@546: for (int i = 0; i < identical.size(); i += 3) { tikhomirov@546: final int originStart = identical.get(i); tikhomirov@546: if (originStart > ln) { tikhomirov@546: // assert false; tikhomirov@546: return -1; tikhomirov@546: } tikhomirov@546: // ln >= b.originStart tikhomirov@546: final int length = identical.get(i+2); tikhomirov@546: if (originStart + length > ln) { tikhomirov@546: int targetStart = identical.get(i+1); tikhomirov@546: return targetStart + (ln - originStart); tikhomirov@546: } tikhomirov@546: } tikhomirov@546: // assert false; tikhomirov@546: return -1; tikhomirov@546: } tikhomirov@546: tikhomirov@546: private final class LineDescriptor implements AnnotateFacility.LineDescriptor { tikhomirov@546: LineDescriptor() { tikhomirov@546: } tikhomirov@546: tikhomirov@546: public int totalLines() { tikhomirov@546: return FileAnnotation.this.knownLines.length; tikhomirov@546: } tikhomirov@546: } tikhomirov@546: }