/* * 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.works.editor.antlr3.highlighting; import java.util.ArrayList; import java.util.List; import javax.swing.text.BadLocationException; import javax.swing.text.StyledDocument; import org.antlr.netbeans.editor.text.OffsetRegion; import org.antlr.runtime.CharStream; import org.antlr.runtime.Token; import org.openide.text.NbDocument; /** * * @author Sam Harwell */ public class DocumentCharStream implements CharStream { private boolean explicitCache; private int currentSnapshotLineStartIndex; private String currentSnapshotLine; private final StyledDocument document; private int markDepth; private List<CharStreamState> markers; private int lastMarker; private int line; private int charPositionInLine; private int index; public DocumentCharStream(StyledDocument document) { this.document = document; //updateCachedLine(); } public DocumentCharStream(StyledDocument document, OffsetRegion cachedSpan) throws BadLocationException { this.document = document; this.explicitCache = true; this.currentSnapshotLineStartIndex = cachedSpan.getStart(); this.currentSnapshotLine = document.getText(cachedSpan.getStart(), cachedSpan.getLength()).toString(); } @Override public int getCharPositionInLine() { return charPositionInLine; } @Override public void setCharPositionInLine(int value) { charPositionInLine = value; } @Override public int getLine() { return line; } @Override public void setLine(int value) { line = value; } @Override public int LT(int i) { return LA(i); } @Override public String substring(int startIndex, int endIndexInclusive) { if (currentSnapshotLine != null) { if (startIndex >= currentSnapshotLineStartIndex && (endIndexInclusive + 1) <= currentSnapshotLineStartIndex + currentSnapshotLine.length()) { return currentSnapshotLine.substring(startIndex - currentSnapshotLineStartIndex, endIndexInclusive - currentSnapshotLineStartIndex + 1); } } try { return document.getText(startIndex, endIndexInclusive - startIndex + 1).toString(); } catch (BadLocationException ex) { return null; } } @Override public void consume() { int la = LA(1); if (la < 0) return; if (la == '\n') { setLine(getLine() + 1); setCharPositionInLine(0); } else { setCharPositionInLine(getCharPositionInLine() + 1); } index++; updateCachedLine(); } @Override public int LA(int i) { if (i == 0) { // undefined return 0; } if (i < 0) { // e.g. translate LA(-1) to use offset i=0, then data[p+0-1] i++; if ((index() + i - 1) < 0) { // invalid; no char before first char return Token.EOF; } } if ((index() + i - 1) >= size()) { return Token.EOF; } int actualIndex = index() + i - 1; if (currentSnapshotLine != null && actualIndex >= currentSnapshotLineStartIndex && actualIndex < currentSnapshotLineStartIndex + currentSnapshotLine.length()) { return currentSnapshotLine.charAt(actualIndex - currentSnapshotLineStartIndex); } try { return document.getText(actualIndex, 1).charAt(0); } catch (BadLocationException ex) { return Token.EOF; } } @Override public int mark() { if (markers == null) { markers = new ArrayList<>(); // depth 0 means no backtracking, leave blank markers.add(null); } markDepth++; CharStreamState state; if (markDepth >= markers.size()) { state = new CharStreamState(); markers.add(state); } else { state = markers.get(markDepth); } state.index = index(); state.line = getLine(); state.charPositionInLine = getCharPositionInLine(); lastMarker = markDepth; return markDepth; } @Override public int index() { return index; } @Override public void rewind(int marker) { CharStreamState state = markers.get(marker); // restore stream state (don't use seek() because it calls updateCachedLine() unnecessarily) index = state.index; setLine(state.line); setCharPositionInLine(state.charPositionInLine); release(marker); updateCachedLine(); } @Override public void rewind() { rewind(lastMarker); } @Override public void release(int marker) { // unwind any other markers made after m and release m markDepth = marker; // release this marker markDepth--; } @Override public void seek(int index) { if (this.index() == index) { return; } this.index = index; this.line = NbDocument.findLineNumber(document, index); this.charPositionInLine = NbDocument.findLineColumn(document, index); updateCachedLine(); } @Override public int size() { return document.getLength(); } @Override public String getSourceName() { return "NbEditor"; } private void updateCachedLine() { if (explicitCache) return; if (currentSnapshotLine == null || index() < currentSnapshotLineStartIndex || index() >= currentSnapshotLineStartIndex + currentSnapshotLine.length()) { if (index() >= 0 && index() < size()) { int currentLine = NbDocument.findLineNumber(document, index()); currentSnapshotLineStartIndex = NbDocument.findLineOffset(document, currentLine); int endIndex = (currentLine < NbDocument.findLineRootElement(document).getElementCount() - 1) ? NbDocument.findLineOffset(document, currentLine + 1) : document.getLength(); try { currentSnapshotLine = document.getText(currentSnapshotLineStartIndex, endIndex - currentSnapshotLineStartIndex).toString(); } catch (BadLocationException ex) { } } else { currentSnapshotLine = null; currentSnapshotLineStartIndex = 0; } } } private static class CharStreamState { int index; int line; int charPositionInLine; } }