/*******************************************************************************
* Copyright (c) 2000, 2008 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.python.pydev.shared_core.partitioner;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.python.pydev.shared_core.string.FastStringBuffer;
import org.python.pydev.shared_core.structure.FastStack;
/**
* A buffered rule based scanner. The buffer always contains a section
* of a fixed size of the document to be scanned. Completely adheres to
* the contract of <code>RuleBasedScanner</code>.
*/
public abstract class AbstractCustomBufferedRuleBasedScanner extends AbstractCustomRuleBasedScanner
implements IMarkScanner, IContentsScanner {
/** The default buffer size. Value = 2000 -- note: default was 500 in original */
private final static int DEFAULT_BUFFER_SIZE = 2000;
/** The actual size of the buffer. Initially set to <code>DEFAULT_BUFFER_SIZE</code> */
private int fBufferSize = DEFAULT_BUFFER_SIZE;
/** The buffer */
private char[] fBuffer = new char[DEFAULT_BUFFER_SIZE];
/** The offset of the document at which the buffer starts */
private int fStart;
/** The offset of the document at which the buffer ends */
private int fEnd;
/** The cached length of the document */
private int fDocumentLength;
private int lastRegexpMatchOffset;
public void setLastRegexpMatchOffset(int endOffset) {
this.lastRegexpMatchOffset = endOffset;
}
public int getLastRegexpMatchOffset() {
return lastRegexpMatchOffset;
}
/**
* Creates a new buffered rule based scanner which does
* not have any rule and a default buffer size of 500 characters.
*/
protected AbstractCustomBufferedRuleBasedScanner() {
super();
}
/**
* Creates a new buffered rule based scanner which does
* not have any rule. The buffer size is set to the given
* number of characters.
*
* @param size the buffer size
*/
public AbstractCustomBufferedRuleBasedScanner(int size) {
super();
setBufferSize(size);
}
/**
* Sets the buffer to the given number of characters.
*
* @param size the buffer size
*/
protected void setBufferSize(int size) {
Assert.isTrue(size > 0);
fBufferSize = size;
fBuffer = new char[size];
}
/**
* Shifts the buffer so that the buffer starts at the
* given document offset.
*
* @param offset the document offset at which the buffer starts
*/
private void shiftBuffer(int offset) {
fStart = offset;
fEnd = fStart + fBufferSize;
if (fEnd > fDocumentLength) {
fEnd = fDocumentLength;
}
try {
String content = fDocument.get(fStart, fEnd - fStart);
content.getChars(0, fEnd - fStart, fBuffer, 0);
} catch (BadLocationException x) {
}
}
/*
* @see RuleBasedScanner#setRange(IDocument, int, int)
*/
@Override
public void setRange(IDocument document, int offset, int length) {
super.setRange(document, offset, length);
fDocumentLength = document.getLength();
shiftBuffer(offset);
}
@Override
public int getMark() {
return fOffset;
}
@Override
public void getContents(int offset, int length, FastStringBuffer buffer) {
buffer.resizeForMinimum(buffer.length() + length);
int mark = this.getMark();
this.setMark(offset);
try {
for (int i = 0; i < length; i++) {
buffer.append((char) this.read());
}
} finally {
this.setMark(mark);
}
}
@Override
public void setMark(int offset) {
fOffset = offset;
fColumn = UNDEFINED;
if (fOffset == fStart) {
shiftBuffer(Math.max(0, fStart - (fBufferSize / 2)));
} else if (fOffset == fEnd) {
shiftBuffer(fEnd);
} else if (fOffset < fStart || fEnd < fOffset) {
shiftBuffer(fOffset);
}
}
// Support for temporarily pushing a sub-range during a partitioning.
private FastStack<TempStacked> rangeStack = new FastStack<>(3);
private static class TempStacked {
private int offset;
private int rangeEnd;
private int lastRegexpMatchOffset;
public TempStacked(int offset, int rangeEnd, int lastRegexpMatchOffset) {
this.offset = offset;
this.rangeEnd = rangeEnd;
this.lastRegexpMatchOffset = lastRegexpMatchOffset;
}
}
public void pushRange(int offset, int len) {
rangeStack.push(new TempStacked(fOffset, fRangeEnd, lastRegexpMatchOffset));
this.fOffset = offset;
this.fRangeEnd = offset + len;
this.setMark(fOffset);
}
public void popRange() {
TempStacked pop = rangeStack.pop();
this.fOffset = pop.offset;
this.fRangeEnd = pop.rangeEnd;
//Although it's not changed at push, it must be restored.
this.lastRegexpMatchOffset = pop.lastRegexpMatchOffset;
this.setMark(fOffset);
}
/*
* @see RuleBasedScanner#read()
*/
@Override
public int read() {
fColumn = UNDEFINED;
if (fOffset >= fRangeEnd) {
++fOffset;
return EOF;
}
if (fOffset == fEnd) {
shiftBuffer(fEnd);
} else if (fOffset < fStart || fEnd < fOffset) {
shiftBuffer(fOffset);
}
return fBuffer[fOffset++ - fStart];
}
/*
* @see RuleBasedScanner#unread()
*/
@Override
public void unread() {
if (fOffset == fStart) {
shiftBuffer(Math.max(0, fStart - (fBufferSize / 2)));
}
--fOffset;
fColumn = UNDEFINED;
}
}