/*
* Copyright 2004-2015 the Seasar Foundation and the Others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package org.seasar.extension.jdbc.util;
import org.seasar.extension.jdbc.exception.SingleQuoteNotCloseRuntimeException;
/**
* トークンを認識するクラスです。
*
* @author higa
*
*/
public class QueryTokenizer {
/**
* EOFをあらわします。
*/
public static final int TT_EOF = -1;
/**
* 単語をあらわします。
*/
public static final int TT_WORD = -2;
/**
* 引用文をあらわします。
*/
public static final int TT_QUOTE = -3;
/**
* 単語、引用文以外をあらわします。
*/
public static final int TT_OTHER = -4;
private String str;
private int pos;
private int nextPos;
private int length;
private String token;
private int type = TT_EOF;
/**
* {@link QueryTokenizer}を作成します。
*
* @param str
* ソースの文字列
*/
public QueryTokenizer(String str) {
this.str = str;
length = str.length();
peek(0);
}
/**
* 次のトークンを前もって調べます。
*
* @param index
* インデックス
*/
protected void peek(int index) {
if (index < length) {
char c = str.charAt(index);
if (Character.isWhitespace(c) || isOrdinary(c)) {
type = TT_OTHER;
} else if (c == '\'') {
type = TT_QUOTE;
} else {
type = TT_WORD;
}
pos = index;
nextPos = index + 1;
} else {
type = TT_EOF;
}
}
/**
* トークンのタイプを返します。
*
* @return トークンのタイプ
*/
public int nextToken() {
if (type == TT_EOF) {
return TT_EOF;
}
if (type == TT_QUOTE) {
for (int i = nextPos; i < length; i++) {
char c = str.charAt(i);
if (c == '\'') {
i++;
if (i >= length) {
token = str.substring(pos, i);
type = TT_EOF;
return TT_QUOTE;
} else if (str.charAt(i) != '\'') {
token = str.substring(pos, i);
peek(i);
return TT_QUOTE;
}
}
}
throw new SingleQuoteNotCloseRuntimeException(str);
}
for (int i = nextPos; i < length; i++) {
char c = str.charAt(i);
if (isOther(c)) {
if (type == TT_WORD) {
token = str.substring(pos, i);
pos = i;
nextPos = pos + 1;
type = TT_OTHER;
return TT_WORD;
}
} else if (c == '\'') {
if (type == TT_WORD) {
token = str.substring(pos, i);
pos = i;
nextPos = pos + 1;
type = TT_QUOTE;
return TT_WORD;
}
if (type == TT_OTHER) {
token = str.substring(pos, i);
pos = i;
nextPos = pos + 1;
type = TT_QUOTE;
return TT_OTHER;
}
} else {
if (type == TT_OTHER) {
token = str.substring(pos, i);
pos = i;
nextPos = pos + 1;
type = TT_WORD;
return TT_OTHER;
}
}
}
token = str.substring(pos, length);
if (type == TT_WORD) {
type = TT_EOF;
return TT_WORD;
}
type = TT_EOF;
return TT_OTHER;
}
/**
* 単語以外かどうかを返します。
*
* @param c
* 文字
* @return 単語以外かどうか
*/
protected boolean isOther(char c) {
return Character.isWhitespace(c) || isOrdinary(c);
}
/**
* 単独で文字として認識するかどうかを返します。
*
* @param c
* 文字
* @return 単独で文字として認識するかどうか
*/
protected boolean isOrdinary(char c) {
return c == '=' || c == '?' || c == '<' || c == '>' || c == '('
|| c == ')' || c == '!' || c == '*' || c == '-' || c == ',';
}
/**
* トークンを返します。
*
* @return トークン
*/
public String getToken() {
return token;
}
}