package expressions; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; import java.util.LinkedList; import java.util.Set; import java.util.TreeSet; import java.util.Vector; import common.prettyprinter.PrettyPrintable; import common.prettyprinter.PrettyString; import common.prettyprinter.PrettyStringBuilder; import expressions.annotation.SyntacticSugar; /** * Abstract base class for all kinds of expressions in the * expression hierarchy. * * @author Benedikt Meurer * @version $Id$ */ public abstract class Expression implements PrettyPrintable { /** * Returns <code>true</code> if the expression is an * exception that cannot be evaluated any further. * * The default implementation simply returns <code>false</code>, * so derived classes will need to override this method if the * class represents an exception. * * @return <code>true</code> if the expression is an * exception. */ public boolean isException() { return false; } /** * Returns <code>true</code> if the expression is * syntactic sugar, that is not included in the * core syntax. * * Note that <code>false</code> is returned if this * expression is part of the core syntax, but one of * the sub expressions is syntactic sugar. If you * want to know whether an expression contains * syntactic sugar in some way, you should use the * {@link #containsSyntacticSugar()} method instead. * * The default implementation of this method returns * <code>true</code> if the class on which this method * is called is annotated with the {@link SyntacticSugar} * annotation (or any of it's super classes is annotated * with {@link SyntacticSugar}). * * @return <code>true</code> if this - the outer most - * expression is not part of the core syntax. * * @see #containsSyntacticSugar() */ public boolean isSyntacticSugar() { for (Class<?> clazz = getClass(); clazz != Expression.class; clazz = clazz.getSuperclass()) { if (clazz.isAnnotationPresent(SyntacticSugar.class)) { return true; } } return false; } /** * Returns <code>true</code> if the expression is a value * that cannot be evaluated any further. * * @return <code>true</code> if the expression is a value * and no further evaluation is possible. */ public boolean isValue() { return false; } /** * Substitutes the value <code>v</code> for the identifier <code>>id</code> * and returns the resulting expression. * * The default implementation of this method provided by the abstract base * class <code>Expression</code> simply returns a reference to the expression * itself. Derived classes need to override this method if substitution is * possible for those expressions. * * @param id the name of the identifier. * @param e the expression to substitute. * * @return the resulting expression. */ public Expression substitute(String id, Expression e) { return this; } /** * @throws UnsupportedOperationException on every invocation. */ @Deprecated public final Expression evaluate(RuleChain ruleChain) { throw new UnsupportedOperationException("evaluate() is no longer used"); } /** * Returns the free identifiers within this expression. * * The default implementation in the {@link Expression} * class uses introspection to determine the sub expressions * and calls <code>free()</code> recursively on all sub * expressions. Some of the derived classes might need to * override this method if they represents a binding mechanism, * like {@link Let}, or if they don't have sub expressions, * but provide free identifiers, like {@link Identifier}. * * @return the free identifiers within this expression. */ public Set<String> free() { TreeSet<String> free = new TreeSet<String>(); for (Enumeration<Expression> c = children(); c.hasMoreElements(); ) { free.addAll(c.nextElement().free()); } return free; } /** * Returns <code>true</code> if the expression contains * memory references or locations. * * The default implementation in the {@link Expression} * class uses introspection to determine the sub expressions * and calls <code>containsReferences()</code> recursively * on all sub expressions. Some of the derived classes, like * {@link Location} or {@link Ref}, will need to override * this method appropriately. * * @return <code>true</code> if the expression contains * memory references or locations. */ public boolean containsReferences() { for (Enumeration<Expression> c = children(); c.hasMoreElements(); ) { if (c.nextElement().containsReferences()) { return true; } } return false; } /** * Returns <code>true</code> if the expression contains * syntactic sugar, else <code>false</code>. * * You can call translateSyntacticSugar() to translate * all expressions to the core language. * * @return <code>true</code> if the expression contains * syntactic sugar. * * @see #isSyntacticSugar() * @see #translateSyntacticSugar() */ public final boolean containsSyntacticSugar() { // check if this expression is syntactic sugar if (isSyntacticSugar()) { return true; } else { // test if any of the sub expressions is syntactic sugar for (Enumeration<Expression> c = children(); c.hasMoreElements(); ) { if (c.nextElement().containsSyntacticSugar()) { return true; } } return false; } } /** * If this {@link Expression} is syntactic, it is translated * to core syntax. * * @return the new expression without syntactic sugar. * * @see #containsSyntacticSugar() */ public Expression translateSyntacticSugar() { return this; } /** * Returns the <code>PrettyString</code> for this expression, * which can be used to present the expression to the user. * * @return the <code>PrettyString</code> for this expression. * * @see PrettyPrintable#toPrettyString() */ public final PrettyString toPrettyString() { return toPrettyStringBuilder().toPrettyString(); } /** * Default string converter for expressions, will be overridden * by special constructs like constants. * * @return the string representation for the whole expression. * * @see java.lang.Object#toString() */ @Override public String toString() { return toPrettyString().toString(); } /** * Returns the <code>PrettyStringBuilder</code> for this * expression, which can be used to generate a pretty * string for the expression. * * @return the pretty string builder for the expression. */ protected abstract PrettyStringBuilder toPrettyStringBuilder(); /** * An empty string set, used solely to save memory in * derived classes, that don't provide any free * identifiers. */ public static final TreeSet<String> EMPTY_SET = new TreeSet<String>(); // // Introspections // /** * Cached vector of sub expressions, so the children do not need * to be determined on every invocation of {@link #children()}. * * @see #children() */ private transient Vector<Expression> children = null; /** * Returns an enumeration for the direct ancestor expressions, the * direct children, of this expression. The enumeration is generated * using the bean properties for every {@link Expression} derived * class. For example, {@link Application} provides <code>getE1()</code> * and <code>getE2()</code>, and thereby the sub expressions <code>e1</code> * and <code>e2</code>. It also supports arrays of expressions, as used * in the {@link Tuple} expression class. * * @return an {@link Enumeration} for the direct ancestor expressions * of this expression. */ protected final Enumeration<Expression> children() { // check if we already determined the children if (this.children == null) { try { this.children = new Vector<Expression>(); PropertyDescriptor[] properties = Introspector.getBeanInfo(getClass(), Expression.class).getPropertyDescriptors(); for (PropertyDescriptor property : properties) { Object value = property.getReadMethod().invoke(this); if (value instanceof Expression[]) { this.children.addAll(Arrays.asList((Expression[])value)); } else if (value instanceof Expression) { this.children.add((Expression)value); } } } catch (RuntimeException exception) { throw exception; } catch (Exception exception) { throw new RuntimeException(exception); } } // return an enumeration for the children return this.children.elements(); } // // Expression Tree Traversal // /** * Returns an {@link Enumeration} that enumerates the * expression within the expression hierarchy starting * at this expression in level order (that is breadth * first enumeration). * * @return a breadth first enumeration of all expressions * within the expression hierarchy starting at * this item. */ public Enumeration<Expression> levelOrderEnumeration() { return new LevelOrderEnumeration(this); } private class LevelOrderEnumeration implements Enumeration<Expression> { private LinkedList<Expression> queue = new LinkedList<Expression>(); LevelOrderEnumeration(Expression expression) { this.queue.add(expression); } public boolean hasMoreElements() { return !this.queue.isEmpty(); } public Expression nextElement() { Expression e = this.queue.poll(); this.queue.addAll(Collections.list(e.children())); return e; } } }