package com.floreysoft.jmte.token; import static com.floreysoft.jmte.util.NestedParser.*; import java.util.List; import com.floreysoft.jmte.util.Util; public class Lexer { public AbstractToken nextToken(final char[] template, final int start, final int end) { String input = new String(template, start, end - start); if (input.startsWith("--")) { // comment return null; } AbstractToken token = innerNextToken(input); token.setText(template, start, end); token.setLine(template, start, end); token.setColumn(template, start, end); return token; } private String unescapeAccess(List<? extends Object> arr,int index){ String val = access(arr,index); if (val!=null && val.trim().length()>0){ val = Util.NO_QUOTE_MINI_PARSER.unescape(val); } return val; } private AbstractToken innerNextToken(final String untrimmedInput) { final String input = Util.trimFront(untrimmedInput); // annotation if (input.length() > 0 && input.charAt(0) == '@') { final List<String> split = Util.RAW_MINI_PARSER.splitOnWhitespace( input.substring(1), 2); String receiver = access(split, 0); String arguments = access(split, 1); AnnotationToken annotationToken = new AnnotationToken(receiver, arguments); return annotationToken; } final List<String> split = Util.RAW_MINI_PARSER .splitOnWhitespace(input); // LENGTH 0 if (split.size() == 0) { // empty expression like ${} return new StringToken(); } if (split.size() >= 2) { // LENGTH 2..n final String cmd = split.get(0); final String objectExpression = split.get(1); if (cmd.equalsIgnoreCase(IfToken.IF)) { final boolean negated; final String ifExpression; // TODO: Both '!' and '=' work only if there are no white space // separators if (objectExpression.startsWith("!")) { negated = true; ifExpression = objectExpression.substring(1); } else { negated = false; ifExpression = objectExpression; } if (!ifExpression.contains("=")) { return new IfToken(ifExpression, negated); } else { final String[] ifSplit = ifExpression.split("="); final String variable = ifSplit[0]; String operand = ifSplit[1]; // remove optional quotations if (operand.startsWith("'") || operand.startsWith("\"")) { operand = operand.substring(1, operand.length() - 1); } return new IfCmpToken(variable, operand, negated); } } if (cmd.equalsIgnoreCase(ForEachToken.FOREACH)) { final String varName = split.get(2); // we might also have // separator // data // but as the separator itself can contain // spaces // and the number of spaces between the previous // parts is unknown, we need to do this smarter int gapCount = 0; int separatorBegin = 0; while (separatorBegin < input.length()) { char c = input.charAt(separatorBegin); separatorBegin++; if (Character.isWhitespace(c)) { gapCount++; if (gapCount == 3) { break; } else { while (Character.isWhitespace(c = input .charAt(separatorBegin))) separatorBegin++; } } } String separator = input.substring(separatorBegin); if (separator !=null){ separator = Util.NO_QUOTE_MINI_PARSER.unescape(separator); } return new ForEachToken(objectExpression, varName, separator .length() != 0 ? separator : null); } } final String objectExpression = split.get(0); // ${ // } which might be used for silent line breaks if (objectExpression.equals("")) { return new StringToken(); } final String cmd = objectExpression; if (cmd.equalsIgnoreCase(ElseToken.ELSE)) { return new ElseToken(); } if (cmd.equalsIgnoreCase(EndToken.END)) { return new EndToken(); } // ${<h1>,address(NIX),</h1>;long(full)} String variableName = null; // address String defaultValue = null; // NIX String prefix = null; // <h1> String suffix = null; // </h1> String rendererName = null; // long String parameters = null; // full // be sure to use the raw input as we might have to preserve // whitespace for prefix and postfix // only innermost parsers are allowed to unescape final List<String> strings = Util.RAW_OUTPUT_MINI_PARSER.split( untrimmedInput, ';', 2); // <h1>,address(NIX),</h1> final String complexVariable = strings.get(0); // only innermost parsers are allowed to unescape final List<String> wrappedStrings = Util.RAW_OUTPUT_MINI_PARSER.split( complexVariable, ',', 3); // <h1> prefix = wrappedStrings.size() == 3 ? unescapeAccess(wrappedStrings, 0) : null; // </h1> suffix = wrappedStrings.size() == 3 ? unescapeAccess(wrappedStrings, 2) : null; // address(NIX) final String completeDefaultString = (wrappedStrings.size() == 3 ? unescapeAccess( wrappedStrings, 1) : complexVariable).trim(); final List<String> defaultStrings = Util.MINI_PARSER.greedyScan( completeDefaultString, "(", ")"); // address variableName = unescapeAccess(defaultStrings, 0); // NIX defaultValue = unescapeAccess(defaultStrings, 1); // long(full) final String format = access(strings, 1); final List<String> scannedFormat = Util.MINI_PARSER.greedyScan(format, "(", ")"); // long rendererName = access(scannedFormat, 0); // full parameters = access(scannedFormat, 1); // this is not a well formed variable name if (variableName.contains(" ")) { return new InvalidToken(); } final StringToken stringToken = new StringToken(untrimmedInput, variableName, defaultValue, prefix, suffix, rendererName, parameters); return stringToken; } }