/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2011, Geomatys * * 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; * version 2.1 of the License. * * 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. */ package org.geotoolkit.process.mapfile; import java.util.ArrayList; import java.util.List; /** * * @author Johann Sorel (Geomatys) * @module */ public final class MapfileExpressionTokenizer { private final String code; private int cursorIndex; private int lineStartIndex; private int lineNumber; private Token token; private MapfileExpressionTokenizer(String code) { this.code = code; this.cursorIndex = 0; this.lineNumber = 0; this.lineStartIndex = 0; } private boolean hasNext() { this.findNext(); return (this.token != null); } private Token next() { this.findNext(); final Token candidate = this.token; this.token = null; return candidate; } private void findNext() { if (this.token != null) { //next value already found return; } //search the next token int startCharacterIndex = moveToNextCharacter(); if (startCharacterIndex == -1) { //we reached the end of the file return; } //start parsing the token token = new Token(); token.startLineIndex = lineNumber; token.endLineIndex = lineNumber; token.startCharacterIndex = cursorIndex - lineStartIndex; char ch = code.charAt(cursorIndex); if (ch == '"') { //we hit a string value moveToStringEnd('"'); return; }else if (ch == '\'') { //we hit a string value moveToStringEnd('\''); return; } moveToWordEnd(); return; } private void moveToWordEnd() { int end = nextDelimiterIndex(cursorIndex); if (end == -1) { //we reached the end of file end = code.length(); } else if (end == cursorIndex) { //token is a delimiter end++; } token.endCharacterIndex = end - lineStartIndex; int size = token.endCharacterIndex - token.startCharacterIndex; token.value = code.substring(cursorIndex, cursorIndex + size); cursorIndex = end; } private void moveToStringEnd(char endchar) { int from = cursorIndex + 1; int end; while (true) { end = code.indexOf(endchar, from); if (end == -1) { //we reached the end of file without finding the end cote throw new RuntimeException("End of String value not found line " + lineNumber + " column " + token.startCharacterIndex); } else if (code.charAt(end - 1) == '\\') { //not the end of the string, just an escape character from = end + 1; continue; } end++; //we take the " in the value break; } token.endCharacterIndex = end - lineStartIndex; int size = token.endCharacterIndex - token.startCharacterIndex; token.value = code.substring(cursorIndex, cursorIndex + size); cursorIndex = end; } /** * Move to the next not empty character */ private int moveToNextCharacter() { while (cursorIndex < code.length()) { char ch = code.charAt(cursorIndex); if (ch == '\n') { this.lineNumber++; this.lineStartIndex = this.cursorIndex; this.cursorIndex++; continue; } else if (ch == ' ' || ch == '\t' || ch == '\r') { this.cursorIndex++; continue; } else { return this.cursorIndex; } } return -1; } private int nextDelimiterIndex(int from) { int index = from; while (index < code.length()) { final char ch = code.charAt(index); if (isDelimiter(ch)) { return index; } index++; } return -1; } private static boolean isDelimiter(char ch) { return (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '(' || ch == ')' || ch == '/' || ch == '|' || ch == '[' || ch == ']' || ch == '='); } public static class Token { public String value; public int startLineIndex; public int endLineIndex; public int startCharacterIndex; public int endCharacterIndex; @Override public String toString() { return value; } } public static List<Token> toTokens(final String code) { // read all tokens final MapfileExpressionTokenizer tokenizer = new MapfileExpressionTokenizer(code); final List<Token> tokenStack = new ArrayList<Token>(); while (tokenizer.hasNext()) { final Token token = tokenizer.next(); tokenStack.add(token); } return tokenStack; } }