/*******************************************************************************
* Copyright (c) 2010, 2011 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:
* Bruno Medeiros - initial API and implementation
*******************************************************************************/
package melnorme.lang.ide.core.text;
import static melnorme.utilbox.core.Assert.AssertNamespace.assertFail;
import static melnorme.utilbox.core.Assert.AssertNamespace.assertNotNull;
import static melnorme.utilbox.core.CoreUtil.tryCast;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.BadPartitioningException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentExtension3;
import org.eclipse.jface.text.IDocumentPartitioner;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.TypedRegion;
/**
* Helper abstract class to scan a document, such that it is aware of document partitions.
* With a partitioning there must be a partitioner for that partitioning, and document must be a IDocumentExtension3
*/
public class AbstractDocumentScanner {
public static int TOKEN_EOF = -1;
public static int TOKEN_INVALID = -2;
public static int TOKEN_OUTSIDE = -3; // Token for whole partitions that we skip over
protected final IDocument document;
protected final String source;
protected final String partitioning;
protected final IDocumentExtension3 documentExt3;
protected final IDocumentPartitioner partitioner;
/** the partition type for the partitions that the document will scan */
protected final String contentType;
/** the current position. */
protected int pos;
/** the limit position, where the scanner will not scan beyond. */
protected int posLimit;
/** the last read token. */
protected int token;
/** last accessed partition (for caching purposes) */
protected ITypedRegion lastPartition = new TypedRegion(-1, 0, "__no_partition_at_all"); // init with empty value
protected AbstractDocumentScanner(IDocument document, String partitioning, String contentType) {
this.document = assertNotNull(document);
this.partitioning = partitioning;
this.source = document.get();
if(partitioning == null) {
this.contentType = IDocument.DEFAULT_CONTENT_TYPE;
this.documentExt3 = null;
this.partitioner = null;
} else {
Assert.isLegal(partitioning != null);
Assert.isLegal(contentType != null);
this.contentType = contentType;
this.documentExt3 = tryCast(document, IDocumentExtension3.class);
Assert.isLegal(documentExt3 != null, "document must support IDocumentExtension3");
this.partitioner = documentExt3.getDocumentPartitioner(partitioning);
Assert.isLegal(partitioner != null, "document must have a partitioner for " + partitioning);
}
}
public IDocument getDocument() {
return document;
}
public int getPosition() {
return pos;
}
public int getLastToken() {
return token;
}
protected void setPosition(int pos) {
this.pos = pos;
}
public final void setScanRange(int pos, int posLimit) {
this.pos = pos;
this.posLimit = posLimit;
}
protected final int getSourceLength() {
return document.getLength();
}
protected final int readPreviousCharacter() {
if(pos <= posLimit) {
return token = TOKEN_EOF;
} else {
ITypedRegion partition;
try {
partition = getPartition(pos-1);
} catch(BadLocationException e) {
return token = TOKEN_OUTSIDE;
}
pos--;
if (contentType.equals(partition.getType())) {
return token = source.charAt(pos);
} else {
pos = partition.getOffset();
return token = TOKEN_OUTSIDE;
}
}
}
public final int readNextCharacter() {
if(pos >= posLimit) {
return token = TOKEN_EOF;
} else {
int charPos = pos;
ITypedRegion partition;
try {
partition = getPartition(charPos);
} catch(BadLocationException e) {
return token = TOKEN_OUTSIDE;
}
pos++;
if (contentType.equals(partition.getType())) {
return token = source.charAt(charPos);
} else {
pos = partition.getOffset() + partition.getLength();
return token = TOKEN_OUTSIDE;
}
}
}
public final void revertPreviousCharacter() {
pos++;
}
public final void revertNextCharacter() {
pos--;
}
public ITypedRegion getPartition(int position) throws BadLocationException {
if(!partitionContainsPosition(lastPartition, position)) {
if(documentExt3 != null) {
try {
lastPartition = documentExt3.getPartition(partitioning, position, false);
} catch (BadPartitioningException e) {
throw assertFail(); // Cannot happen, we have ensured that partitioning exists
}
} else {
lastPartition = document.getPartition(position);
}
}
return lastPartition;
}
public static boolean partitionContainsPosition(IRegion region, int position) {
return region.getOffset() <= position && position < region.getOffset() + region.getLength();
}
}