org.xowl.hime.redist.lexer.BaseText Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of redist Show documentation
Show all versions of redist Show documentation
Redistributable Java runtime for the parsers generated with Hime
/*******************************************************************************
* Copyright (c) 2015 Laurent Wouters
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* This program 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program.
* If not, see .
*
* Contributors:
* Laurent Wouters - [email protected]
******************************************************************************/
package org.xowl.hime.redist.lexer;
import org.xowl.hime.redist.Text;
import org.xowl.hime.redist.TextContext;
import org.xowl.hime.redist.TextPosition;
import org.xowl.hime.redist.TextSpan;
import java.util.Arrays;
/**
* Represents the base implementation of Text
* All line numbers and column numbers are 1-based.
* Indices in the content are 0-based.
*
* @author Laurent Wouters
*/
public abstract class BaseText implements Text {
/**
* The initial size of the cache of line start indices
*/
static final int INIT_LINE_COUNT_CACHE_SIZE = 10000;
/**
* Cache of the starting indices of each line within the text
*/
int[] lines;
/**
* Index of the next line
*/
int line;
/**
* Gets the character at the specified index
*
* @param index Index from the start
* @return The character at the specified index
*/
public abstract char getValue(int index);
/**
* Gets whether the specified index is after the end of the text represented by this object
*
* @param index Index from the start
* @return true
if the index is after the end of the text
*/
public abstract boolean isEnd(int index);
/**
* Finds all the lines in this content
*/
protected abstract void findLines();
/**
* Determines whether [c1, c2] form a line ending sequence
* Recognized sequences are:
* [U+000D, U+000A] (this is Windows-style \r \n)
* [U+????, U+000A] (this is unix style \n)
* [U+000D, U+????] (this is MacOS style \r, without \n after)
* Others:
* [?, U+000B], [?, U+000C], [?, U+0085], [?, U+2028], [?, U+2029]
*
* @param c1 First character
* @param c2 Second character
* @return true if this is a line ending sequence
*/
boolean isLineEnding(char c1, char c2) {
// other characters
if (c2 == '\u000B' || c2 == '\u000C' || c2 == '\u0085' || c2 == '\u2028' || c2 == '\u2029')
return true;
// matches [\r, \n] [\r, ??] and [??, \n]
return c1 == '\r' || c2 == '\n';
}
/**
* Adds a line starting at the specified index
*
* @param index An index in the content
*/
void addLine(int index) {
if (line >= lines.length)
lines = Arrays.copyOf(lines, lines.length + INIT_LINE_COUNT_CACHE_SIZE);
lines[line] = index;
line++;
}
/**
* Finds the index in the cache of the line at the given input index in the content
*
* @param index The index within this content
* @return The index of the corresponding line in the cache
*/
private int findLineAt(int index) {
if (lines == null)
findLines();
for (int i = 1; i != line; i++) {
if (index < lines[i])
return i - 1;
}
return line - 1;
}
@Override
public int getLineCount() {
if (lines == null)
findLines();
return line;
}
@Override
public String getValue(TextSpan span) {
return getValue(span.getIndex(), span.getLength());
}
@Override
public int getLineIndex(int line) {
if (lines == null)
findLines();
return lines[line - 1];
}
@Override
public String getLineContent(int line) {
return getValue(getLineIndex(line), getLineLength(line));
}
@Override
public TextPosition getPositionAt(int index) {
int l = findLineAt(index);
return new TextPosition(l + 1, index - lines[l] + 1);
}
@Override
public TextContext getContext(TextPosition position) {
return getContext(position, 1);
}
@Override
public TextContext getContext(TextPosition position, int length) {
String content = getLineContent(position.getLine());
if (content.length() == 0)
return new TextContext("", "^");
int end = content.length() - 1;
while (end != -1 && (content.charAt(end) == '\n' || content.charAt(end) == '\r'))
end--;
int start = 0;
while (start < end && Character.isWhitespace(content.charAt(start)))
start++;
if (position.getColumn() - 1 < start) {
// the position is in the whitespace prefix ...
start = 0;
}
if (position.getColumn() - 1 > end) {
// the position is in the trailing line endings ...
end = content.length() - 1;
}
StringBuilder builder = new StringBuilder();
for (int i = start; i != position.getColumn() - 1; i++)
builder.append(content.charAt(i) == '\t' ? '\t' : ' ');
builder.append("^");
for (int i = 1; i != length; i++)
builder.append("^");
return new TextContext(content.substring(start, end + 1), builder.toString());
}
@Override
public TextContext getContext(TextSpan span) {
TextPosition position = getPositionAt(span.getIndex());
return getContext(position, span.getLength());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy