/** * Constructs map permitting translation between 0-based character offsets and 1-based * lines/columns. */ public static TextMap fromString(String text) { final int textLength = text.length(); final ArrayList<Integer> 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); final int[] nlOffsets = new int[lines.size()]; for (int line = 0; line < lines.size(); line++) { nlOffsets[line] = lines.get(line); } final boolean finalNL = textLength > 0 && (textLength == nlOffsets[nlOffsets.length - 2]); return new TextMap(nlOffsets, textLength, finalNL); }
protected TextMap createTextMap() { final String code = getCode(); if (code == null) { throw new RuntimeException("can't read file " + getName()); } return TextMap.fromString(code); }
/** * Gets the text (not including a possible terminating newline) in a (1-based) numbered line. */ public final String getCode(int lineNumber) { checkTextMap(); final int offset = textMap.lineStartOffset(lineNumber); final int length = textMap.lineLength(lineNumber); return getCode().substring(offset, offset + length); }
/** * Creates a representation of a contiguous region of text in the source. Computes the * {@code charIndex} value by building a {@linkplain TextMap map} of lines in the source. * <p> * Checks the position arguments for consistency with the source. * <p> * The resulting representation defines hash/equality around equivalent location, presuming that * {@link Source} representations are canonical. * * @param identifier terse description of the region * @param startLine 1-based line number of the first character in the section * @param startColumn 1-based column number of the first character in the section * @param length the number of characters in the section * @return newly created object representing the specified region * @throws IllegalArgumentException if arguments are outside the text of the source * @throws IllegalStateException if the source is one of the "null" instances */ public final SourceSection createSection(String identifier, int startLine, int startColumn, int length) { checkTextMap(); final int lineStartOffset = textMap.lineStartOffset(startLine); if (startColumn > textMap.lineLength(startLine)) { throw new IllegalArgumentException("column out of range"); } final int startOffset = lineStartOffset + startColumn - 1; return new DefaultSourceSection(this, identifier, startLine, startColumn, startOffset, length); }
public static TextMap fromBytes(byte[] bytes, int byteIndex, int length, BytesDecoder bytesDecoder) { final ArrayList<Integer> lines = new ArrayList<>(); lines.add(0); bytesDecoder.decodeLines(bytes, byteIndex, length, new BytesDecoder.LineMarker() { public void markLine(int index) { lines.add(index); } }); lines.add(Integer.MAX_VALUE); final int[] nlOffsets = new int[lines.size()]; for (int line = 0; line < lines.size(); line++) { nlOffsets[line] = lines.get(line); } final boolean finalNL = length > 0 && (length == nlOffsets[nlOffsets.length - 2]); return new TextMap(nlOffsets, length, finalNL); }
/** * Gets the number of characters in a line, identified by 1-based line number; * <em>does not</em> 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; }
/** * Creates a representation of a line of text in the source identified only by line number, from * which the character information will be computed. * * @param identifier terse description of the line * @param lineNumber 1-based line number of the first character in the section * @return newly created object representing the specified line * @throws IllegalArgumentException if the line does not exist the source * @throws IllegalStateException if the source is one of the "null" instances */ public final SourceSection createSection(String identifier, int lineNumber) { checkTextMap(); final int charIndex = textMap.lineStartOffset(lineNumber); final int length = textMap.lineLength(lineNumber); return createSection(identifier, charIndex, length); }
@Override protected TextMap createTextMap() { return TextMap.fromBytes(bytes, byteIndex, length, decoder); } }
/** * 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) throws IllegalArgumentException { return checkTextMap().lineLength(lineNumber); }
/** * 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 text lines in the source, including empty lines; characters at the end of the * source without a terminating newline count as a line. */ public final int getLineCount() { return checkTextMap().lineCount(); }
/** * Gets the number of characters in the source. */ public final int getLength() { return checkTextMap().length(); }
/** * 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]; }
/** * Converts 0-based character offset to 1-based number of the column occupied by the * character. * <p> * 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]; }
/** * 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) throws IllegalArgumentException { return checkTextMap().offsetToLine(offset); }
/** * 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 getColumnNumber(int offset) throws IllegalArgumentException { return checkTextMap().offsetToCol(offset); }