/* * Copyright (c) 2003-2004 Christopher Lenz and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Common Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/cpl-v10.html * * Contributors: * Christopher Lenz - initial API and implementation * * $Id: CssCodeReader.java,v 1.1 2004-09-02 18:07:13 jsurfer Exp $ */ package net.sourceforge.phpeclipse.css.core.internal.text; import java.io.IOException; import java.io.Reader; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; /** * */ public class CssCodeReader extends Reader { // Constants --------------------------------------------------------------- /** * Constant for configuring a reader to read the document backwards. */ public static final int BACKWARD = -1; /** * Constant for configuring a reader to read the document forwards. */ public static final int FORWARD = 1; // Instance Variables ------------------------------------------------------ /** * The document to read. Is null when the reader is closed. */ private IDocument document; /** * The direction in which the document should be read. Can be either * {@link CssCodeReader#BACKWARD} or {@link CssCodeReader#FORWARD}. */ private int direction; /** * The offset at which to start reading. */ private int offset; /** * The offset at which to end reading. */ private int end = -1; /** * Whether comments should be skipped. */ private boolean skipComments; /** * Whether string literals should be skipped. */ private boolean skipStrings; /** * Whether whitespace characters should be skipped. */ private boolean skipWhitespace; // Constructors ------------------------------------------------------------ /** * Constructor. * * @param document the document to read * @param offset the offset at which to start reading */ public CssCodeReader(IDocument document, int offset) { this(document, offset, document.getLength() - offset, FORWARD); } /** * Constructor. * * @param document the document to read * @param offset the offset at which to start reading * @param length the number of characters to read at most */ public CssCodeReader(IDocument document, int offset, int length) { this(document, offset, length, FORWARD); } /** * Constructor. * * @param document the document to read * @param offset the offset at which to start reading * @param length the number of characters to read at most * @param direction the reading direction (either * {@link CssCodeReader#BACKWARD} or {@link CssCodeReader#FORWARD}) */ public CssCodeReader(IDocument document, int offset, int length, int direction) { this(document, offset, length, direction, true, false, false); } /** * Constructor. * * @param document the document to read * @param offset the offset at which to start reading * @param length the number of characters to read at most * @param direction the reading direction (either * {@link CssCodeReader#BACKWARD} or {@link CssCodeReader#FORWARD}) * @param skipComments whether the reader should skip comments * @param skipStrings whether the reader should skip strings * @param skipWhitespace whether the reader should skip whitespace */ public CssCodeReader(IDocument document, int offset, int length, int direction, boolean skipComments, boolean skipStrings, boolean skipWhitespace) { if ((direction != BACKWARD) && (direction != FORWARD)) { throw new IllegalArgumentException(); } if ((offset < 0) || (offset > document.getLength())) { throw new IllegalArgumentException(); } this.document = document; this.offset = offset; this.direction = direction; if (direction == FORWARD) { end = Math.min(document.getLength(), offset + length); } else { end = Math.max(0, offset - length); } this.skipComments = skipComments; this.skipStrings = skipStrings; this.skipWhitespace = skipWhitespace; } // Reader Implementation --------------------------------------------------- /** * @see Reader#close() */ public void close() { document = null; } /** * @see Reader#read(char[], int, int) */ public int read(char[] cbuf, int off, int len) throws IOException { for (int i = off; i < off + len; i++) { int c = read(); if (c == -1) { if (i == off) { return -1; } else { return i - off; } } cbuf[i] = (char) c; } return len; } /** * @see Reader#read() */ public int read() throws IOException { if (document == null) { throw new IllegalStateException(); } try { if (direction == FORWARD) { return readForwards(); } else { return readBackwards(); } } catch (BadLocationException ble) { throw new IOException(ble.getMessage()); } } // Public Methods ---------------------------------------------------------- /** * Returns the document that is being read. * * @return The document */ public IDocument getDocument() { return document; } /** * Returns the offset of the last read character. Should only be called * after read has been called. * * @return The offset of the last read character */ public int getOffset() { return (direction == FORWARD) ? offset - 1 : offset; } /** * Returns whether the reader is currently configured to skip comments. * * @return true if the reader is skipping comments and * false otherwise */ public boolean isSkippingComments() { return skipComments; } /** * Configures the reader to skip comment blocks. * * @param skipComments Whether comments should be skipped */ public void setSkipComments(boolean skipComments) { this.skipComments = skipComments; } /** * Returns whether the reader is currently configured to skip string * literals. * * @return true if the reader is skipping strings and * false otherwise */ public boolean isSkippingStrings() { return skipStrings; } /** * Configures the reader to skip strings. * * @param skipStrings Whether strings should be skipped */ public void setSkipStrings(boolean skipStrings) { this.skipStrings = skipStrings; } /** * Returns whether the reader is currently configured to skip whitespace. * * @return true if the reader is skipping whitespace and * false otherwise */ public boolean isSkippingWhitespace() { return skipWhitespace; } /** * Configures the reader to skip whitespace. * * @param skipWhitespace Whether whitespace should be skipped */ public void setSkipWhitespace(boolean skipWhitespace) { this.skipWhitespace = skipWhitespace; } // Private Methods --------------------------------------------------------- private int readBackwards() throws BadLocationException { while (offset > 0) { offset--; char current = document.getChar(offset); switch (current) { case '/': { if (skipComments && (offset > 1)) { char next = document.getChar(offset - 1); if (next == '*') { // a comment ends, advance to the comment start offset -= 2; skipUntilStartOfComment(); continue; } } break; } case '"': case '\'': { if (skipStrings) { offset--; skipUntilStartOfString(current); continue; } break; } default: { if (skipWhitespace && Character.isWhitespace(current)) { continue; } } } return current; } return -1; } private int readForwards() throws BadLocationException { while (offset < end) { char current = document.getChar(offset++); switch (current) { case '/': { if (skipComments && (offset < end)) { char next = document.getChar(offset); if (next == '*') { // a comment starts, advance to the comment end offset++; skipUntilEndOfComment(); continue; } } break; } case '"': case '\'': { if (skipStrings) { skipUntilEndOfString(current); continue; } break; } default: { if (skipWhitespace && Character.isWhitespace(current)) { offset++; continue; } } } return current; } return -1; } private void skipUntilStartOfComment() throws BadLocationException { while (offset > 0) { char current = document.getChar(offset--); if ((current == '*') && (0 <= offset) && (document.getChar(offset) == '/')) { return; } } } private void skipUntilEndOfComment() throws BadLocationException { while (offset < end) { char current = document.getChar(offset++); if (current == '*') { if ((offset < end) && (document.getChar(offset) == '/')) { offset++; return; } } } } private void skipUntilStartOfString(char delimiter) throws BadLocationException { while (offset > 0) { char current = document.getChar(offset); if (current == delimiter) { if (!((0 <= offset) && document.getChar(offset - 1) == '\\')) { return; } } offset--; } } private void skipUntilEndOfString(char delimiter) throws BadLocationException { while (offset < end) { char current = document.getChar(offset++); if (current == '\\') { // ignore escaped characters offset++; } else if (current == delimiter) { return; } } } }