content: /*******************************************************************************
   * Copyright (c) 2000, 2007 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.eclipse.wst.jsdt.internal.ui.text;
  
  
  import java.io.IOException;
  
  import org.eclipse.wst.jsdt.internal.ui.text.html.SingleCharReader;
  import org.eclipse.jface.text.BadLocationException;
  import org.eclipse.jface.text.IDocument;
  
  
  /**
   * Reads from a document either forwards or backwards. May be configured to
   * skip comments and strings.
   */
  public class JavaCodeReader extends SingleCharReader {
  
  	/** The EOF character */
  	public static final int EOF= -1;
  
  	private boolean fSkipComments= false;
  	private boolean fSkipStrings= false;
  	private boolean fForward= false;
  
  	private IDocument fDocument;
  	private int fOffset;
  
  	private int fEnd= -1;
  	private int fCachedLineNumber= -1;
  	private int fCachedLineOffset= -1;
  
  
  	public JavaCodeReader() {
  	}
  
  	/**
  	 * Returns the offset of the last read character. Should only be called after read has been called.
  	 */
  	public int getOffset() {
  		return fForward ? fOffset -1 : fOffset;
  	}
  
  	public void configureForwardReader(IDocument document, int offset, int length, boolean skipComments, boolean skipStrings) throws IOException {
  		fDocument= document;
  		fOffset= offset;
  		fSkipComments= skipComments;
  		fSkipStrings= skipStrings;
  
  		fForward= true;
  		fEnd= Math.min(fDocument.getLength(), fOffset + length);
  	}
  
  	public void configureBackwardReader(IDocument document, int offset, boolean skipComments, boolean skipStrings) throws IOException {
  		fDocument= document;
  		fOffset= offset;
  		fSkipComments= skipComments;
  		fSkipStrings= skipStrings;
  
  		fForward= false;
  		try {
  			fCachedLineNumber= fDocument.getLineOfOffset(fOffset);
  		} catch (BadLocationException x) {
  			throw new IOException(x.getMessage());
  		}
  	}
  
  	/*
  	 * @see Reader#close()
  	 */
  	public void close() throws IOException {
  		fDocument= null;
  	}
  
  	/*
  	 * @see SingleCharReader#read()
  	 */
  	public int read() throws IOException {
  		try {
  			return fForward ? readForwards() : readBackwards();
  		} catch (BadLocationException x) {
  			throw new IOException(x.getMessage());
  		}
  	}
  
  	private void gotoCommentEnd() throws BadLocationException {
  		while (fOffset < fEnd) {
  			char current= fDocument.getChar(fOffset++);
  			if (current == '*') {
  				if (fOffset < fEnd && fDocument.getChar(fOffset) == '/') {
  					++ fOffset;
  					return;
  				}
  			}
  		}
  	}
  
  	private void gotoStringEnd(char delimiter) throws BadLocationException {
  		while (fOffset < fEnd) {
  			char current= fDocument.getChar(fOffset++);
  			if (current == '\\') {
  				// ignore escaped characters
  				++ fOffset;
  			} else if (current == delimiter) {
  				return;
  			}
  		}
  	}
  
  	private void gotoLineEnd() throws BadLocationException {
  		int line= fDocument.getLineOfOffset(fOffset);
  		fOffset= fDocument.getLineOffset(line + 1);
  	}
  
  	private int readForwards() throws BadLocationException {
  		while (fOffset < fEnd) {
  			char current= fDocument.getChar(fOffset++);
  
  			switch (current) {
  				case '/':
  
  					if (fSkipComments && fOffset < fEnd) {
  						char next= fDocument.getChar(fOffset);
  						if (next == '*') {
  							// a comment starts, advance to the comment end
  							++ fOffset;
  							gotoCommentEnd();
  							continue;
  						} else if (next == '/') {
  							// '//'-comment starts, advance to the line end
  							gotoLineEnd();
  							continue;
  						}
  					}
  
  					return current;
  
  				case '"':
  				case '\'':
  
  					if (fSkipStrings) {
  						gotoStringEnd(current);
  						continue;
  					}
  
  					return current;
  			}
  
  			return current;
  		}
  
  		return EOF;
  	}
  
  	private void handleSingleLineComment() throws BadLocationException {
  		int line= fDocument.getLineOfOffset(fOffset);
  		if (line < fCachedLineNumber) {
  			fCachedLineNumber= line;
  			fCachedLineOffset= fDocument.getLineOffset(line);
  			int offset= fOffset;
  			while (fCachedLineOffset < offset) {
  				char current= fDocument.getChar(offset--);
  				if (current == '/' && fCachedLineOffset <= offset && fDocument.getChar(offset) == '/') {
  					fOffset= offset;
  					return;
  				}
  			}
  		}
  	}
  
  	private void gotoCommentStart() throws BadLocationException {
  		while (0 < fOffset) {
  			char current= fDocument.getChar(fOffset--);
  			if (current == '*' && 0 <= fOffset && fDocument.getChar(fOffset) == '/')
  				return;
  		}
  	}
  
  	private void gotoStringStart(char delimiter) throws BadLocationException {
  		while (0 < fOffset) {
  			char current= fDocument.getChar(fOffset);
  			if (current == delimiter) {
  				if ( !(0 <= fOffset && fDocument.getChar(fOffset -1) == '\\'))
  					return;
  			}
  			-- fOffset;
  		}
  	}
  
  	private int readBackwards() throws BadLocationException {
  
  		while (0 < fOffset) {
  			-- fOffset;
  
  			handleSingleLineComment();
  
  			char current= fDocument.getChar(fOffset);
  			switch (current) {
  				case '/':
  
  					if (fSkipComments && fOffset > 1) {
  						char next= fDocument.getChar(fOffset - 1);
  						if (next == '*') {
  							// a comment ends, advance to the comment start
  							fOffset -= 2;
  							gotoCommentStart();
  							continue;
  						}
  					}
  
  					return current;
  
  				case '"':
  				case '\'':
  
  					if (fSkipStrings) {
  						-- fOffset;
  						gotoStringStart(current);
  						continue;
  					}
  
  					return current;
  			}
  
  			return current;
  		}
  
  		return EOF;
  	}
  }
  
  