# HG changeset patch # User Michael Van De Vanter # Date 1402977126 25200 # Node ID 6f7d3f3703d3b46d3245f9cd90f14fbc71dd25ac # Parent a3a9d703c078e400085a044c9362ac44d5e0c5f9 Truffle/Source: - LineLocation and LineBreakpoint no longer implement Comparable - TextMap now internal to the Source factory diff -r a3a9d703c078 -r 6f7d3f3703d3 graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/utilities/SourceTextTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/utilities/SourceTextTest.java Mon Jun 16 20:52:06 2014 -0700 @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.test.utilities; + +import static org.junit.Assert.*; + +import org.junit.*; + +import com.oracle.truffle.api.source.*; + +public class SourceTextTest { + + private final Source emptySource = Source.fromText("", null); + + private final Source emptyLineSource = Source.fromText("\n", null); + + private final Source shortSource = Source.fromText("01", null); + + private final Source longSource = Source.fromText("01234\n67\n9\n", null); + + @Test + public void emptyTextTest0() { + assertEquals(emptySource.getLineCount(), 0); + } + + @Test(expected = IllegalArgumentException.class) + public void emptyTextTest1() { + emptySource.getLineNumber(0); + } + + @Test(expected = IllegalArgumentException.class) + public void emptyTextTest2() { + emptySource.getColumnNumber(0); + } + + @Test(expected = IllegalArgumentException.class) + public void emptyTextTest3() { + emptySource.getLineNumber(-1); + } + + @Test(expected = IllegalArgumentException.class) + public void emptyTextTest4() { + emptySource.getLineStartOffset(0); + } + + @Test(expected = IllegalArgumentException.class) + public void emptyTextTest5() { + emptySource.getLineStartOffset(1); + } + + @Test(expected = IllegalArgumentException.class) + public void emptyTextTest6() { + emptySource.getLineLength(1); + } + + @Test + public void emptyLineTest0() { + assertEquals(emptyLineSource.getLineCount(), 1); + assertEquals(emptyLineSource.getLineNumber(0), 1); + assertEquals(emptyLineSource.getLineStartOffset(1), 0); + assertEquals(emptyLineSource.getColumnNumber(0), 1); + assertEquals(emptyLineSource.getLineLength(1), 0); + } + + @Test(expected = IllegalArgumentException.class) + public void emptyLineTest1() { + emptyLineSource.getLineNumber(1); + } + + @Test(expected = IllegalArgumentException.class) + public void emptyLineTest2() { + emptyLineSource.getLineStartOffset(2); + } + + @Test(expected = IllegalArgumentException.class) + public void emptyLineTest3() { + emptyLineSource.getColumnNumber(1); + } + + @Test(expected = IllegalArgumentException.class) + public void emptyLineTest4() { + emptyLineSource.getLineLength(2); + } + + @Test + public void shortTextTest0() { + + assertEquals(shortSource.getLineCount(), 1); + + assertEquals(shortSource.getLineNumber(0), 1); + assertEquals(shortSource.getLineStartOffset(1), 0); + assertEquals(shortSource.getColumnNumber(0), 1); + + assertEquals(shortSource.getLineNumber(1), 1); + assertEquals(shortSource.getColumnNumber(1), 2); + + assertEquals(shortSource.getLineLength(1), 2); + } + + @Test(expected = IllegalArgumentException.class) + public void shortTextTest1() { + shortSource.getLineNumber(-1); + } + + @Test(expected = IllegalArgumentException.class) + public void shortTextTest2() { + shortSource.getColumnNumber(-1); + } + + @Test(expected = IllegalArgumentException.class) + public void shortTextTest3() { + shortSource.getLineNumber(2); + } + + @Test(expected = IllegalArgumentException.class) + public void shortTextTest4() { + shortSource.getColumnNumber(2); + } + + @Test(expected = IllegalArgumentException.class) + public void shortTextTest5() { + shortSource.getLineLength(2); + } + + @Test(expected = IllegalArgumentException.class) + public void shortTextTest6() { + shortSource.getLineLength(2); + } + + @Test + public void longTextTest0() { + + assertEquals(longSource.getLineCount(), 3); + + assertEquals(longSource.getLineNumber(0), 1); + assertEquals(longSource.getLineStartOffset(1), 0); + assertEquals(longSource.getColumnNumber(0), 1); + + assertEquals(longSource.getLineNumber(4), 1); + assertEquals(longSource.getColumnNumber(4), 5); + + assertEquals(longSource.getLineNumber(5), 1); // newline + assertEquals(longSource.getColumnNumber(5), 6); // newline + assertEquals(longSource.getLineLength(1), 5); + + assertEquals(longSource.getLineNumber(6), 2); + assertEquals(longSource.getLineStartOffset(2), 6); + assertEquals(longSource.getColumnNumber(6), 1); + + assertEquals(longSource.getLineNumber(7), 2); + assertEquals(longSource.getColumnNumber(7), 2); + + assertEquals(longSource.getLineNumber(8), 2); // newline + assertEquals(longSource.getLineNumber(8), 2); // newline + assertEquals(longSource.getLineLength(2), 2); + + assertEquals(longSource.getLineNumber(9), 3); + assertEquals(longSource.getLineStartOffset(3), 9); + assertEquals(longSource.getColumnNumber(9), 1); + + assertEquals(longSource.getLineNumber(10), 3); // newline + assertEquals(longSource.getColumnNumber(10), 2); // newline + assertEquals(longSource.getLineLength(3), 1); + + } + + @Test(expected = IllegalArgumentException.class) + public void longTextTest1() { + longSource.getLineNumber(11); + } + + @Test(expected = IllegalArgumentException.class) + public void longTextTest2() { + longSource.getColumnNumber(11); + } + + @Test(expected = IllegalArgumentException.class) + public void longTextTest3() { + longSource.getLineStartOffset(4); + } + +} diff -r a3a9d703c078 -r 6f7d3f3703d3 graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/utilities/TextMapTest.java --- a/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/utilities/TextMapTest.java Mon Jun 16 23:07:45 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,202 +0,0 @@ -/* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.truffle.api.test.utilities; - -import static org.junit.Assert.*; - -import org.junit.*; - -import com.oracle.truffle.api.source.*; - -public class TextMapTest { - - final TextMap emptyTextMap = new TextMap(""); - - final TextMap emptyLineMap = new TextMap("\n"); - - private final TextMap shortMap = new TextMap("01"); - - private final TextMap longMap = new TextMap("01234\n67\n9\n"); - - @Test - public void emptyTextTest0() { - assertEquals(emptyTextMap.lineCount(), 0); - } - - @Test(expected = IllegalArgumentException.class) - public void emptyTextTest1() { - emptyTextMap.offsetToLine(0); - } - - @Test(expected = IllegalArgumentException.class) - public void emptyTextTest2() { - emptyTextMap.offsetToCol(0); - } - - @Test(expected = IllegalArgumentException.class) - public void emptyTextTest3() { - emptyTextMap.lineStartOffset(-1); - } - - @Test(expected = IllegalArgumentException.class) - public void emptyTextTest4() { - emptyTextMap.lineStartOffset(0); - } - - @Test(expected = IllegalArgumentException.class) - public void emptyTextTest5() { - emptyTextMap.lineStartOffset(1); - } - - @Test(expected = IllegalArgumentException.class) - public void emptyTextTest6() { - emptyTextMap.lineLength(1); - } - - @Test - public void emptyLineTest0() { - assertEquals(emptyLineMap.lineCount(), 1); - assertEquals(emptyLineMap.offsetToLine(0), 1); - assertEquals(emptyLineMap.lineStartOffset(1), 0); - assertEquals(emptyLineMap.offsetToCol(0), 1); - assertEquals(emptyLineMap.lineLength(1), 0); - } - - @Test(expected = IllegalArgumentException.class) - public void emptyLineTest1() { - emptyLineMap.offsetToLine(1); - } - - @Test(expected = IllegalArgumentException.class) - public void emptyLineTest2() { - emptyLineMap.lineStartOffset(2); - } - - @Test(expected = IllegalArgumentException.class) - public void emptyLineTest3() { - emptyLineMap.offsetToCol(1); - } - - @Test(expected = IllegalArgumentException.class) - public void emptyLineTest4() { - emptyLineMap.lineLength(2); - } - - @Test - public void shortTextTest0() { - - assertEquals(shortMap.lineCount(), 1); - - assertEquals(shortMap.offsetToLine(0), 1); - assertEquals(shortMap.lineStartOffset(1), 0); - assertEquals(shortMap.offsetToCol(0), 1); - - assertEquals(shortMap.offsetToLine(1), 1); - assertEquals(shortMap.offsetToCol(1), 2); - - assertEquals(shortMap.lineLength(1), 2); - } - - @Test(expected = IllegalArgumentException.class) - public void shortTextTest1() { - shortMap.offsetToLine(-1); - } - - @Test(expected = IllegalArgumentException.class) - public void shortTextTest2() { - shortMap.offsetToCol(-1); - } - - @Test(expected = IllegalArgumentException.class) - public void shortTextTest3() { - shortMap.offsetToLine(2); - } - - @Test(expected = IllegalArgumentException.class) - public void shortTextTest4() { - shortMap.offsetToCol(2); - } - - @Test(expected = IllegalArgumentException.class) - public void shortTextTest5() { - shortMap.lineStartOffset(2); - } - - @Test(expected = IllegalArgumentException.class) - public void shortTextTest6() { - shortMap.lineLength(2); - } - - @Test - public void longTextTest0() { - - assertEquals(longMap.lineCount(), 3); - - assertEquals(longMap.offsetToLine(0), 1); - assertEquals(longMap.lineStartOffset(1), 0); - assertEquals(longMap.offsetToCol(0), 1); - - assertEquals(longMap.offsetToLine(4), 1); - assertEquals(longMap.offsetToCol(4), 5); - - assertEquals(longMap.offsetToLine(5), 1); // newline - assertEquals(longMap.offsetToCol(5), 6); // newline - assertEquals(longMap.lineLength(1), 5); - - assertEquals(longMap.offsetToLine(6), 2); - assertEquals(longMap.lineStartOffset(2), 6); - assertEquals(longMap.offsetToCol(6), 1); - - assertEquals(longMap.offsetToLine(7), 2); - assertEquals(longMap.offsetToCol(7), 2); - - assertEquals(longMap.offsetToLine(8), 2); // newline - assertEquals(longMap.offsetToLine(8), 2); // newline - assertEquals(longMap.lineLength(2), 2); - - assertEquals(longMap.offsetToLine(9), 3); - assertEquals(longMap.lineStartOffset(3), 9); - assertEquals(longMap.offsetToCol(9), 1); - - assertEquals(longMap.offsetToLine(10), 3); // newline - assertEquals(longMap.offsetToCol(10), 2); // newline - assertEquals(longMap.lineLength(3), 1); - - } - - @Test(expected = IllegalArgumentException.class) - public void longTextTest1() { - longMap.offsetToLine(11); - } - - @Test(expected = IllegalArgumentException.class) - public void longTextTest2() { - longMap.offsetToCol(11); - } - - @Test(expected = IllegalArgumentException.class) - public void longTextTest3() { - longMap.lineStartOffset(4); - } - -} diff -r a3a9d703c078 -r 6f7d3f3703d3 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/LineLocation.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/LineLocation.java Mon Jun 16 23:07:45 2014 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/LineLocation.java Mon Jun 16 20:52:06 2014 -0700 @@ -29,7 +29,7 @@ * instance of {@link Source}, suitable for hash table keys with equality defined in terms of * content. */ -public interface LineLocation extends Comparable { +public interface LineLocation { public Source getSource(); diff -r a3a9d703c078 -r 6f7d3f3703d3 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java Mon Jun 16 23:07:45 2014 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java Mon Jun 16 20:52:06 2014 -0700 @@ -194,10 +194,10 @@ return builder.toString(); } - protected Source() { + Source() { } - protected TextMap textMap = null; + private TextMap textMap = null; protected abstract void reset(); @@ -266,23 +266,38 @@ /** * Given a 0-based character offset, return the 1-based number of the line that includes the * position. + * + * @throws IllegalArgumentException if the offset is outside the text contents */ - public final int getLineNumber(int offset) { + public final int getLineNumber(int offset) throws IllegalArgumentException { return checkTextMap().offsetToLine(offset); } /** - * Given a 1-based line number, return the 0-based offset of the first character in the line. + * Given a 0-based character offset, return the 1-based number of the column at the position. + * + * @throws IllegalArgumentException if the offset is outside the text contents */ - public final int getLineStartOffset(int lineNumber) { + public final int getColumnNumber(int offset) throws IllegalArgumentException { + return checkTextMap().offsetToCol(offset); + } + + /** + * Given a 1-based line number, return the 0-based offset of the first character in the line. + * + * @throws IllegalArgumentException if there is no such line in the text + */ + public final int getLineStartOffset(int lineNumber) throws IllegalArgumentException { return checkTextMap().lineStartOffset(lineNumber); } /** * The number of characters (not counting a possible terminating newline) in a (1-based) * numbered line. + * + * @throws IllegalArgumentException if there is no such line in the text */ - public final int getLineLength(int lineNumber) { + public final int getLineLength(int lineNumber) throws IllegalArgumentException { return checkTextMap().lineLength(lineNumber); } @@ -809,15 +824,152 @@ return source.equals(other.source); } - @Override - public int compareTo(Object o) { - final LineLocationImpl other = (LineLocationImpl) o; - final int nameOrder = source.getName().compareTo(other.source.getName()); - if (nameOrder != 0) { - return nameOrder; + } + + /** + * A utility for converting between coordinate systems in a string of text interspersed with + * newline characters. The coordinate systems are: + * + *

