Mercurial > hg4j
diff src/org/tmatesoft/hg/internal/RangeSeq.java @ 558:154718ae23ed
Annotate: refactor/reuse range handling code
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Mon, 25 Feb 2013 18:41:44 +0100 |
parents | |
children | d3c71498919c |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/tmatesoft/hg/internal/RangeSeq.java Mon Feb 25 18:41:44 2013 +0100 @@ -0,0 +1,175 @@ +/* + * Copyright (c) 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.Formatter; + +/** + * Sequence of range pairs (denoted origin and target), {originStart, targetStart, length}, tailored for diff/annotate + * + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ +public final class RangeSeq { + // XXX smth like IntSliceVector to access triples (or slices of any size, in fact) + // with easy indexing, e.g. #get(sliceIndex, indexWithinSlice) + // and vect.get(7,2) instead of vect.get(7*SIZEOF_SLICE+2) + private final IntVector ranges = new IntVector(3*10, 3*5); + private int count; + + public void add(int start1, int start2, int length) { + if (count > 0) { + int lastIndex = 3 * (count-1); + int lastS1 = ranges.get(lastIndex); + int lastS2 = ranges.get(lastIndex + 1); + int lastLen = ranges.get(lastIndex + 2); + if (start1 == lastS1 + lastLen && start2 == lastS2 + lastLen) { + // new range continues the previous one - just increase the length + ranges.set(lastIndex + 2, lastLen + length); + return; + } + } + ranges.add(start1, start2, length); + count++; + } + + public void clear() { + ranges.clear(); + count = 0; + } + + public int size() { + return count; + } + + /** + * find out line index in the target that matches specified origin line + */ + public int mapLineIndex(int ln) { + for (int i = 0; i < ranges.size(); i += 3) { + int s1 = ranges.get(i); + if (s1 > ln) { + return -1; + } + int l = ranges.get(i+2); + if (s1 + l > ln) { + int s2 = ranges.get(i + 1); + return s2 + (ln - s1); + } + } + return -1; + } + + /** + * find out line index in origin that matches specified target line + */ + public int reverseMapLine(int targetLine) { + for (int i = 0; i < ranges.size(); i +=3) { + int ts = ranges.get(i + 1); + if (ts > targetLine) { + return -1; + } + int l = ranges.get(i + 2); + if (ts + l > targetLine) { + int os = ranges.get(i); + return os + (targetLine - ts); + } + } + return -1; + } + + public RangeSeq intersect(RangeSeq target) { + RangeSeq v = new RangeSeq(); + for (int i = 0; i < ranges.size(); i += 3) { + int originLine = ranges.get(i); + int targetLine = ranges.get(i + 1); + int length = ranges.get(i + 2); + int startTargetLine = -1, startOriginLine = -1, c = 0; + for (int j = 0; j < length; j++) { + int lnInFinal = target.mapLineIndex(targetLine + j); + if (lnInFinal == -1 || (startTargetLine != -1 && lnInFinal != startTargetLine + c)) { + // the line is not among "same" in ultimate origin + // or belongs to another/next "same" chunk + if (startOriginLine == -1) { + continue; + } + v.add(startOriginLine, startTargetLine, c); + c = 0; + startOriginLine = startTargetLine = -1; + // fall-through to check if it's not complete miss but a next chunk + } + if (lnInFinal != -1) { + if (startOriginLine == -1) { + startOriginLine = originLine + j; + startTargetLine = lnInFinal; + c = 1; + } else { + // lnInFinal != startTargetLine + s is covered above + assert lnInFinal == startTargetLine + c; + c++; + } + } + } + if (startOriginLine != -1) { + assert c > 0; + v.add(startOriginLine, startTargetLine, c); + } + } + return v; + } + + // true when specified line in origin is equal to a line in target + public boolean includesOriginLine(int ln) { + return includes(ln, 0); + } + + // true when specified line in target is equal to a line in origin + public boolean includesTargetLine(int ln) { + return includes(ln, 1); + } + + private boolean includes(int ln, int o) { + for (int i = 2; i < ranges.size(); o += 3, i+=3) { + int rangeStart = ranges.get(o); + if (rangeStart > ln) { + return false; + } + int rangeLen = ranges.get(i); + if (rangeStart + rangeLen > ln) { + return true; + } + } + return false; + } + + public CharSequence dump() { + StringBuilder sb = new StringBuilder(); + Formatter f = new Formatter(sb); + for (int i = 0; i < ranges.size(); i += 3) { + int s1 = ranges.get(i); + int s2 = ranges.get(i + 1); + int len = ranges.get(i + 2); + f.format("[%d..%d) == [%d..%d); ", s1, s1 + len, s2, s2 + len); + } + return sb; + } + + @Override + public String toString() { + return String.format("RangeSeq[%d]", count); + } +} \ No newline at end of file