I have a Tokenizer doing lexer thing.
It is used like
String line = "class Main {"
ArrayList<String> tokens = LineTokenizer.tokenize(line)
The overall logic of LineTokenizer.tokenize
is like below and tokenize
method is static
for ( char c : line ){
if (condition){
doSomething1(); //like arrayList.add(c)
doSomething2(); //like tempWord += c
}else if(condition2) {
doSomething2();
}else if(condition3) {
doSomething2();
doSomething3();
}
}
And There are 8 conditions which are functionized just because of readability.
private static boolean isOpeningQuotationMark(char currentChar) {
return (currentChar == '\"' && progressInQuotationMark == false);
}
private static boolean isClosingQuotationMark(char currentChar) {
return (currentChar == '\"' && progressInQuotationMark == true);
}
private static boolean isTailWhiteSpace(char currentChar) {
return Character.isWhitespace(currentChar) && !tempWord.isEmpty();
}
private static boolean isHeadWhiteSpace(char currentChar) {
return (Character.isWhitespace(currentChar) && tempWord.isEmpty());
}
private static boolean isSymbol(char currentChar){
return Symbol.contains(currentChar);
}
private static boolean metSymbolAtEmpty(char currentChar) {
return Symbol.contains(currentChar) && tempWord.isEmpty();
}
private static boolean metSymbolAtFull(char currentChar) {
return Symbol.contains(currentChar) && !tempWord.isEmpty();
}
And This class have 2 fields which are just needed to make 8 conditions static for the tokenize
which is also static method.
private static boolean progressInQuotationMark = false;
private static String tempWord = "";
And This is whole code
public class LineTokenizer {
private static boolean progressInQuotationMark = false;
private static String tempWord = "";
private static boolean isOpeningQuotationMark(char currentChar) {
return (currentChar == '\"' && progressInQuotationMark == false);
}
private static boolean isClosingQuotationMark(char currentChar) {
return (currentChar == '\"' && progressInQuotationMark == true);
}
private static boolean isTailWhiteSpace(char currentChar) {
return Character.isWhitespace(currentChar) && !tempWord.isEmpty();
}
private static boolean isHeadWhiteSpace(char currentChar) {
return (Character.isWhitespace(currentChar) && tempWord.isEmpty());
}
private static boolean isSymbol(char currentChar){
return Symbol.contains(currentChar);
}
private static boolean metSymbolAtEmpty(char currentChar) {
return Symbol.contains(currentChar) && tempWord.isEmpty();
}
private static boolean metSymbolAtFull(char currentChar) {
return Symbol.contains(currentChar) && !tempWord.isEmpty();
}
public static ArrayList<String> tokenize(String line){
ArrayList<String> tokenizedLine = new ArrayList<>();
for (char currentChar : line.toCharArray()) {
if (isOpeningQuotationMark(currentChar)) {
progressInQuotationMark = true;
} else if (isClosingQuotationMark(currentChar)) {
tokenizedLine.add('\"' + tempWord + '\"');
tempWord = "";
progressInQuotationMark = false;
} else if (progressInQuotationMark) {
tempWord += currentChar;
} else if (isTailWhiteSpace(currentChar)) {
tokenizedLine.add(tempWord);
tempWord = "";
} else if (isHeadWhiteSpace(currentChar)) {
;
} else if (!isSymbol(currentChar)) {
tempWord += currentChar;
} else if (metSymbolAtFull(currentChar)) {
tokenizedLine.add(tempWord);
tokenizedLine.add(String.valueOf(currentChar));
tempWord = "";
} else if (metSymbolAtEmpty(currentChar)) {
tokenizedLine.add(String.valueOf(currentChar));
tempWord = "";
}
}
return tokenizedLine;
}
}
This code works fine. But I think there are code smells because of
- "long" if statement.
- I think it violates Open Closed Principle.
- The order of if statement is hard to change.
- If I want to change or add new condition, I have to think all the conditions again.
What I tried is
make a new class for a char in lines. class CodeChar . But This Class needed a tempWord of original logic and some conditions in caller class. I made a enum for each conditions and made them type attribute of CodeChar. So I can make Set dictionary of <CodeCharType, Runnable> <- Runnable in this Set is logic inside if statements But Those also caused dependency of each other and looks kinda "dirty"
How can I solve this problem? Is there a pattern for this?
Before I consider changing the if statement, Are There already fundamental design problem?
Or Is that okay to be like that?
And This is another problem but If It is okay to ask,
ArrayList<String> tokens = LineTokenizer.tokenize(line)
In this code, does tokenize
should not be a verb? because It is not void?