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

}
