package org.andork.codegen; import java.util.Comparator; import java.util.List; public class LineRegion { public final int startLine; public final int startColumn; public final int endLine; public final int endColumn; public LineRegion(int startLine, int startColumn, int endLine, int endColumn) { super(); if (startLine < 0) { throw new IllegalArgumentException("startLine must be >= 0"); } if (startColumn < 0) { throw new IllegalArgumentException("startColumn must be >= 0"); } if (endLine < startLine) { throw new IllegalArgumentException("endLine must be >= startLine"); } if (startLine == endLine && endColumn < startColumn) { throw new IllegalArgumentException("endColumn must be >= startColumn if startLine == endLine"); } this.startLine = startLine; this.startColumn = startColumn; this.endLine = endLine; this.endColumn = endColumn; } /** * Creates a zero-size region. * * @param line * @param beforeColumn */ public LineRegion(int line, int beforeColumn) { if (line < 0) { throw new IllegalArgumentException("line must be >= 0"); } if (beforeColumn < 0) { throw new IllegalArgumentException("beforeColumn must be >= 0"); } this.startLine = line; this.startColumn = beforeColumn; this.endLine = line; this.endColumn = beforeColumn - 1; } public LineRegion(LineRegion other) { this(other.startLine, other.startColumn, other.endLine, other.endColumn); } public LineRegion union(LineRegion other) { int nStartLine; int nStartColumn; int nEndLine; int nEndColumn; if (startLine == other.startLine) { nStartLine = startLine; nStartColumn = Math.min(startColumn, other.startColumn); } else if (startLine < other.startLine) { nStartLine = startLine; nStartColumn = startColumn; } else { nStartLine = other.startLine; nStartColumn = other.startColumn; } if (endLine == other.endLine) { nEndLine = endLine; nEndColumn = Math.max(endColumn, other.endColumn); } else if (endLine > other.endLine) { nEndLine = endLine; nEndColumn = endColumn; } else { nEndLine = other.endLine; nEndColumn = other.endColumn; } return new LineRegion(nStartLine, nStartColumn, nEndLine, nEndColumn); } public boolean isZeroSize() { return startLine == endLine && endColumn < startColumn; } public boolean overlaps(LineRegion other) { if (startLine > other.endLine || endLine < other.startLine) { return false; } if (other.isZeroSize()) { if (isZeroSize()) { return false; } if (startLine == other.startLine && startColumn >= other.startColumn) { return false; } if (endLine == other.endLine && endColumn <= other.endColumn) { return false; } return true; } if (startLine == other.startLine && startColumn <= other.endColumn) { return true; } if (endLine == other.endLine && endColumn >= other.startColumn) { return true; } return false; } public static void replace(List<String> lines, LineRegion region, String replacement, int tabSize) { StringBuffer sb = new StringBuffer(); String startLine = lines.get(region.startLine); int startIndex = indexOfColumn(startLine, region.startColumn, tabSize); String endLine = lines.get(region.endLine); int endIndex = indexOfColumn(endLine, region.endColumn, tabSize); sb.append(startLine.substring(0, startIndex)); sb.append(replacement); if (endIndex + 1 < endLine.length()) { sb.append(endLine.substring(endIndex + 1)); } for (int line = region.endLine; line >= region.startLine; line--) { lines.remove(line); } String[] newLines = sb.toString().split("(\r\n|\n\r|\n|\r)"); for (int line = newLines.length - 1; line >= 0; line--) { lines.add(region.startLine, newLines[line]); } } private static int indexOfColumn(String s, int column, int tabSize) { if (column < 0) { return column; } int curColumn = 0; int i; for (i = 0; i < s.length() && curColumn < column; i++) { if (s.charAt(i) == '\t') { curColumn += tabSize; curColumn -= curColumn % tabSize; } else { curColumn++; } if (curColumn > column) { return i; } } return i; } public boolean equals(Object o) { if (o instanceof LineRegion) { LineRegion l = (LineRegion) o; return startLine == l.startLine && startColumn == l.startColumn && endLine == l.endLine && endColumn == l.endColumn; } return false; } public int hashCode() { int hashCode = startLine; hashCode = (23 * hashCode) ^ startColumn; hashCode = (29 * hashCode) ^ endLine; hashCode = (31 * hashCode) ^ endColumn; return hashCode; } public static Comparator<LineRegion> OVERLAP_FORBIDDING_COMPARATOR = new OverlapForbiddingComparator(); private static class OverlapForbiddingComparator implements Comparator<LineRegion> { @Override public int compare(LineRegion o1, LineRegion o2) { if (o1.overlaps(o2)) { throw new IllegalArgumentException("o1 and o2 must not overlap"); } int result = o1.startLine - o2.startLine; if (result != 0) { return result; } if (o1.isZeroSize()) { if (o2.isZeroSize()) { return o1.startColumn - o2.startColumn; } else { return o1.startColumn <= o2.startColumn ? -1 : 1; } } else { if (o2.isZeroSize()) { return o1.startColumn >= o2.startColumn ? 1 : -1; } else { return o1.startColumn - o2.startColumn; } } } } public String toString() { return String.format("%d : %d - %d : %d", startLine, startColumn, endLine, endColumn); } }