+ * This utility is based on positions occupied by characters, not text stream positions as in a + * text editor. The distinction shows up in editors where you can put the cursor just past the + * last character in a buffer; this is necessary, among other reasons, so that you can put the + * edit cursor in a new (empty) buffer. For the purposes of this utility, however, there are no + * character positions in an empty text string and there are no lines in an empty text string. + *

+ * A newline character designates the end of a line and occupies a column position. + *

+ * If the text ends with a character other than a newline, then the characters following the + * final newline character count as a line, even though not newline-terminated. + *

+ * Limitations: + *

+ */ + private static final class TextMap { + + // 0-based offsets of newline characters in the text, with sentinel + private final int[] nlOffsets; + + // The number of characters in the text, including newlines (which count as 1). + private final int textLength; + + // Is the final text character a newline? + final boolean finalNL; + + /** + * Constructs map permitting translation between 0-based character offsets and 1-based + * lines/columns. + */ + public TextMap(String text) { + this.textLength = text.length(); + final ArrayList lines = new ArrayList<>(); + lines.add(0); + int offset = 0; + + while (offset < text.length()) { + final int nlIndex = text.indexOf('\n', offset); + if (nlIndex >= 0) { + offset = nlIndex + 1; + lines.add(offset); + } else { + break; + } + } + lines.add(Integer.MAX_VALUE); + + nlOffsets = new int[lines.size()]; + for (int line = 0; line < lines.size(); line++) { + nlOffsets[line] = lines.get(line); } - return Integer.compare(line, other.line); + + finalNL = textLength > 0 && (textLength == nlOffsets[nlOffsets.length - 2]); + } + + /** + * Converts 0-based character offset to 1-based number of the line containing the character. + * + * @throws IllegalArgumentException if the offset is outside the string. + */ + public int offsetToLine(int offset) throws IllegalArgumentException { + if (offset < 0 || offset >= textLength) { + throw new IllegalArgumentException("offset out of bounds"); + } + int line = 1; + while (offset >= nlOffsets[line]) { + line++; + } + return line; + } + + /** + * Converts 0-based character offset to 1-based number of the column occupied by the + * character. + *

+ * Tabs are not expanded; they occupy 1 column. + * + * @throws IllegalArgumentException if the offset is outside the string. + */ + public int offsetToCol(int offset) throws IllegalArgumentException { + return 1 + offset - nlOffsets[offsetToLine(offset) - 1]; + } + + /** + * The number of lines in the text; if characters appear after the final newline, then they + * also count as a line, even though not newline-terminated. + */ + public int lineCount() { + if (textLength == 0) { + return 0; + } + return finalNL ? nlOffsets.length - 2 : nlOffsets.length - 1; + } + + /** + * Converts 1-based line number to the 0-based offset of the line's first character; this + * would be the offset of a newline if the line is empty. + * + * @throws IllegalArgumentException if there is no such line in the text. + */ + public int lineStartOffset(int line) throws IllegalArgumentException { + if (textLength == 0 || lineOutOfRange(line)) { + throw new IllegalArgumentException("line out of bounds"); + } + return nlOffsets[line - 1]; + } + + /** + * Gets the number of characters in a line, identified by 1-based line number; + * does not include the final newline, if any. + * + * @throws IllegalArgumentException if there is no such line in the text. + */ + public int lineLength(int line) throws IllegalArgumentException { + if (textLength == 0 || lineOutOfRange(line)) { + throw new IllegalArgumentException("line out of bounds"); + } + if (line == nlOffsets.length - 1 && !finalNL) { + return textLength - nlOffsets[line - 1]; + } + return (nlOffsets[line] - nlOffsets[line - 1]) - 1; + + } + + /** + * Is the line number out of range. + */ + private boolean lineOutOfRange(int line) { + return line <= 0 || line >= nlOffsets.length || (line == nlOffsets.length - 1 && finalNL); } } + } diff -r a3a9d703c078 -r 6f7d3f3703d3 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/TextMap.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/TextMap.java Mon Jun 16 23:07:45 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,172 +0,0 @@ -/* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.truffle.api.source; - -import java.util.*; - -/** - * A utility for converting between coordinate systems in a string of text interspersed with newline - * characters. The coordinate systems are: - *

    - *
  • 0-based character offset from the beginning of the text, where newline characters count as a - * single character and the first character in the text occupies position 0.
  • - *
  • 1-based position in the 2D space of lines and columns, in which the first position in the - * text is at (1,1).
  • - *
- *

- * This utility is based on positions occupied by characters, not text stream positions as in a text - * editor. The distinction shows up in editors where you can put the cursor just past the last - * character in a buffer; this is necessary, among other reasons, so that you can put the edit - * cursor in a new (empty) buffer. For the purposes of this utility, however, there are no character - * positions in an empty text string and there are no lines in an empty text string. - *

- * A newline character designates the end of a line and occupies a column position. - *

- * If the text ends with a character other than a newline, then the characters following the final - * newline character count as a line, even though not newline-terminated. - *

- * Limitations: - *

    - *
  • Does not handle multiple character encodings correctly.
  • - *
  • Treats tabs as occupying 1 column.
  • - *
  • Does not handle multiple-character line termination sequences correctly.
  • - *
- */ -public final class TextMap { - - // 0-based offsets of newline characters in the text, with sentinel - private final int[] nlOffsets; - - // The number of characters in the text, including newlines (which count as 1). - private final int textLength; - - // Is the final text character a newline? - final boolean finalNL; - - /** - * Constructs map permitting translation between 0-based character offsets and 1-based - * lines/columns. - */ - public TextMap(String text) { - this.textLength = text.length(); - final ArrayList lines = new ArrayList<>(); - lines.add(0); - int offset = 0; - - while (offset < text.length()) { - final int nlIndex = text.indexOf('\n', offset); - if (nlIndex >= 0) { - offset = nlIndex + 1; - lines.add(offset); - } else { - break; - } - } - lines.add(Integer.MAX_VALUE); - - nlOffsets = new int[lines.size()]; - for (int line = 0; line < lines.size(); line++) { - nlOffsets[line] = lines.get(line); - } - - finalNL = textLength > 0 && (textLength == nlOffsets[nlOffsets.length - 2]); - } - - /** - * Converts 0-based character offset to 1-based number of the line containing the character. - * - * @throws IllegalArgumentException if the offset is outside the string. - */ - public int offsetToLine(int offset) throws IllegalArgumentException { - if (offset < 0 || offset >= textLength) { - throw new IllegalArgumentException("offset out of bounds"); - } - int line = 1; - while (offset >= nlOffsets[line]) { - line++; - } - return line; - } - - /** - * Converts 0-based character offset to 1-based number of the column occupied by the character. - *

- * Tabs are not expanded; they occupy 1 column. - * - * @throws IllegalArgumentException if the offset is outside the string. - */ - public int offsetToCol(int offset) throws IllegalArgumentException { - return 1 + offset - nlOffsets[offsetToLine(offset) - 1]; - } - - /** - * The number of lines in the text; if characters appear after the final newline, then they also - * count as a line, even though not newline-terminated. - */ - public int lineCount() { - if (textLength == 0) { - return 0; - } - return finalNL ? nlOffsets.length - 2 : nlOffsets.length - 1; - } - - /** - * Converts 1-based line number to the 0-based offset of the line's first character; this would - * be the offset of a newline if the line is empty. - * - * @throws IllegalArgumentException if there is no such line in the text. - */ - public int lineStartOffset(int line) throws IllegalArgumentException { - if (textLength == 0 || lineOutOfRange(line)) { - throw new IllegalArgumentException("line out of bounds"); - } - return nlOffsets[line - 1]; - } - - /** - * Gets the number of characters in a line, identified by 1-based line number; does not - * include the final newline, if any. - * - * @throws IllegalArgumentException if there is no such line in the text. - */ - public int lineLength(int line) throws IllegalArgumentException { - if (textLength == 0 || lineOutOfRange(line)) { - throw new IllegalArgumentException("line out of bounds"); - } - if (line == nlOffsets.length - 1 && !finalNL) { - return textLength - nlOffsets[line - 1]; - } - return (nlOffsets[line] - nlOffsets[line - 1]) - 1; - - } - - /** - * Is the line number out of range. - */ - private boolean lineOutOfRange(int line) { - return line <= 0 || line >= nlOffsets.length || (line == nlOffsets.length - 1 && finalNL); - } - -}