duke@1: /*
ohair@554: * Copyright (c) 1999, 2006, Oracle and/or its affiliates. All rights reserved.
duke@1: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
duke@1: *
duke@1: * This code is free software; you can redistribute it and/or modify it
duke@1: * under the terms of the GNU General Public License version 2 only, as
ohair@554: * published by the Free Software Foundation. Oracle designates this
duke@1: * particular file as subject to the "Classpath" exception as provided
ohair@554: * by Oracle in the LICENSE file that accompanied this code.
duke@1: *
duke@1: * This code is distributed in the hope that it will be useful, but WITHOUT
duke@1: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
duke@1: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
duke@1: * version 2 for more details (a copy is included in the LICENSE file that
duke@1: * accompanied this code).
duke@1: *
duke@1: * You should have received a copy of the GNU General Public License version
duke@1: * 2 along with this work; if not, write to the Free Software Foundation,
duke@1: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
duke@1: *
ohair@554: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
ohair@554: * or visit www.oracle.com if you need additional information or have any
ohair@554: * questions.
duke@1: */
duke@1:
duke@1: package com.sun.tools.javac.util;
duke@1:
duke@1: import java.util.BitSet;
duke@1: import static com.sun.tools.javac.util.LayoutCharacters.*;
duke@1:
duke@1: /** A class that defines source code positions as simple character
duke@1: * offsets from the beginning of the file. The first character
duke@1: * is at position 0.
duke@1: *
duke@1: * Support is also provided for (line,column) coordinates, but tab
duke@1: * expansion is optional and no Unicode excape translation is considered.
duke@1: * The first character is at location (1,1).
duke@1: *
jjg@581: *
This is NOT part of any supported API.
jjg@581: * If you write code that depends on this, you do so at your own risk.
duke@1: * This code and its internal interfaces are subject to change or
duke@1: * deletion without notice.
duke@1: */
duke@1: public class Position {
duke@1: public static final int NOPOS = -1;
duke@1:
duke@1: public static final int FIRSTPOS = 0;
duke@1: public static final int FIRSTLINE = 1;
duke@1: public static final int FIRSTCOLUMN = 1;
duke@1:
duke@1: public static final int LINESHIFT = 10;
duke@1: public static final int MAXCOLUMN = (1<
duke@1: * Notes: The first character position FIRSTPOS is at
duke@1: * (FIRSTLINE,FIRSTCOLUMN). No account is taken of Unicode escapes.
duke@1: *
duke@1: * @param src Source characters
duke@1: * @param max Number of characters to read
duke@1: * @param expandTabs If true, expand tabs when calculating columns
duke@1: */
duke@1: public static LineMap makeLineMap(char[] src, int max, boolean expandTabs) {
duke@1: LineMapImpl lineMap = expandTabs ?
duke@1: new LineTabMapImpl(max) : new LineMapImpl();
duke@1: lineMap.build(src, max);
duke@1: return lineMap;
duke@1: }
duke@1:
duke@1: /** Encode line and column numbers in an integer as:
duke@1: * line-number << LINESHIFT + column-number
duke@1: * {@link Position.NOPOS represents an undefined position.
duke@1: *
duke@1: * @param line number of line (first is 1)
duke@1: * @param col number of character on line (first is 1)
duke@1: * @return an encoded position or {@link Position.NOPOS
duke@1: * if the line or column number is too big to
duke@1: * represent in the encoded format
duke@1: * @throws IllegalArgumentException if line or col is less than 1
duke@1: */
duke@1: public static int encodePosition(int line, int col) {
duke@1: if (line < 1)
duke@1: throw new IllegalArgumentException("line must be greater than 0");
duke@1: if (col < 1)
duke@1: throw new IllegalArgumentException("column must be greater than 0");
duke@1:
duke@1: if (line > MAXLINE || col > MAXCOLUMN) {
duke@1: return NOPOS;
duke@1: }
duke@1: return (line << LINESHIFT) + col;
duke@1: }
duke@1:
duke@1: public static interface LineMap extends com.sun.source.tree.LineMap {
duke@1: /** Find the start position of a line.
duke@1: *
duke@1: * @param line number of line (first is 1)
duke@1: * @return position of first character in line
duke@1: * @throws ArrayIndexOutOfBoundsException
duke@1: * if lineNumber < 1
duke@1: * if lineNumber > no. of lines
duke@1: */
duke@1: int getStartPosition(int line);
duke@1:
duke@1: /** Find the position corresponding to a (line,column).
duke@1: *
duke@1: * @param line number of line (first is 1)
duke@1: * @param column number of character on line (first is 1)
duke@1: *
duke@1: * @return position of character
duke@1: * @throws ArrayIndexOutOfBoundsException
duke@1: * if line < 1
duke@1: * if line > no. of lines
duke@1: */
duke@1: int getPosition(int line, int column);
duke@1:
duke@1: /** Find the line containing a position; a line termination
duke@1: * character is on the line it terminates.
duke@1: *
duke@1: * @param pos character offset of the position
duke@1: * @return the line number on which pos occurs (first line is 1)
duke@1: */
duke@1: int getLineNumber(int pos);
duke@1:
duke@1: /** Find the column for a character position.
duke@1: * Note: this method does not handle tab expansion.
duke@1: * If tab expansion is needed, use a LineTabMap instead.
duke@1: *
duke@1: * @param pos character offset of the position
duke@1: * @return the column number at which pos occurs
duke@1: */
duke@1: int getColumnNumber(int pos);
duke@1: }
duke@1:
duke@1: static class LineMapImpl implements LineMap {
duke@1: protected int[] startPosition; // start position of each line
duke@1:
duke@1: protected LineMapImpl() {}
duke@1:
duke@1: protected void build(char[] src, int max) {
duke@1: int c = 0;
duke@1: int i = 0;
duke@1: int[] linebuf = new int[max];
duke@1: while (i < max) {
duke@1: linebuf[c++] = i;
duke@1: do {
duke@1: char ch = src[i];
duke@1: if (ch == '\r' || ch == '\n') {
duke@1: if (ch == '\r' && (i+1) < max && src[i+1] == '\n')
duke@1: i += 2;
duke@1: else
duke@1: ++i;
duke@1: break;
duke@1: }
duke@1: else if (ch == '\t')
duke@1: setTabPosition(i);
duke@1: } while (++i < max);
duke@1: }
duke@1: this.startPosition = new int[c];
duke@1: System.arraycopy(linebuf, 0, startPosition, 0, c);
duke@1: }
duke@1:
duke@1: public int getStartPosition(int line) {
duke@1: return startPosition[line - FIRSTLINE];
duke@1: }
duke@1:
duke@1: public long getStartPosition(long line) {
duke@1: return getStartPosition(longToInt(line));
duke@1: }
duke@1:
duke@1: public int getPosition(int line, int column) {
duke@1: return startPosition[line - FIRSTLINE] + column - FIRSTCOLUMN;
duke@1: }
duke@1:
duke@1: public long getPosition(long line, long column) {
duke@1: return getPosition(longToInt(line), longToInt(column));
duke@1: }
duke@1:
duke@1: // Cache of last line number lookup
duke@1: private int lastPosition = Position.FIRSTPOS;
duke@1: private int lastLine = Position.FIRSTLINE;
duke@1:
duke@1: public int getLineNumber(int pos) {
duke@1: if (pos == lastPosition) {
duke@1: return lastLine;
duke@1: }
duke@1: lastPosition = pos;
duke@1:
duke@1: int low = 0;
duke@1: int high = startPosition.length-1;
duke@1: while (low <= high) {
duke@1: int mid = (low + high) >> 1;
duke@1: int midVal = startPosition[mid];
duke@1:
duke@1: if (midVal < pos)
duke@1: low = mid + 1;
duke@1: else if (midVal > pos)
duke@1: high = mid - 1;
duke@1: else {
duke@1: lastLine = mid + 1; // pos is at beginning of this line
duke@1: return lastLine;
duke@1: }
duke@1: }
duke@1: lastLine = low;
duke@1: return lastLine; // pos is on this line
duke@1: }
duke@1:
duke@1: public long getLineNumber(long pos) {
duke@1: return getLineNumber(longToInt(pos));
duke@1: }
duke@1:
duke@1: public int getColumnNumber(int pos) {
duke@1: return pos - startPosition[getLineNumber(pos) - FIRSTLINE] + FIRSTCOLUMN;
duke@1: }
duke@1:
duke@1: public long getColumnNumber(long pos) {
duke@1: return getColumnNumber(longToInt(pos));
duke@1: }
duke@1:
duke@1: private static int longToInt(long longValue) {
duke@1: int intValue = (int)longValue;
duke@1: if (intValue != longValue)
duke@1: throw new IndexOutOfBoundsException();
duke@1: return intValue;
duke@1: }
duke@1:
duke@1: protected void setTabPosition(int offset) {}
duke@1: }
duke@1:
duke@1: /**
duke@1: * A LineMap that handles tab expansion correctly. The cost is
duke@1: * an additional bit per character in the source array.
duke@1: */
duke@1: public static class LineTabMapImpl extends LineMapImpl {
duke@1: private BitSet tabMap; // bits set for tab positions.
duke@1:
duke@1: public LineTabMapImpl(int max) {
duke@1: super();
duke@1: tabMap = new BitSet(max);
duke@1: }
duke@1:
duke@1: protected void setTabPosition(int offset) {
duke@1: tabMap.set(offset);
duke@1: }
duke@1:
duke@1: public int getColumnNumber(int pos) {
duke@1: int lineStart = startPosition[getLineNumber(pos) - FIRSTLINE];
duke@1: int column = 0;
duke@1: for (int bp = lineStart; bp < pos; bp++) {
duke@1: if (tabMap.get(bp))
duke@1: column = (column / TabInc * TabInc) + TabInc;
duke@1: else
duke@1: column++;
duke@1: }
duke@1: return column + FIRSTCOLUMN;
duke@1: }
duke@1:
duke@1: public int getPosition(int line, int column) {
duke@1: int pos = startPosition[line - FIRSTLINE];
duke@1: column -= FIRSTCOLUMN;
duke@1: int col = 0;
duke@1: while (col < column) {
duke@1: pos++;
duke@1: if (tabMap.get(pos))
duke@1: col = (col / TabInc * TabInc) + TabInc;
duke@1: else
duke@1: col++;
duke@1: }
duke@1: return pos;
duke@1: }
duke@1: }
duke@1: }