aoqi@0: /* aoqi@0: * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. Oracle designates this aoqi@0: * particular file as subject to the "Classpath" exception as provided aoqi@0: * by Oracle in the LICENSE file that accompanied this code. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: package com.sun.tools.javac.util; aoqi@0: aoqi@0: import java.util.BitSet; aoqi@0: import static com.sun.tools.javac.util.LayoutCharacters.*; aoqi@0: aoqi@0: /** A class that defines source code positions as simple character aoqi@0: * offsets from the beginning of the file. The first character aoqi@0: * is at position 0. aoqi@0: * aoqi@0: * Support is also provided for (line,column) coordinates, but tab aoqi@0: * expansion is optional and no Unicode excape translation is considered. aoqi@0: * The first character is at location (1,1). aoqi@0: * aoqi@0: *

This is NOT part of any supported API. aoqi@0: * If you write code that depends on this, you do so at your own risk. aoqi@0: * This code and its internal interfaces are subject to change or aoqi@0: * deletion without notice. aoqi@0: */ aoqi@0: public class Position { aoqi@0: public static final int NOPOS = -1; aoqi@0: aoqi@0: public static final int FIRSTPOS = 0; aoqi@0: public static final int FIRSTLINE = 1; aoqi@0: public static final int FIRSTCOLUMN = 1; aoqi@0: aoqi@0: public static final int LINESHIFT = 10; aoqi@0: public static final int MAXCOLUMN = (1< aoqi@0: * Notes: The first character position FIRSTPOS is at aoqi@0: * (FIRSTLINE,FIRSTCOLUMN). No account is taken of Unicode escapes. aoqi@0: * aoqi@0: * @param src Source characters aoqi@0: * @param max Number of characters to read aoqi@0: * @param expandTabs If true, expand tabs when calculating columns aoqi@0: */ aoqi@0: public static LineMap makeLineMap(char[] src, int max, boolean expandTabs) { aoqi@0: LineMapImpl lineMap = expandTabs ? aoqi@0: new LineTabMapImpl(max) : new LineMapImpl(); aoqi@0: lineMap.build(src, max); aoqi@0: return lineMap; aoqi@0: } aoqi@0: aoqi@0: /** Encode line and column numbers in an integer as: aoqi@0: * {@code line-number << LINESHIFT + column-number }. aoqi@0: * {@link Position#NOPOS} represents an undefined position. aoqi@0: * aoqi@0: * @param line number of line (first is 1) aoqi@0: * @param col number of character on line (first is 1) aoqi@0: * @return an encoded position or {@link Position#NOPOS} aoqi@0: * if the line or column number is too big to aoqi@0: * represent in the encoded format aoqi@0: * @throws IllegalArgumentException if line or col is less than 1 aoqi@0: */ aoqi@0: public static int encodePosition(int line, int col) { aoqi@0: if (line < 1) aoqi@0: throw new IllegalArgumentException("line must be greater than 0"); aoqi@0: if (col < 1) aoqi@0: throw new IllegalArgumentException("column must be greater than 0"); aoqi@0: aoqi@0: if (line > MAXLINE || col > MAXCOLUMN) { aoqi@0: return NOPOS; aoqi@0: } aoqi@0: return (line << LINESHIFT) + col; aoqi@0: } aoqi@0: aoqi@0: public static interface LineMap extends com.sun.source.tree.LineMap { aoqi@0: /** Find the start position of a line. aoqi@0: * aoqi@0: * @param line number of line (first is 1) aoqi@0: * @return position of first character in line aoqi@0: * @throws ArrayIndexOutOfBoundsException aoqi@0: * if {@code lineNumber < 1} aoqi@0: * if {@code lineNumber > no. of lines} aoqi@0: */ aoqi@0: int getStartPosition(int line); aoqi@0: aoqi@0: /** Find the position corresponding to a (line,column). aoqi@0: * aoqi@0: * @param line number of line (first is 1) aoqi@0: * @param column number of character on line (first is 1) aoqi@0: * aoqi@0: * @return position of character aoqi@0: * @throws ArrayIndexOutOfBoundsException aoqi@0: * if {@code line < 1} aoqi@0: * if {@code line > no. of lines} aoqi@0: */ aoqi@0: int getPosition(int line, int column); aoqi@0: aoqi@0: /** Find the line containing a position; a line termination aoqi@0: * character is on the line it terminates. aoqi@0: * aoqi@0: * @param pos character offset of the position aoqi@0: * @return the line number on which pos occurs (first line is 1) aoqi@0: */ aoqi@0: int getLineNumber(int pos); aoqi@0: aoqi@0: /** Find the column for a character position. aoqi@0: * Note: this method does not handle tab expansion. aoqi@0: * If tab expansion is needed, use a LineTabMap instead. aoqi@0: * aoqi@0: * @param pos character offset of the position aoqi@0: * @return the column number at which pos occurs aoqi@0: */ aoqi@0: int getColumnNumber(int pos); aoqi@0: } aoqi@0: aoqi@0: static class LineMapImpl implements LineMap { aoqi@0: protected int[] startPosition; // start position of each line aoqi@0: aoqi@0: protected LineMapImpl() {} aoqi@0: aoqi@0: protected void build(char[] src, int max) { aoqi@0: int c = 0; aoqi@0: int i = 0; aoqi@0: int[] linebuf = new int[max]; aoqi@0: while (i < max) { aoqi@0: linebuf[c++] = i; aoqi@0: do { aoqi@0: char ch = src[i]; aoqi@0: if (ch == '\r' || ch == '\n') { aoqi@0: if (ch == '\r' && (i+1) < max && src[i+1] == '\n') aoqi@0: i += 2; aoqi@0: else aoqi@0: ++i; aoqi@0: break; aoqi@0: } aoqi@0: else if (ch == '\t') aoqi@0: setTabPosition(i); aoqi@0: } while (++i < max); aoqi@0: } aoqi@0: this.startPosition = new int[c]; aoqi@0: System.arraycopy(linebuf, 0, startPosition, 0, c); aoqi@0: } aoqi@0: aoqi@0: public int getStartPosition(int line) { aoqi@0: return startPosition[line - FIRSTLINE]; aoqi@0: } aoqi@0: aoqi@0: public long getStartPosition(long line) { aoqi@0: return getStartPosition(longToInt(line)); aoqi@0: } aoqi@0: aoqi@0: public int getPosition(int line, int column) { aoqi@0: return startPosition[line - FIRSTLINE] + column - FIRSTCOLUMN; aoqi@0: } aoqi@0: aoqi@0: public long getPosition(long line, long column) { aoqi@0: return getPosition(longToInt(line), longToInt(column)); aoqi@0: } aoqi@0: aoqi@0: // Cache of last line number lookup aoqi@0: private int lastPosition = Position.FIRSTPOS; aoqi@0: private int lastLine = Position.FIRSTLINE; aoqi@0: aoqi@0: public int getLineNumber(int pos) { aoqi@0: if (pos == lastPosition) { aoqi@0: return lastLine; aoqi@0: } aoqi@0: lastPosition = pos; aoqi@0: aoqi@0: int low = 0; aoqi@0: int high = startPosition.length-1; aoqi@0: while (low <= high) { aoqi@0: int mid = (low + high) >> 1; aoqi@0: int midVal = startPosition[mid]; aoqi@0: aoqi@0: if (midVal < pos) aoqi@0: low = mid + 1; aoqi@0: else if (midVal > pos) aoqi@0: high = mid - 1; aoqi@0: else { aoqi@0: lastLine = mid + 1; // pos is at beginning of this line aoqi@0: return lastLine; aoqi@0: } aoqi@0: } aoqi@0: lastLine = low; aoqi@0: return lastLine; // pos is on this line aoqi@0: } aoqi@0: aoqi@0: public long getLineNumber(long pos) { aoqi@0: return getLineNumber(longToInt(pos)); aoqi@0: } aoqi@0: aoqi@0: public int getColumnNumber(int pos) { aoqi@0: return pos - startPosition[getLineNumber(pos) - FIRSTLINE] + FIRSTCOLUMN; aoqi@0: } aoqi@0: aoqi@0: public long getColumnNumber(long pos) { aoqi@0: return getColumnNumber(longToInt(pos)); aoqi@0: } aoqi@0: aoqi@0: private static int longToInt(long longValue) { aoqi@0: int intValue = (int)longValue; aoqi@0: if (intValue != longValue) aoqi@0: throw new IndexOutOfBoundsException(); aoqi@0: return intValue; aoqi@0: } aoqi@0: aoqi@0: protected void setTabPosition(int offset) {} aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * A LineMap that handles tab expansion correctly. The cost is aoqi@0: * an additional bit per character in the source array. aoqi@0: */ aoqi@0: public static class LineTabMapImpl extends LineMapImpl { aoqi@0: private BitSet tabMap; // bits set for tab positions. aoqi@0: aoqi@0: public LineTabMapImpl(int max) { aoqi@0: super(); aoqi@0: tabMap = new BitSet(max); aoqi@0: } aoqi@0: aoqi@0: protected void setTabPosition(int offset) { aoqi@0: tabMap.set(offset); aoqi@0: } aoqi@0: aoqi@0: public int getColumnNumber(int pos) { aoqi@0: int lineStart = startPosition[getLineNumber(pos) - FIRSTLINE]; aoqi@0: int column = 0; aoqi@0: for (int bp = lineStart; bp < pos; bp++) { aoqi@0: if (tabMap.get(bp)) aoqi@0: column = (column / TabInc * TabInc) + TabInc; aoqi@0: else aoqi@0: column++; aoqi@0: } aoqi@0: return column + FIRSTCOLUMN; aoqi@0: } aoqi@0: aoqi@0: public int getPosition(int line, int column) { aoqi@0: int pos = startPosition[line - FIRSTLINE]; aoqi@0: column -= FIRSTCOLUMN; aoqi@0: int col = 0; aoqi@0: while (col < column) { aoqi@0: pos++; aoqi@0: if (tabMap.get(pos)) aoqi@0: col = (col / TabInc * TabInc) + TabInc; aoqi@0: else aoqi@0: col++; aoqi@0: } aoqi@0: return pos; aoqi@0: } aoqi@0: } aoqi@0: }