package com.javadude.annotation.processors; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Stack; import com.javadude.annotation.processors.template.ExpressionException; public class Symbols { private static final char DELIMITER = '_'; private static final String UPPER = "UPPER"; private static final String LOWER = "LOWER"; private static final String NOPACKAGE = "NOPACKAGE"; private static final String PARENT = "PARENT"; public static final Object NULL_VALUE = new Object() {@Override public String toString() {return "<NULL>";}}; private Stack<Map<String, Object>> values_ = new Stack<Map<String, Object>>(); public Symbols() { pushScope(); } public Symbols(Map<String, Object> newScope) { pushScope(newScope); } public void pushScope() { values_.push(new HashMap<String, Object>()); } public void pushScope(String key, Object value) { pushScope(); put(key, value); } public void pushScope(String key, Object value, String key2, Object value2) { pushScope(); put(key, value); put(key2, value2); } public void pushScope(Map<String, Object> newScope) { pushScope(); for (Map.Entry<String, Object> entry : newScope.entrySet()) { put(entry.getKey(), entry.getValue()); } } public void popScope() { values_.pop(); } public Object get(String key, int line) { int scope = values_.size() - 1; // which scope to start lookup boolean upper = false; boolean lower = false; boolean noPackage = false; String originalKey = key; // pick off the modifiers while (true) { int delimiter = key.indexOf(Symbols.DELIMITER); if (delimiter == -1) break; if (delimiter == key.length()) throw new RuntimeException("Invalid variable reference; trailing delimiter (" + Symbols.DELIMITER + "): " + originalKey + " on line " + line); String before = key.substring(0, delimiter); String after = key.substring(delimiter + 1); if (Symbols.PARENT.equals(before)) { scope--; key = after; } else if (Symbols.UPPER.equals(before)) { if (upper) throw new ExpressionException("Multiple " + Symbols.UPPER + " refs in " + originalKey + " on line " + line); upper = true; key = after; } else if (Symbols.LOWER.equals(before)) { if (lower) throw new ExpressionException("Multiple " + Symbols.LOWER + " refs in " + originalKey + " on line " + line); lower = true; key = after; } else if (Symbols.NOPACKAGE.equals(before)) { if (noPackage) throw new ExpressionException("Multiple " + Symbols.NOPACKAGE + " refs in " + originalKey + " on line " + line); noPackage = true; key = after; } else { // must be a variable with throw-away suffix key = before; // throw away the suffixes } } while (scope >= 0) { Object value = values_.get(scope).get(key); if (value != null) { if (noPackage){ int i = ((String) value).lastIndexOf('.'); if (i != -1) { value = ((String) value).substring(i + 1); } } if (upper) value = Utils.upperFirstChar(value); if (lower) value = Utils.lowerFirstChar(value); return value; } scope--; } throw new ExpressionException("Variable '" + key + "' undefined for expression '" + originalKey + "' on line " + line); } public void put(String key, Object value) { if (value == null) value = Symbols.NULL_VALUE; values_.peek().put(key, value); } public boolean testSymbol(String variable, int line) { Object value = get(variable, line); if (value == Symbols.NULL_VALUE) return false; if (value.getClass() == Boolean.class) return ((Boolean) value).booleanValue(); else if (value instanceof Collection<?>) return !((Collection<?>) value).isEmpty(); else return true; } }