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