/******************************************************************************* * 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(); } }