001: /*
002: * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.tools.javac.util;
027:
028: import java.util.BitSet;
029: import static com.sun.tools.javac.util.LayoutCharacters.*;
030:
031: /** A class that defines source code positions as simple character
032: * offsets from the beginning of the file. The first character
033: * is at position 0.
034: *
035: * Support is also provided for (line,column) coordinates, but tab
036: * expansion is optional and no Unicode excape translation is considered.
037: * The first character is at location (1,1).
038: *
039: * <p><b>This is NOT part of any API supported by Sun Microsystems. If
040: * you write code that depends on this, you do so at your own risk.
041: * This code and its internal interfaces are subject to change or
042: * deletion without notice.</b>
043: */
044: @Version("@(#)Position.java 1.25 07/05/05")
045: public class Position {
046: public static final int NOPOS = -1;
047:
048: public static final int FIRSTPOS = 0;
049: public static final int FIRSTLINE = 1;
050: public static final int FIRSTCOLUMN = 1;
051:
052: public static final int LINESHIFT = 10;
053: public static final int MAXCOLUMN = (1 << LINESHIFT) - 1;
054: public static final int MAXLINE = (1 << (Integer.SIZE - LINESHIFT)) - 1;
055:
056: public static final int MAXPOS = Integer.MAX_VALUE;
057:
058: /**
059: * This is class is not supposed to be instantiated.
060: */
061: private Position() {
062: }
063:
064: /** A two-way map between line/column numbers and positions,
065: * derived from a scan done at creation time. Tab expansion is
066: * optionally supported via a character map. Text content
067: * is not retained.
068: *<p>
069: * Notes: The first character position FIRSTPOS is at
070: * (FIRSTLINE,FIRSTCOLUMN). No account is taken of Unicode escapes.
071: *
072: * @param src Source characters
073: * @param max Number of characters to read
074: * @param expandTabs If true, expand tabs when calculating columns
075: */
076: public static LineMap makeLineMap(char[] src, int max,
077: boolean expandTabs) {
078: LineMapImpl lineMap = expandTabs ? new LineTabMapImpl(max)
079: : new LineMapImpl();
080: lineMap.build(src, max);
081: return lineMap;
082: }
083:
084: /** Encode line and column numbers in an integer as:
085: * line-number << LINESHIFT + column-number
086: * {@link Position.NOPOS represents an undefined position.
087: *
088: * @param line number of line (first is 1)
089: * @param col number of character on line (first is 1)
090: * @return an encoded position or {@link Position.NOPOS
091: * if the line or column number is too big to
092: * represent in the encoded format
093: * @throws IllegalArgumentException if line or col is less than 1
094: */
095: public static int encodePosition(int line, int col) {
096: if (line < 1)
097: throw new IllegalArgumentException(
098: "line must be greater than 0");
099: if (col < 1)
100: throw new IllegalArgumentException(
101: "column must be greater than 0");
102:
103: if (line > MAXLINE || col > MAXCOLUMN) {
104: return NOPOS;
105: }
106: return (line << LINESHIFT) + col;
107: }
108:
109: public static interface LineMap extends com.sun.source.tree.LineMap {
110: /** Find the start position of a line.
111: *
112: * @param line number of line (first is 1)
113: * @return position of first character in line
114: * @throws ArrayIndexOutOfBoundsException
115: * if <tt>lineNumber < 1</tt>
116: * if <tt>lineNumber > no. of lines</tt>
117: */
118: int getStartPosition(int line);
119:
120: /** Find the position corresponding to a (line,column).
121: *
122: * @param line number of line (first is 1)
123: * @param column number of character on line (first is 1)
124: *
125: * @return position of character
126: * @throws ArrayIndexOutOfBoundsException
127: * if <tt>line < 1</tt>
128: * if <tt>line > no. of lines</tt>
129: */
130: int getPosition(int line, int column);
131:
132: /** Find the line containing a position; a line termination
133: * character is on the line it terminates.
134: *
135: * @param pos character offset of the position
136: * @return the line number on which pos occurs (first line is 1)
137: */
138: int getLineNumber(int pos);
139:
140: /** Find the column for a character position.
141: * Note: this method does not handle tab expansion.
142: * If tab expansion is needed, use a LineTabMap instead.
143: *
144: * @param pos character offset of the position
145: * @return the column number at which pos occurs
146: */
147: int getColumnNumber(int pos);
148: }
149:
150: static class LineMapImpl implements LineMap {
151: protected int[] startPosition; // start position of each line
152:
153: protected LineMapImpl() {
154: }
155:
156: protected void build(char[] src, int max) {
157: int c = 0;
158: int i = 0;
159: int[] linebuf = new int[max];
160: while (i < max) {
161: linebuf[c++] = i;
162: do {
163: char ch = src[i];
164: if (ch == '\r' || ch == '\n') {
165: if (ch == '\r' && (i + 1) < max
166: && src[i + 1] == '\n')
167: i += 2;
168: else
169: ++i;
170: break;
171: } else if (ch == '\t')
172: setTabPosition(i);
173: } while (++i < max);
174: }
175: this .startPosition = new int[c];
176: System.arraycopy(linebuf, 0, startPosition, 0, c);
177: }
178:
179: public int getStartPosition(int line) {
180: return startPosition[line - FIRSTLINE];
181: }
182:
183: public long getStartPosition(long line) {
184: return getStartPosition(longToInt(line));
185: }
186:
187: public int getPosition(int line, int column) {
188: return startPosition[line - FIRSTLINE] + column
189: - FIRSTCOLUMN;
190: }
191:
192: public long getPosition(long line, long column) {
193: return getPosition(longToInt(line), longToInt(column));
194: }
195:
196: // Cache of last line number lookup
197: private int lastPosition = Position.FIRSTPOS;
198: private int lastLine = Position.FIRSTLINE;
199:
200: public int getLineNumber(int pos) {
201: if (pos == lastPosition) {
202: return lastLine;
203: }
204: lastPosition = pos;
205:
206: int low = 0;
207: int high = startPosition.length - 1;
208: while (low <= high) {
209: int mid = (low + high) >> 1;
210: int midVal = startPosition[mid];
211:
212: if (midVal < pos)
213: low = mid + 1;
214: else if (midVal > pos)
215: high = mid - 1;
216: else {
217: lastLine = mid + 1; // pos is at beginning of this line
218: return lastLine;
219: }
220: }
221: lastLine = low;
222: return lastLine; // pos is on this line
223: }
224:
225: public long getLineNumber(long pos) {
226: return getLineNumber(longToInt(pos));
227: }
228:
229: public int getColumnNumber(int pos) {
230: return pos - startPosition[getLineNumber(pos) - FIRSTLINE]
231: + FIRSTCOLUMN;
232: }
233:
234: public long getColumnNumber(long pos) {
235: return getColumnNumber(longToInt(pos));
236: }
237:
238: private static int longToInt(long longValue) {
239: int intValue = (int) longValue;
240: if (intValue != longValue)
241: throw new IndexOutOfBoundsException();
242: return intValue;
243: }
244:
245: protected void setTabPosition(int offset) {
246: }
247: }
248:
249: /**
250: * A LineMap that handles tab expansion correctly. The cost is
251: * an additional bit per character in the source array.
252: */
253: public static class LineTabMapImpl extends LineMapImpl {
254: private BitSet tabMap; // bits set for tab positions.
255:
256: public LineTabMapImpl(int max) {
257: super ();
258: tabMap = new BitSet(max);
259: }
260:
261: protected void setTabPosition(int offset) {
262: tabMap.set(offset);
263: }
264:
265: public int getColumnNumber(int pos) {
266: int lineStart = startPosition[getLineNumber(pos)
267: - FIRSTLINE];
268: int column = 0;
269: for (int bp = lineStart; bp < pos; bp++) {
270: if (tabMap.get(bp))
271: column = (column / TabInc * TabInc) + TabInc;
272: else
273: column++;
274: }
275: return column + FIRSTCOLUMN;
276: }
277:
278: public int getPosition(int line, int column) {
279: int pos = startPosition[line - FIRSTLINE];
280: column -= FIRSTCOLUMN;
281: int col = 0;
282: while (col < column) {
283: pos++;
284: if (tabMap.get(pos))
285: col = (col / TabInc * TabInc) + TabInc;
286: else
287: col++;
288: }
289: return pos;
290: }
291: }
292: }
|