/*
* Copyright 2009-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.codehaus.groovy.antlr;
import java.util.List;
/**
* Maps lines/columns to offsets in a text file. Assumes '\n' is the newline
* delimiter. The newline character is included as the last char on the line.
* Lines and columns are both 1 based
*
* <ul>
* <li> "" -> [0,0]
* <li> "a" -> [0,1]
* <li> "\n" -> [0,1], [1,0]
* <li> "a\n" -> [0,2], [2,0]
* <li> "a\nb" -> [0,2], [2,1]
* <li> "a\nbc\n" -> [0,2], [2,3], [5,0]
* </ul>
*
* @author Andrew Eisenberg
* @created Jun 4, 2009
*/
public class LocationSupport {
private static final int[] NO_LINE_ENDINGS = new int[0];
public static final LocationSupport NO_LOCATIONS = new LocationSupport();
private final int[] lineEndings;
public LocationSupport() {
lineEndings = NO_LINE_ENDINGS;
}
public LocationSupport(int[] lineEndings) {
this.lineEndings = lineEndings;
}
public LocationSupport(List<StringBuffer> lines) {
if (lines != null) {
lineEndings = processLineEndings(lines);
} else {
lineEndings = NO_LINE_ENDINGS;
}
}
private int[] processLineEndings(List<? extends CharSequence> lines) {
int[] newLineEndings = new int[lines.size() + 1]; // last index stores end of file
int total = 0;
int current = 1;
for (CharSequence line : lines) {
newLineEndings[current++] = (total += (line.length()));
}
return newLineEndings;
}
// public LocationSupport(char[] contents) {
// if (contents != null) {
// lineEndings = processLineEndings(contents);
// } else {
// lineEndings = NO_LINE_ENDINGS;
// }
// }
// private int[] processLineEndings(char[] contents) {
// List<Integer> l = new ArrayList<Integer>();
// for (int i = 0; i < contents.length; i++) {
//
// if (contents[i] == '\n') {
// l.add(i);
// } else if (contents[i] == '\r') {
// l.add(i);
// if (i < contents.length && contents[i] == '\n') {
// i++;
// }
// }
// }
// int[] newLineEndings = new int[l.size()];
// int i = 0;
// for (Integer integer : l) {
// newLineEndings[i] = integer.intValue();
// }
// return newLineEndings;
// }
// TODO: Maybe should throw exception if out of bounds?
public int findOffset(int row, int col) {
return row <= lineEndings.length && row > 0 ? lineEndings[row - 1] + col - 1 : 0;
}
public int getEnd() {
return lineEndings.length > 0 ? lineEndings[lineEndings.length - 1] : 0;
}
public int getEndColumn() {
if (lineEndings.length > 1) {
return lineEndings[lineEndings.length - 1] - lineEndings[lineEndings.length - 2];
} else if (lineEndings.length > 0) {
return lineEndings[0];
} else {
return 0;
}
}
public int getEndLine() {
return lineEndings.length > 0 ? lineEndings.length - 1 : 0; // last index contains length of document
}
public int[] getRowCol(int offset) {
for (int i = 1; i < lineEndings.length; i++) {
if (lineEndings[i] > offset) {
return new int[] {i, offset - lineEndings[i - 1] + 1};
}
}
// after end of document
throw new RuntimeException("Location is after end of document. Offset : " + offset);
}
public boolean isPopulated() {
return lineEndings.length > 0;
}
}