/*
* 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.gen.internal.data;
import static org.seasar.extension.jdbc.gen.internal.data.DumpFileTokenizer.TokenType.*;
/**
* ダンプファイル内のトークンを認識するクラスです。
*
* @author taedium
*/
public class DumpFileTokenizer {
/**
* トークンタイプ
*
* @author taedium
*/
public enum TokenType {
/** 値 */
VALUE,
/** null */
NULL,
/** 区切り文字 */
DELIMITER,
/** 行の終端 */
END_OF_LINE,
/** バッファーの終端 */
END_OF_BUFFER,
/** ファイルの終端 */
END_OF_FILE
}
/** バッファ */
protected StringBuilder buf = new StringBuilder(200);
/** バッファ内の現在位置 */
protected int pos;
/** バッファ内の次の位置 */
protected int nextPos;
/** バッファの長さ */
protected int length;
/** 区切り文字 */
protected char delimiter;
/** トークンのタイプ */
protected TokenType type = END_OF_LINE;
/** トークン */
protected String token;
/** ファイルの終端の場合{@code true} */
protected boolean endOfFile;
/**
* インスタンスを構築します。
*
* @param delimiter
* 区切り文字
*/
public DumpFileTokenizer(char delimiter) {
this.delimiter = delimiter;
}
/**
* 文字をchar配列として追加します。
*
* @param chars
* char配列としての文字
* @param len
* 有効な文字の長さ、ファイルの終端の場合-1
*/
public void addChars(char[] chars, int len) {
if (endOfFile) {
throw new IllegalStateException("endOfFile");
}
if (len < 0) {
endOfFile = true;
} else {
buf.append(chars, 0, len);
}
length = buf.length();
peek(pos);
}
/**
* 次のトークンを前もって調べます。
*
* @param index
* 開始インデックス
*/
protected void peek(int index) {
if (index < length) {
pos = index;
char c = buf.charAt(index);
if (c == '"') {
for (int i = index + 1; i < length; i++) {
c = buf.charAt(i);
if (c == '"') {
i++;
if (i < length) {
c = buf.charAt(i);
if (c != '"') {
for (int j = i; j < length; j++) {
c = buf.charAt(j);
if (c == delimiter || isEndOfLine(j)) {
type = VALUE;
nextPos = j;
return;
}
}
}
}
}
}
type = endOfFile ? END_OF_FILE : END_OF_BUFFER;
} else if (c == delimiter) {
if (type == END_OF_LINE || type == DELIMITER) {
type = NULL;
nextPos = index;
} else {
type = DELIMITER;
nextPos = index + 1;
}
} else if (isEndOfLine(index)) {
if (type == END_OF_LINE || type == DELIMITER) {
type = NULL;
nextPos = index;
} else {
type = END_OF_LINE;
nextPos = isCRLF(index) ? index + 2 : index + 1;
}
} else if (c == '\r') {
if (type == END_OF_LINE || type == DELIMITER) {
type = NULL;
nextPos = index;
} else {
type = endOfFile ? END_OF_FILE : END_OF_BUFFER;
}
} else {
for (int i = index; i < length; i++) {
c = buf.charAt(i);
if (c == delimiter || isEndOfLine(i)) {
type = VALUE;
nextPos = i;
return;
}
}
type = endOfFile ? END_OF_FILE : END_OF_BUFFER;
}
} else {
type = endOfFile ? END_OF_FILE : END_OF_BUFFER;
}
}
/**
* 行の終端の場合{@code true}を返します。
*
* @param index
* 開始インデックス
* @return 行の終端の場合{@code true}
*/
protected boolean isEndOfLine(int index) {
char c = buf.charAt(index);
if (c == '\r') {
if (index + 1 < length || endOfFile) {
return true;
}
} else if (c == '\n') {
return true;
}
return false;
}
/**
* CRLFを表す場合{@code true}
*
* @param index
* 開始インデックス
* @return CRLFを表す場合{@code true}
*/
protected boolean isCRLF(int index) {
char c = buf.charAt(index);
if (c == '\r') {
int i = index + 1;
return i < length && buf.charAt(i) == '\n';
}
return false;
}
/**
* 次のトークンタイプを返します。
*
* @return 次のトークンタイプ
*/
public TokenType nextToken() {
switch (type) {
case VALUE:
token = buf.substring(pos, nextPos);
peek(nextPos);
return VALUE;
case NULL:
token = buf.substring(pos, nextPos);
peek(nextPos);
return NULL;
case DELIMITER:
token = buf.substring(pos, nextPos);
peek(nextPos);
return DELIMITER;
case END_OF_LINE:
token = buf.substring(pos, nextPos);
buf.delete(0, nextPos);
buf.trimToSize();
length = buf.length();
pos = 0;
nextPos = 0;
peek(0);
return END_OF_LINE;
case END_OF_BUFFER:
token = buf.substring(pos);
return END_OF_BUFFER;
case END_OF_FILE:
token = buf.substring(pos);
return END_OF_FILE;
}
throw new IllegalStateException(type.name());
}
/**
* トークンを返します。
*
* @return トークン
*/
public String getToken() {
return token;
}
}