/*
 * FindBugs - Find Bugs in Java programs
 * Copyright (C) 2006, University of Maryland
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307, USA
 */

package edu.umd.cs.findbugs.sourceViewer;

import java.text.CharacterIterator;
import java.util.HashSet;

public class JavaScanner {
	public final static int NORMAL_TEXT = 0;
	public final static int COMMENT= 1;
	public final static int JAVADOC = 2;

	public final static int KEYWORD = 3;
	public final static int QUOTE= 4;
	public final static int EOF = -1;




	private final static HashSet<String> KEYWORDS = new HashSet<String>();

	private final static int MAX_KEYWORD_LENGTH;





	static {
		String[] keywordList = new String[] { "abstract", "assert", "boolean",
				"break", "byte", "case", "catch", "char", "class", "const",
				"continue", "default", "do", "double", "else", "enum",
				"extends", "false", "final", "finally", "float", "for", "goto",
				"if", "implements", "import", "instanceof", "int", "interface",
				"long", "native", "new", "null", "package", "private",
				"protected", "public", "return", "short", "static", "strictfp",
				"super", "switch", "synchronized", "this", "throw", "throws",
				"transient", "true", "try", "void", "volatile", "while" };
		int max = 0;
		for (String s : keywordList) {
			if (max < s.length())
				max = s.length();
			KEYWORDS.add(s);
		}
		MAX_KEYWORD_LENGTH = max;
	}

	private final StringBuilder buf = new StringBuilder();


	private final CharacterIterator iterator;

	private int kind;

	private int startPosition;

	public JavaScanner(CharacterIterator iterator) {
		this.iterator = iterator;
	}

	public int getStartPosition() {
		return startPosition;
	}

	public int getEndPosition() {
		return iterator.getIndex();
	}

	public int getLength() {
		return  iterator.getIndex() - startPosition;
	}
	public int getKind() {
		return kind;
	}
	public int next() {
		startPosition = iterator.getIndex();
		char c = iterator.current();
		iterator.next(); // advance
		if (c == CharacterIterator.DONE) {
			kind = EOF;
		}
		else if (Character.isJavaIdentifierStart(c)) {
			buf.append(c);
			boolean couldBeKeyword = Character.isLowerCase(c);
			while (true) {
				c = iterator.current();
				if (!Character.isJavaIdentifierPart(c))
					break;
				buf.append(c);
				if (couldBeKeyword) {
					if (!Character.isLowerCase(c)
							|| buf.length() > MAX_KEYWORD_LENGTH)
						couldBeKeyword = false;
				}
				c = iterator.next();
			}
			kind = NORMAL_TEXT;
			if (couldBeKeyword) {
				if (KEYWORDS.contains(buf.toString()))
					kind = KEYWORD;
			}
			buf.setLength(0);
		} else if (c == '/') {
			char c2 = iterator.current();
			if (c2 == '/') {
				while (true) {
					c2 = iterator.next();
					if (c2 == '\n' || c2 == '\r' || c2 == CharacterIterator.DONE)
						break;
				}
				kind = COMMENT;
				return kind;
			} else if (c2 == '*') {
				scanComment: while (c2 != CharacterIterator.DONE) {
					c2 = iterator.next();
					if (c2 == '*') {
						do {
						c2 = iterator.next();
						if (c2 == '/')
							break scanComment;
						} while(c2 == '*');
					}
				}
				kind = JAVADOC;
				return kind;
			}
		} else if (c == '"') {
			kind = QUOTE;
			char c2 = iterator.current();
			while (c2 != '"' && c2 != '\n' && c2 != '\r' && c2 != CharacterIterator.DONE) {
				if (c2 == '\\') {
					c2 = iterator.next();
					if (c2 == '\n' || c2 == '\r')
						break;
				}
				c2 = iterator.next();
			}
			iterator.next(); // advance past closing char
		} else if (c == '\'') {
			 // need to catch '"' so isn't considered to start a String
			kind = QUOTE; // or NORMAL_TEXT ?
			char c2 = iterator.current();
			if (c2 == '\\') c2 = iterator.next(); // advance past the escape char
			if (c2 != '\n' && c2 != '\r' && c2 != CharacterIterator.DONE)
				c2 = iterator.next(); // advance past the content char
			if (c2 != '\n' && c2 != '\r' && c2 != CharacterIterator.DONE)
				iterator.next(); // advance past closing char

		} else
			kind = NORMAL_TEXT;
		// System.out.println(kind + " " + startPosition + "-" + iterator.getIndex());
		return kind;
	}

}
