CustomFormatTokenizer.java
package com.github.mygreen.cellformatter.tokenizer;
import java.util.LinkedList;
import com.github.mygreen.cellformatter.lang.Utils;
/**
* カスタム定義の書式をトークンに分割する。
* @author T.TSUCHIE
*
*/
public class CustomFormatTokenizer {
/**
* 書式をトークンに分割する。
* @param pattern Excelの書式
* @return
*/
public TokenStore parse(final String pattern) {
final TokenStore store = new TokenStore();
if(Utils.isEmpty(pattern)) {
return store;
}
splitToken(store, pattern);
return store;
}
private void splitToken(final TokenStore store, final String pattern) {
// 解析時の途中の文字を一時的に補完しておく。
final LinkedList<String> stack = new LinkedList<String>();
final int length = pattern.length();
for(int i=0; i < length; i++) {
final char c = pattern.charAt(i);
if(StackUtils.equalsAnyTopElement(stack, Token.STR_ESCAPES)) {
// スタックの一番上がエスケープの文字だが、文字列の囲み文字(")の終了の場合は、文字列として追加する
if(c == '"' && StackUtils.equalsBottomElement(stack, "\"")) {
store.add(Token.word(StackUtils.popupAndConcat(stack) + c));
continue;
}
// スタックの一番上がエスケープ文字の場合でかつ、括弧や文字列などの囲み文字の中の場合は、通常の文字として扱う。
if(StackUtils.equalsAnyBottomElement(stack, new String[]{"[", "\""})) {
stack.push(String.valueOf(c));
} else {
// エスケープ文字として分割する。
final String escapedChar = StackUtils.popup(stack);
final String concatStr = StackUtils.popupAndConcat(stack);
if(concatStr.length() >= 1) {
// エスケープ文字以前の文字を追加する。
store.add(Token.factor(concatStr));
}
store.add(Token.escapedChar(escapedChar + c));
}
continue;
}
if(c == '"') {
if(StackUtils.equalsBottomElement(stack, "\"")) {
// 文字列の囲み文字'"'の終わりの場合、文字列として追加する。
store.add(Token.word(StackUtils.popupAndConcat(stack) + c));
} else if(!stack.isEmpty()) {
// 文字列の引用符で始まらず、既に文字が入っている場合は、既存のものを取り出し分割する。
// 既存のものはFactorとする。
store.add(Token.factor(StackUtils.popupAndConcat(stack)));
stack.push(String.valueOf(c));
} else {
// 文字列の開始の場合
stack.push(String.valueOf(c));
}
} else if(c == '[') {
if(!stack.isEmpty() && !StackUtils.equalsBottomElement(stack, "\"")) {
// 文字列の引用符の中ではなく、既に文字が入っている場合は、既存のものを取り出し分割する。
// 既存のものはFactorとする。
store.add(Token.factor(StackUtils.popupAndConcat(stack)));
}
stack.push(String.valueOf(c));
} else if(c == ']') {
if(StackUtils.equalsBottomElement(stack, "[")) {
// 条件の終わりの場合
store.add(Token.condition(StackUtils.popupAndConcat(stack) + c));
} else {
stack.push(String.valueOf(c));
}
} else if(c == ';') {
if(!stack.isEmpty()) {
// 既に文字が入っている場合は、既存のものを取り出し分割する。
store.add(Token.factor(StackUtils.popupAndConcat(stack)));
}
store.add(Token.SYMBOL_SEMI_COLON);
} else if(c == '_') {
if(StackUtils.equalsBottomElement(stack, "\"")) {
// 文字列の中の場合の場合は、文字列として処理する。
stack.push(String.valueOf(c));
continue;
}
// スタックに文字がある場合、既存のものを取り出し分割する。
if(!stack.isEmpty()) {
store.add(Token.factor(StackUtils.popupAndConcat(stack)));
}
// 次に続く1文字を取得する
StringBuilder next = new StringBuilder();
if(i + 1 < length) {
i++;
final char c2 = pattern.charAt(i);
next.append(c2);
if(c2 == '\\') {
if(i + 1 < length) {
i++;
i++;
final char c3 = pattern.charAt(i);
next.append(c3);
}
}
store.add(Token.underscore(c + next.toString()));
} else {
store.add(Token.factor(String.valueOf(c)));
}
} else if(c == '*') {
if(StackUtils.equalsBottomElement(stack, "\"")) {
// 文字列の中の場合の場合は、文字列として処理する。
stack.push(String.valueOf(c));
continue;
}
// スタックに文字がある場合、既存のものを取り出し分割する。
if(!stack.isEmpty()) {
store.add(Token.factor(StackUtils.popupAndConcat(stack)));
}
// 次に続く1文字を取得する
StringBuilder next = new StringBuilder();
if(i + 1 < length) {
i++;
final char c2 = pattern.charAt(i);
next.append(c2);
if(c2 == '\\') {
if(i + 1 < length) {
i++;
i++;
final char c3 = pattern.charAt(i);
next.append(c3);
}
}
store.add(Token.asterisk(c + next.toString()));
} else {
store.add(Token.factor(String.valueOf(c)));
}
} else {
stack.push(String.valueOf(c));
}
}
if(!stack.isEmpty()) {
store.add(Token.factor(StackUtils.popupAndConcat(stack)));
}
}
}