/*
* Copyright (c) 2012 Sam Harwell, Tunnel Vision Laboratories LLC
* All rights reserved.
*
* The source code of this document is proprietary work, and is not licensed for
* distribution. For information about licensing, contact Sam Harwell at:
* sam@tunnelvisionlabs.com
*/
package org.antlr.netbeans.editor.text.impl;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import org.antlr.netbeans.editor.text.DocumentSnapshot;
import org.antlr.netbeans.editor.text.DocumentSnapshotLine;
import org.antlr.netbeans.editor.text.OffsetRegion;
import org.antlr.netbeans.editor.text.TrackingFidelity;
import org.antlr.netbeans.editor.text.TrackingPosition;
import org.antlr.netbeans.editor.text.TrackingPositionRegion;
import org.antlr.netbeans.editor.text.VersionedDocument;
import org.netbeans.api.annotations.common.NonNull;
import org.openide.util.Parameters;
/**
*
* @author Sam Harwell
*/
public class NbDocumentSnapshot implements DocumentSnapshot {
@NonNull
private final NbDocumentVersion textVersion;
public NbDocumentSnapshot(@NonNull NbDocumentVersion textVersion) {
Parameters.notNull("textVersion", textVersion);
this.textVersion = textVersion;
}
@Override
public int getLineCount() {
return getVersion().getLineData().getLineCount();
}
@Override
public Iterable<DocumentSnapshotLine> getLines() {
return new LineIterable(this);
}
@Override
public VersionedDocument getVersionedDocument() {
return getVersion().getVersionedDocument();
}
@Override
public NbDocumentVersion getVersion() {
return textVersion;
}
@Override
public TrackingPosition createTrackingPosition(int position, TrackingPosition.Bias bias) {
return textVersion.createTrackingPosition(position, bias);
}
@Override
public TrackingPosition createTrackingPosition(int position, TrackingPosition.Bias bias, TrackingFidelity fidelity) {
return textVersion.createTrackingPosition(position, bias, fidelity);
}
@Override
public TrackingPositionRegion createTrackingRegion(OffsetRegion span, TrackingPositionRegion.Bias bias) {
return textVersion.createTrackingRegion(span, bias);
}
@Override
public TrackingPositionRegion createTrackingRegion(int start, int length, TrackingPositionRegion.Bias bias) {
return textVersion.createTrackingRegion(start, length, bias);
}
@Override
public TrackingPositionRegion createTrackingRegion(OffsetRegion span, TrackingPositionRegion.Bias bias, TrackingFidelity fidelity) {
return textVersion.createTrackingRegion(span, bias, fidelity);
}
@Override
public TrackingPositionRegion createTrackingRegion(int start, int length, TrackingPositionRegion.Bias bias, TrackingFidelity fidelity) {
return textVersion.createTrackingRegion(start, length, bias);
}
@Override
public DocumentSnapshotLine findLineFromLineNumber(int lineNumber) {
if (lineNumber < 0 || lineNumber >= getLineCount()) {
throw new IndexOutOfBoundsException();
}
return new NbDocumentSnapshotLine(this, lineNumber);
}
@Override
public DocumentSnapshotLine findLineFromOffset(int offset) {
return findLineFromLineNumber(findLineNumber(offset));
}
@Override
public int findLineNumber(int offset) {
return getVersion().getLineData().getLineNumberFromPosition(offset);
}
@Override
public int length() {
return getVersion().getLineData().getLength();
}
@Override
public char charAt(int index) {
if (index < 0 || index >= length()) {
throw new IndexOutOfBoundsException();
}
LineTextCache lineData = getVersion().getLineData();
int block = lineData.getBlockFromPosition(index);
int line = lineData.getLineNumberFromPosition(index);
int lineStart = lineData.getLineStart(block, line);
String lineText = lineData.getLineText(block, line);
return lineText.charAt(index - lineStart);
}
@Override
public CharSequence subSequence(int start, int end) {
return new SubSequence(this, start, end);
}
@Override
public String getText() {
return subSequence(0, length()).toString();
}
@Override
public String toString() {
String content = "???";
return String.format("version: %d lines: %d length: %d\n content: %s",
getVersion().getVersionNumber(),
getLineCount(),
length(),
content);
}
private static final class LineIterable implements Iterable<DocumentSnapshotLine> {
@NonNull
private final NbDocumentSnapshot snapshot;
public LineIterable(@NonNull NbDocumentSnapshot snapshot) {
Parameters.notNull("snapshot", snapshot);
this.snapshot = snapshot;
}
@Override
public Iterator<DocumentSnapshotLine> iterator() {
return new LineIterator(snapshot);
}
}
private static class LineIterator implements Iterator<DocumentSnapshotLine> {
@NonNull
private final NbDocumentSnapshot snapshot;
private int currentLine = -1;
public LineIterator(@NonNull NbDocumentSnapshot snapshot) {
Parameters.notNull("snapshot", snapshot);
this.snapshot = snapshot;
}
@Override
public boolean hasNext() {
return currentLine < snapshot.getLineCount();
}
@Override
public DocumentSnapshotLine next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
currentLine++;
return new NbDocumentSnapshotLine(snapshot, currentLine);
}
@Override
public void remove() {
throw new UnsupportedOperationException("The iterator is read only.");
}
}
private static final class SubSequence implements CharSequence {
@NonNull
private final NbDocumentSnapshot snapshot;
private final int start;
private final int end;
public SubSequence(@NonNull NbDocumentSnapshot snapshot, int start, int end) {
Parameters.notNull("snapshot", snapshot);
if (start < 0 || end > snapshot.length()) {
throw new IndexOutOfBoundsException();
}
if (end < start) {
throw new IllegalArgumentException();
}
assert start <= snapshot.length();
this.snapshot = snapshot;
this.start = start;
this.end = end;
}
@Override
public int length() {
return end - start;
}
@Override
public char charAt(int index) {
if (index < 0 || index >= length()) {
throw new IndexOutOfBoundsException();
}
return snapshot.charAt(start + index);
}
@Override
public CharSequence subSequence(int start, int end) {
if (end > length()) {
throw new IndexOutOfBoundsException();
}
SubSequence result = new SubSequence(snapshot, this.start + start, this.start + end);
assert start >= 0 && end >= start : "should have thrown exception";
return result;
}
@Override
public String toString() {
if (length() == 0) {
return "";
}
StringBuilder builder = new StringBuilder(length());
LineTextCache data = snapshot.textVersion.getLineData();
int startBlock = data.getBlockFromPosition(start);
int startLine = data.getBlockLineFromPosition(startBlock, start);
int startColumn = start - data.getLineStart(startBlock, data.getBlockLineOffsets().get(startBlock) + startLine);
int endBlock = end == start ? startBlock : data.getBlockFromPosition(end - 1);
int endLine = end == start ? startLine : data.getBlockLineFromPosition(endBlock, end - 1);
int endColumn = end == start ? startColumn : (end - 1) - data.getLineStart(endBlock, data.getBlockLineOffsets().get(endBlock) + endLine);
for (int block = startBlock; block <= endBlock; block++) {
List<String> blockData = data.getLineData().get(block);
for (int line = (block == startBlock) ? startLine : 0; line < ((block == endBlock) ? endLine + 1 : blockData.size()); line++) {
@SuppressWarnings("LocalVariableHidesMemberVariable")
int start = 0;
@SuppressWarnings("LocalVariableHidesMemberVariable")
int end = blockData.get(line).length();
if (block == startBlock && line == startLine) {
start = startColumn;
}
if (block == endBlock && line == endLine) {
end = endColumn + 1;
}
builder.append(blockData.get(line).substring(start, end));
}
}
return builder.toString();
}
}
}