Package joust.tree.annotatedtree

Source Code of joust.tree.annotatedtree.AJCTree$AJCSwitch

package joust.tree.annotatedtree;

import com.sun.source.tree.*;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.code.TypeTag;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.util.JCDiagnostic;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.Name;
import joust.analysers.sideeffects.Effects;
import joust.tree.annotatedtree.treeinfo.EffectSet;
import joust.utils.data.JavacListUtils;
import joust.utils.logging.LogUtils;
import joust.utils.ReflectionUtils;
import joust.utils.tree.TreeUtils;
import lombok.Delegate;
import lombok.Getter;
import lombok.NonNull;
import lombok.experimental.ExtensionMethod;
import lombok.extern.java.Log;
import java.lang.reflect.Field;
import java.util.Set;
import java.util.logging.Logger;

import static com.sun.tools.javac.code.Symbol.*;
import static com.sun.tools.javac.code.TypeTag.*;
import static com.sun.tools.javac.tree.JCTree.*;

/**
* A representation of the Java AST. Decorates the nodes in javac's existing AST representation with the extra
* information required, and slightly tweaks the API to make it suck less (No more fields of type JCTree!)
*/
@Log
@ExtensionMethod({Logger.class, LogUtils.LogExtensions.class})
public abstract class AJCTree implements Tree, Cloneable, JCDiagnostic.DiagnosticPosition {
    // The decorated tree node. Since runtime generics are not available, this field gets shadowed in each concrete
    // subclass of AJCTree, but is required for the delegates to work correctly, and to allow reference to the
    // decorated tree node from an instance of type AJCTree.
    @Delegate @Getter private final JCTree decoratedTree;

    // Get the type annotation for this node, instead of getting the type vaguely related to this node (As the various
    // getType() methods on the various JCTree classes seem to do...).
    public Type getNodeType() {
        return decoratedTree.type;
    }

    // The parent node, if any is meaningfully defined.
    public AJCTree mParentNode;

    protected AJCTree(JCTree tree) {
        decoratedTree = tree;
    }

    /**
     * Replace this node with the given node, if possible.
     */
    public void swapFor(@NonNull AJCTree replacement) {
        if (replacement == this) {
            return;
        }

        if (mParentNode == null) {
            log.fatal("Unable to swap " + this + " for " + replacement + " - parent was null.");
            return;
        }

        // TODO: This is EEEVIL.
        // Reparent the new node.
        replacement.mParentNode = mParentNode;

        if (this instanceof AJCStatement) {
            ((AJCStatement) replacement).enclosingBlock = ((AJCStatement) this).enclosingBlock;
        }


        // Determine which of the fields on the parent are occupied by this node and make the appropriate swap.
        // It is required that no object is inserted more than once into a particular parent.
        Class<?> pClass = mParentNode.getClass();
        Field[] fields = ReflectionUtils.getAllFields(pClass);

        for (int i = 0; i < fields.length; i++) {
            fields[i].setAccessible(true);
            try {
                // For non-list fields, just swap in parent.
                if(!"com.sun.tools.javac.util.List".equals(fields[i].getType().getCanonicalName())) {
                    if (fields[i].get(mParentNode) == this) {
                        // Target found!
                        fields[i].set(mParentNode, replacement);

                        // Push this change onto the JCTree, as well...
                        Class<? extends JCTree> underlyingClass = mParentNode.decoratedTree.getClass();
                        Field targetField = ReflectionUtils.findField(underlyingClass, fields[i].getName());
                        targetField.set(mParentNode.decoratedTree, replacement.decoratedTree);
                        log.debug("Swapping {} for {} in field {}", this, replacement, fields[i].getName());
                        return;
                    }

                    continue;
                }

                // For list fields, swap inside the list...
                @SuppressWarnings("unchecked")
                List<AJCTree> theList = (List<AJCTree>) fields[i].get(mParentNode);
                if (theList.contains(this)) {
                    theList = JavacListUtils.replace(theList, this, replacement);
                    fields[i].set(mParentNode, theList);

                    // Push this change onto the JCTree, as well...
                    Class<? extends JCTree> underlyingClass = mParentNode.decoratedTree.getClass();
                    Field targetField = ReflectionUtils.findField(underlyingClass, fields[i].getName());

                    @SuppressWarnings("unchecked")
                    List<JCTree> realList = (List<JCTree>) targetField.get(mParentNode.decoratedTree);
                    realList = JavacListUtils.replace(realList, decoratedTree, replacement.decoratedTree);
                    targetField.set(mParentNode.decoratedTree, realList);

                    return;
                }
            } catch (IllegalAccessException e) {
                // Ostensibly can never happen, because setAccessible is called...
                log.fatal("IllegalAccessException accessing field " + fields[i] + " on " + pClass.getCanonicalName(), e);
            } catch (NoSuchFieldException e) {
                log.fatal("NoSuchFieldException accessing field " + fields[i] + " on " + pClass.getCanonicalName(), e);
            }
        }

        log.fatal("Unable to perform swap of {} for {} in {}", this, replacement, mParentNode);
    }

    /**
     * Unwrap a list of tree nodes and produce a list of their underlying AST nodes.
     */
    @SuppressWarnings("unchecked")
    public static<T extends JCTree, Q extends AJCTree> List<T> unwrap(List<Q> nodes) {
        List<T> realNodes = List.nil();

        for (Q node : nodes) {
            realNodes = realNodes.prepend((T) node.getDecoratedTree());
        }

        return realNodes.reverse();
    }

    @Override
    public String toString() {
        return decoratedTree.toString();
    }

    /**
     * Base class for nodes that can have side effects - avoids allocating effect sets for things like type identifiers.
     */
    public abstract static class AJCEffectAnnotatedTree extends AJCTree {
        public Effects effects = new Effects(EffectSet.ALL_EFFECTS);

        // The results of LVA at this node.
        public Set<VarSymbol> liveVariables;

        protected AJCEffectAnnotatedTree(JCTree tree) {
            super(tree);
        }

        @Override
        public String toString() {
            return super.toString() + ':' + effects.getEffectSet();
        }
    }

    public abstract static class AJCStatement extends AJCEffectAnnotatedTree implements StatementTree {
        @Delegate @Getter private final JCStatement decoratedTree;

        // The block in which this statement resides, if any.
        protected AJCBlock enclosingBlock;

        public AJCBlock getEnclosingBlock() {
            if (enclosingBlock != null) {
                return enclosingBlock;
            }

            if (mParentNode instanceof AJCStatement) {
                return ((AJCStatement) mParentNode).getEnclosingBlock();
            } else if (mParentNode instanceof AJCExpressionTree) {
                return ((AJCExpressionTree) mParentNode).getEnclosingBlock();
            }

            log.fatal("Failed to find enclosing block for statement: " + this);
            return null;
        }

        // Since a statement is always in a block, we can provide a specialised swap function and avoid the evilness.
        @Override
        public void swapFor(@NonNull AJCTree replacement) {
            if (replacement == this) {
                return;
            }

            // Except for let expressions, that is.
            if (mParentNode instanceof AJCLetExpr) {
                super.swapFor(replacement);
                return;
            }

            if (replacement instanceof AJCStatement) {
                // Swap in the enclosing block. Simplez!
                enclosingBlock.swap(this, (AJCStatement) replacement);
            } else {
                log.fatal("Attempt to swap an AJCStatement for a non-statement: {}:{}", replacement, replacement.getClass().getCanonicalName());
                throw new UnsupportedOperationException("Attempt to swap an AJCStatement for a non-statement!");
            }
        }

        protected AJCStatement(JCStatement tree) {
            super(tree);
            decoratedTree = tree;
        }

        public boolean isEmptyStatement() {
            return false;
        }
    }

    /**
     * Marker interface for expressions.
     */
    public interface AJCExpression {}

    public abstract static class AJCExpressionTree extends AJCEffectAnnotatedTree implements ExpressionTree, AJCExpression {
        @Delegate @Getter private final JCExpression decoratedTree;

        /**
         * Get the statement in which this expression resides.
         */
        public AJCStatement getEnclosingStatement() {
            log.debug("getEnclosingStatement on {} parent {}", this, mParentNode);
            if (mParentNode == null) {
                log.debug("PANIC");
            }
            AJCTree parent = mParentNode;
            while (!(parent instanceof AJCStatement) && parent.mParentNode != null) {
                parent = parent.mParentNode;
            }

            if (!(parent instanceof AJCStatement)) {
                log.fatal("Unable to find enclosing statement for: " + this);
                return null;
            }

            return (AJCStatement) parent;
        }

        /**
         * Get the block in which the statement containing this expression is contained. Handy for finding the place to
         * stick new temporary variables.
         */
        public AJCBlock getEnclosingBlock() {
            return getEnclosingStatement().getEnclosingBlock();
        }

        protected AJCExpressionTree(JCExpression tree) {
            super(tree);
            decoratedTree = tree;
        }

        public boolean isEmptyExpression() {
            return false;
        }
    }

    public static class AJCConditional extends AJCExpressionTree implements ConditionalExpressionTree {
        @Delegate @Getter private final JCConditional decoratedTree;

        public AJCExpressionTree cond;
        public AJCExpressionTree truepart;
        public AJCExpressionTree falsepart;

        protected AJCConditional(JCConditional tree) {
            super(tree);
            decoratedTree = tree;
        }

        protected AJCConditional(JCConditional tree, AJCExpressionTree c, AJCExpressionTree tPart, AJCExpressionTree fPart) {
            this(tree);
            cond = c;
            truepart = tPart;
            falsepart = fPart;
        }
    }

    public static class AJCCall extends AJCSymbolRefTree<MethodSymbol> implements MethodInvocationTree {
        @Delegate @Getter private final JCMethodInvocation decoratedTree;

        @Delegate
        public AJCSymbolRefTree<MethodSymbol> meth;
        public List<AJCExpressionTree> args;

        protected AJCCall(JCMethodInvocation tree) {
            super(tree);
            decoratedTree = tree;
        }

        protected AJCCall(JCMethodInvocation tree, AJCSymbolRefTree<MethodSymbol> m, List<AJCExpressionTree> arg) {
            this(tree);
            meth = m;
            args = arg;
        }
    }

    public static class AJCNewClass extends AJCSymbolRefTree<MethodSymbol> implements NewClassTree {
        @Delegate @Getter private final JCNewClass decoratedTree;

        public AJCSymbolRefTree<ClassSymbol> clazz;
        public List<AJCExpressionTree> args;

        protected AJCNewClass(JCNewClass tree) {
            super(tree);
            decoratedTree = tree;
        }

        protected AJCNewClass(JCNewClass tree, AJCSymbolRefTree<ClassSymbol> clasz,
                             List<AJCExpressionTree> arg) {
            this(tree);
            clazz = clasz;
            args = arg;
        }

        @Override
        public MethodSymbol getTargetSymbol() {
            return (MethodSymbol) decoratedTree.constructor;
        }
    }

    public static class AJCClassDecl extends AJCTree implements ClassTree {
        @Delegate @Getter private final JCClassDecl decoratedTree;

        public AJCModifiers mods;
        /** the classes this class extends */
        public AJCExpressionTree extending;
        /** the interfaces implemented by this class */
        public List<AJCExpressionTree> implementing;

        // Fields defined in this class.
        public List<AJCVariableDecl> fields;

        // Methods defined in this class.
        public List<AJCMethodDecl> methods;

        // Classes defined in this class.
        public List<AJCClassDecl> classes;

        protected AJCClassDecl(JCClassDecl tree) {
            super(tree);
            decoratedTree = tree;
        }

        protected AJCClassDecl(JCClassDecl tree, AJCModifiers mods,
                              AJCExpressionTree extending, List<AJCExpressionTree> implementing, List<AJCVariableDecl> fields,
                              List<AJCMethodDecl> methods, List<AJCClassDecl> classes) {
            this(tree);
            this.mods = mods;
            this.extending = extending;
            this.implementing = implementing;
            this.fields = fields;
            this.methods = methods;
            this.classes = classes;
        }

        public ClassSymbol getSym() {
            return decoratedTree.sym;
        }
    }

    public static class AJCMethodDecl extends AJCTree implements MethodTree, AJCSymbolRef<MethodSymbol> {
        @Delegate @Getter private final JCMethodDecl decoratedTree;

        public AJCModifiers mods;
        /** type of method return value */
        public AJCTypeExpression restype;
        /** receiver parameter */
        public AJCVariableDecl recvparam;
        /** value parameters */
        public List<AJCVariableDecl> params;
        /** exceptions thrown by this method */
        public List<AJCExpressionTree> thrown;
        /** statements in the method */
        public AJCBlock body;
        /** default value, for annotation types */
        public AJCExpressionTree defaultValue;

        // All symbols ever live within the body of this method.
        public Set<VarSymbol> everLive;

        protected AJCMethodDecl(JCMethodDecl tree) {
            super(tree);
            decoratedTree = tree;
        }

        protected AJCMethodDecl(JCMethodDecl tree, AJCModifiers mods, AJCTypeExpression restype,
                               AJCVariableDecl recvparam, List<AJCVariableDecl> params, List<AJCExpressionTree> thrown, AJCBlock body,
                               AJCExpressionTree defaultValue)
        {
            this(tree);
            this.mods = mods;
            this.restype = restype;
            this.params = params;
            this.recvparam = recvparam;
            this.thrown = thrown;
            this.body = body;
            this.defaultValue = defaultValue;
        }

        @Override
        public MethodSymbol getTargetSymbol() {
            return decoratedTree.sym;
        }
    }

    public static class AJCVariableDecl extends AJCStatement implements VariableTree, AJCSymbolRef<VarSymbol> {
        @Delegate @Getter private final JCVariableDecl decoratedTree;

        public AJCModifiers mods;
        /** type of the variable */
        public AJCTypeExpression vartype;
        /** variable's initial value */
        @Getter protected AJCExpressionTree init = new AJCEmptyExpression();

        protected AJCVariableDecl(JCVariableDecl tree) {
            super(tree);
            decoratedTree = tree;
        }


        protected AJCVariableDecl(JCVariableDecl tree, AJCModifiers mods, AJCTypeExpression vartype,
                                 AJCExpressionTree init) {
            this(tree);
            this.mods = mods;
            this.vartype = vartype;
            this.init = init;
        }

        public void setInit(AJCExpressionTree expr) {
            init = expr;
            decoratedTree.init = expr.decoratedTree;
        }

        @Override
        public VarSymbol getTargetSymbol() {
            return decoratedTree.sym;
        }
    }

    /**
     * An expression that does nothing.
     */
    public static class AJCEmptyExpression extends AJCExpressionTree {
        protected AJCEmptyExpression() {
            super(null);
            effects = new Effects(EffectSet.NO_EFFECTS);
        }

        @Override
        public boolean isEmptyExpression() {
            return true;
        }

        @Override
        public String toString() {
            return "/* */";
        }
    }

    public static class AJCSkip extends AJCStatement implements EmptyStatementTree {
        @Delegate @Getter private final JCSkip decoratedTree;

        protected AJCSkip(JCSkip tree) {
            super(tree);
            decoratedTree = tree;
            effects = new Effects(EffectSet.NO_EFFECTS);
        }

        @Override
        public boolean isEmptyStatement() {
            return true;
        }
    }

    /**
     * Base class for statements that contain lists of other statements (case statements and blocks).
     */
    public abstract static class AJCStatementHoldingStatement extends AJCStatement {
        public enum Type {
            BLOCK,
            CASE
        }
        private final Type type;

        // The list of contained statements.
        public List<AJCStatement> stats;

        // The value of the "stats" field of the underlying JCTree node.
        private List<JCStatement> underlyingStats;

        // The method in which this block resides.
        public AJCMethodDecl enclosingMethod;

        protected AJCStatementHoldingStatement(JCBlock tree) {
            super(tree);
            type = Type.BLOCK;
            underlyingStats = tree.stats;
        }

        protected AJCStatementHoldingStatement(JCCase tree) {
            super(tree);
            type = Type.CASE;
            underlyingStats = tree.stats;
        }

        /**
         * Push changes to the underlyingStats value down to the JCTree.
         */
        private void setUnderlyingStats() {
            switch (type) {
                case BLOCK:
                    ((JCBlock) getDecoratedTree()).stats = underlyingStats;
                    break;
                case CASE:
                    ((JCCase) getDecoratedTree()).stats = underlyingStats;
                    break;
            }
        }

        /**
         * Remove the given statement from this block, and reflect the update in the underlying Javac AST.
         */
        public void remove(AJCStatement statement) {
            stats = JavacListUtils.removeElement(stats, statement);
            underlyingStats = JavacListUtils.removeElement(underlyingStats, statement.decoratedTree);
            setUnderlyingStats();
        }

        /**
         * Add the given statement at the specified index in the block, reflecting the update in underlying decorated block.
         */
        public void insert(AJCStatement statement, int index) {
            if (type == Type.BLOCK) {
                statement.enclosingBlock = (AJCBlock) this;
            }
            statement.mParentNode = this;
            if (statement instanceof AJCBlock) {
                ((AJCBlock) statement).enclosingMethod = enclosingMethod;
            }

            stats = JavacListUtils.addAtIndex(stats, index, statement);
            underlyingStats = JavacListUtils.addAtIndex(underlyingStats, index, statement.decoratedTree);
            setUnderlyingStats();
        }
        public void insert(List<AJCStatement> statements, int index) {
            // Try to take the easy way out.
            if (statements.length() == 1) {
                insert(statements.head, index);
                return;
            }

            // Compute early since the splicing is destructive.
            List<JCStatement> unwrapped = AJCTree.unwrap(statements);

            stats = JavacListUtils.addAtIndex(stats, index, statements);
            for (AJCStatement st : stats) {
                if (type == Type.BLOCK) {
                    st.enclosingBlock = (AJCBlock) this;
                }
                st.mParentNode = this;
                if (st instanceof AJCBlock) {
                    ((AJCBlock) st).enclosingMethod = enclosingMethod;
                }
            }

            underlyingStats = JavacListUtils.addAtIndex(underlyingStats, index, unwrapped);
            setUnderlyingStats();
        }

        private int indexOfOrFail(AJCStatement node) {
            int index = stats.indexOf(node);
            if (index == -1) {
                log.fatal("Unable to locate target statement: " + node + " in block:\n" + this);
            }

            return index;
        }
        /**
         * Insert a statement before a given statement.
         */
        public void insertBefore(AJCStatement before, AJCStatement insert) {
            int index = indexOfOrFail(before);
            insert(insert, index);
        }
        public void insertBefore(AJCStatement before, List<AJCStatement> insert) {
            int index = indexOfOrFail(before);
            insert(insert, index);
        }

        /**
         * Insert a statement after a given statement.
         */
        public void insertAfter(AJCStatement after, AJCStatement insert) {
            int index = indexOfOrFail(after);
            insert(insert, index + 1);
        }
        public void insertAfter(AJCStatement after, List<AJCStatement> insert) {
            int index = indexOfOrFail(after);
            insert(insert, index + 1);
        }

        public int indexOf(AJCStatement statement) {
            return stats.indexOf(statement);
        }

        /**
         * Swap the given statement for the other given statement.
         */
        public void swap(AJCStatement target, AJCStatement replacement) {
            if (target == replacement) {
                return;
            }

            replacement.mParentNode = this;
            if (type == Type.BLOCK) {
                replacement.enclosingBlock = (AJCBlock) this;
            }
            stats = JavacListUtils.replace(stats, target, replacement);
            underlyingStats = JavacListUtils.replace(underlyingStats, target.decoratedTree, replacement.decoratedTree);
            setUnderlyingStats();
        }
    }

    public static class AJCBlock extends AJCStatementHoldingStatement implements BlockTree {
        @Delegate @Getter private final JCBlock decoratedTree;

        protected AJCBlock(JCBlock tree) {
            super(tree);
            decoratedTree = tree;
        }

        protected AJCBlock(JCBlock tree, List<AJCStatement> statements) {
            this(tree);
            stats = statements;
        }
    }

    public static class AJCDoWhileLoop extends AJCStatement implements DoWhileLoopTree {
        @Delegate @Getter private final JCDoWhileLoop decoratedTree;

        public AJCBlock body;
        public AJCExpressionTree cond;

        protected AJCDoWhileLoop(JCDoWhileLoop tree) {
            super(tree);
            decoratedTree = tree;
        }

        protected AJCDoWhileLoop(JCDoWhileLoop tree, AJCBlock body, AJCExpressionTree cond) {
            this(tree);
            this.body = body;
            this.cond = cond;
        }
    }

    public static class AJCWhileLoop extends AJCStatement implements WhileLoopTree {
        @Delegate @Getter private final JCWhileLoop decoratedTree;

        public AJCExpressionTree cond;
        public AJCBlock body;

        protected AJCWhileLoop(JCWhileLoop tree) {
            super(tree);
            decoratedTree = tree;
        }

        protected AJCWhileLoop(JCWhileLoop tree, AJCExpressionTree cond, AJCBlock body) {
            this(tree);
            this.body = body;
            this.cond = cond;
        }
    }

    public static class AJCForLoop extends AJCStatement implements ForLoopTree {
        @Delegate @Getter private final JCForLoop decoratedTree;

        public List<AJCStatement> init;
        public AJCExpressionTree cond;
        public List<AJCExpressionStatement> step;
        public AJCBlock body;

        protected AJCForLoop(JCForLoop tree) {
            super(tree);
            decoratedTree = tree;
        }


        protected AJCForLoop(JCForLoop tree, List<AJCStatement> init, AJCExpressionTree cond, List<AJCExpressionStatement> update, AJCBlock body) {
            this(tree);
            this.init = init;
            this.cond = cond;
            this.step = update;
            this.body = body;
        }
    }

    public static class AJCLabeledStatement extends AJCStatement implements LabeledStatementTree {
        @Delegate @Getter private final JCLabeledStatement decoratedTree;

        public AJCStatement body;

        protected AJCLabeledStatement(JCLabeledStatement tree) {
            super(tree);
            decoratedTree = tree;
        }

        protected AJCLabeledStatement(JCLabeledStatement tree, AJCStatement body) {
            this(tree);
            this.body = body;
        }
    }

    public static class AJCSwitch extends AJCStatement implements SwitchTree {
        @Delegate @Getter private final JCSwitch decoratedTree;

        public AJCExpressionTree selector;
        public List<AJCCase> cases;

        protected AJCSwitch(JCSwitch tree) {
            super(tree);
            decoratedTree = tree;
        }

        protected AJCSwitch(JCSwitch tree, AJCExpressionTree selector, List<AJCCase> cases) {
            this(tree);
            this.selector = selector;
            this.cases = cases;
        }
    }

    public static class AJCCase extends AJCStatementHoldingStatement implements CaseTree {
        @Delegate @Getter private final JCCase decoratedTree;

        // The empty expression here represents the default case.
        public AJCExpressionTree pat = new AJCEmptyExpression();

        protected AJCCase(JCCase tree) {
            super(tree);
            decoratedTree = tree;
        }

        protected AJCCase(JCCase tree, AJCExpressionTree pat, List<AJCStatement> stats) {
            this(tree);
            this.pat = pat;
            this.stats = stats;
        }
    }

    public static class AJCSynchronized extends AJCStatement implements SynchronizedTree {
        @Delegate @Getter private final JCSynchronized decoratedTree;

        public AJCExpressionTree lock;
        public AJCBlock body;

        protected AJCSynchronized(JCSynchronized tree) {
            super(tree);
            decoratedTree = tree;
        }

        protected AJCSynchronized(JCSynchronized tree, AJCExpressionTree lock, AJCBlock body) {
            this(tree);
            this.lock = lock;
            this.body = body;
        }
    }

    public static class AJCTry extends AJCStatement implements TryTree {
        @Delegate @Getter private final JCTry decoratedTree;

        public AJCBlock body;
        public List<AJCCatch> catchers;
        public AJCBlock finalizer;

        protected AJCTry(JCTry tree) {
            super(tree);
            decoratedTree = tree;
        }

        protected AJCTry(JCTry tree, AJCBlock body, List<AJCCatch> catchers, AJCBlock finalizer) {
            this(tree);
            this.body = body;
            this.catchers = catchers;
            this.finalizer = finalizer;
        }
    }

    public static class AJCCatch extends AJCEffectAnnotatedTree implements CatchTree {
        @Delegate @Getter private final JCCatch decoratedTree;

        public AJCVariableDecl param;
        public AJCBlock body;

        protected AJCCatch(JCCatch tree) {
            super(tree);
            decoratedTree = tree;
        }

        protected AJCCatch(JCCatch tree, AJCVariableDecl param, AJCBlock body) {
            this(tree);
            this.param = param;
            this.body = body;
        }
    }

    public static class AJCIf extends AJCStatement implements IfTree {
        @Delegate @Getter private final JCIf decoratedTree;

        public AJCExpressionTree cond;
        public AJCBlock thenpart;
        public AJCBlock elsepart;

        protected AJCIf(JCIf tree) {
            super(tree);
            decoratedTree = tree;
        }

        protected AJCIf(JCIf tree, AJCExpressionTree cond, AJCBlock thenpart, AJCBlock elsepart) {
            this(tree);
            this.cond = cond;
            this.thenpart = thenpart;
            this.elsepart = elsepart;
        }
    }

    public static class AJCExpressionStatement extends AJCStatement implements ExpressionStatementTree {
        @Delegate @Getter private final JCExpressionStatement decoratedTree;

        public AJCExpressionTree expr;

        protected AJCExpressionStatement(JCExpressionStatement tree) {
            super(tree);
            decoratedTree = tree;
        }

        protected AJCExpressionStatement(JCExpressionStatement tree, AJCExpressionTree expr) {
            this(tree);
            this.expr = expr;
        }
    }

    public static class AJCBreak extends AJCStatement implements BreakTree {
        @Delegate @Getter private final JCBreak decoratedTree;

        public AJCTree target;

        protected AJCBreak(JCBreak tree) {
            super(tree);
            decoratedTree = tree;
        }
    }

    public static class AJCContinue extends AJCStatement implements ContinueTree {
        @Delegate @Getter private final JCContinue decoratedTree;

        public AJCTree target;

        protected AJCContinue(JCContinue tree) {
            super(tree);
            decoratedTree = tree;
        }
    }

    public static class AJCReturn extends AJCStatement implements ReturnTree {
        @Delegate @Getter private final JCReturn decoratedTree;

        public AJCExpressionTree expr;

        protected AJCReturn(JCReturn tree) {
            super(tree);
            decoratedTree = tree;
        }

        protected AJCReturn(JCReturn tree, AJCExpressionTree expr) {
            this(tree);
            this.expr = expr;
        }
    }

    public static class AJCThrow extends AJCStatement implements ThrowTree {
        @Delegate @Getter private final JCThrow decoratedTree;

        public AJCExpressionTree expr;

        protected AJCThrow(JCThrow tree) {
            super(tree);
            decoratedTree = tree;
        }

        protected AJCThrow(JCThrow tree, AJCExpressionTree expr) {
            this(tree);
            this.expr = expr;
        }
    }

    public static class AJCNewArray extends AJCExpressionTree implements NewArrayTree {
        @Delegate @Getter private final JCNewArray decoratedTree;

        public AJCTypeExpression elemtype;
        public List<AJCExpressionTree> dims;
        // type annotations on inner-most component
        public List<AJCAnnotation> annotations = List.nil();
        public List<AJCExpressionTree> elems;

        protected AJCNewArray(JCNewArray tree) {
            super(tree);
            decoratedTree = tree;
        }

        protected AJCNewArray(JCNewArray tree, AJCTypeExpression elemtype, List<AJCExpressionTree> dims, List<AJCExpressionTree> elems) {
            this(tree);
            this.elemtype = elemtype;
            this.dims = dims;
            this.elems = elems;
        }
    }

    public static class AJCAssign extends AJCExpressionTree implements AssignmentTree, AJCSymbolRef<VarSymbol> {
        @Delegate @Getter private final JCAssign decoratedTree;

        public AJCSymbolRefTree<VarSymbol> lhs;
        public AJCExpressionTree rhs;

        protected AJCAssign(JCAssign tree) {
            super(tree);
            decoratedTree = tree;
        }

        protected AJCAssign(JCAssign tree, AJCSymbolRefTree<VarSymbol> lhs, AJCExpressionTree rhs) {
            this(tree);
            this.lhs = lhs;
            this.rhs = rhs;
        }

        @Override
        public VarSymbol getTargetSymbol() {
            log.debug("GetTargetSymbol on: {}", this);
            return lhs.getTargetSymbol();
        }
    }

    public abstract static class AJCOperatorExpression extends AJCExpressionTree {
        protected AJCOperatorExpression(JCExpression tree) {
            super(tree);
        }

        // Since javac provides no nice class for expressions with operators, instead redefining an identical field in
        // multiple classes, we have to do some stupidity to tidy it up here...
        public void setOperator(Symbol op) {
            JCExpression tree = getDecoratedTree();
            if (tree instanceof JCAssignOp) {
                ((JCAssignOp) tree).operator = op;
            } else if (tree instanceof JCBinary) {
                ((JCBinary) tree).operator = op;
            } else if (tree instanceof JCUnary) {
                ((JCUnary) tree).operator = op;
            } else {
                log.fatal("Unknown operator expression type: " + tree.getClass().getCanonicalName());
                return;
            }
        }

        public abstract Symbol getOperator();
    }

    public static class AJCAssignOp extends AJCOperatorExpression implements CompoundAssignmentTree, AJCSymbolRef<VarSymbol> {
        @Delegate @Getter private final JCAssignOp decoratedTree;

        @Delegate
        public AJCSymbolRefTree<VarSymbol> lhs;
        public AJCExpressionTree rhs;

        protected AJCAssignOp(JCAssignOp tree) {
            super(tree);
            decoratedTree = tree;
        }

        public AJCAssignOp(JCAssignOp tree, AJCSymbolRefTree<VarSymbol> lhs, AJCExpressionTree rhs) {
            this(tree);
            this.lhs = lhs;
            this.rhs = rhs;
        }
    }

    public static class AJCUnary extends AJCOperatorExpression implements UnaryTree {
        @Delegate @Getter private final JCUnary decoratedTree;

        public AJCExpressionTree arg;

        protected AJCUnary(JCUnary tree) {
            super(tree);
            decoratedTree = tree;
        }

        public AJCUnary(JCUnary tree, AJCExpressionTree arg) {
            this(tree);
            this.arg = arg;
        }
    }

    /**
     * A unary assignment, such as a++.
     */
    public static class AJCUnaryAsg extends AJCOperatorExpression implements UnaryTree, AJCSymbolRef<VarSymbol> {
        @Delegate @Getter private final JCUnary decoratedTree;

        @Delegate
        public AJCSymbolRefTree<VarSymbol> arg;

        protected AJCUnaryAsg(JCUnary tree) {
            super(tree);
            decoratedTree = tree;
        }

        public AJCUnaryAsg(JCUnary tree, AJCSymbolRefTree<VarSymbol> arg) {
            this(tree);
            this.arg = arg;
        }
    }

    public static class AJCBinary extends AJCOperatorExpression implements BinaryTree {
        @Delegate @Getter private final JCBinary decoratedTree;

        public AJCExpressionTree lhs;
        public AJCExpressionTree rhs;

        protected AJCBinary(JCBinary tree) {
            super(tree);
            decoratedTree = tree;
        }

        public AJCBinary(JCBinary tree, AJCExpressionTree lhs, AJCExpressionTree rhs) {
            this(tree);
            this.lhs = lhs;
            this.rhs = rhs;
        }
    }

    public static class AJCTypeCast extends AJCExpressionTree implements TypeCastTree {
        @Delegate @Getter private final JCTypeCast decoratedTree;

        public AJCTypeExpression clazz;
        public AJCExpressionTree expr;

        protected AJCTypeCast(JCTypeCast tree) {
            super(tree);
            decoratedTree = tree;
        }

        protected AJCTypeCast(JCTypeCast tree, AJCTypeExpression clazz, AJCExpressionTree expr) {
            this(tree);
            this.clazz = clazz;
            this.expr = expr;
        }
    }

    public static class AJCInstanceOf extends AJCExpressionTree implements InstanceOfTree {
        @Delegate @Getter private final JCInstanceOf decoratedTree;

        public AJCExpressionTree expr;
        public AJCSymbolRef<TypeSymbol> clazz;

        protected AJCInstanceOf(JCInstanceOf tree) {
            super(tree);
            decoratedTree = tree;
        }

        protected AJCInstanceOf(JCInstanceOf tree, AJCExpressionTree expr, AJCSymbolRef<TypeSymbol> clazz) {
            this(tree);
            this.expr = expr;
            this.clazz = clazz;
        }
    }

    public static class AJCArrayAccess extends AJCSymbolRefTree<VarSymbol> implements ArrayAccessTree {
        @Delegate @Getter private final JCArrayAccess decoratedTree;

        public AJCExpressionTree indexed;
        public AJCExpressionTree index;

        protected AJCArrayAccess(JCArrayAccess tree) {
            super(tree);
            decoratedTree = tree;
        }

        protected AJCArrayAccess(JCArrayAccess tree, AJCExpressionTree indexed, AJCExpressionTree index) {
            this(tree);
            this.indexed = indexed;
            this.index = index;
        }

        @Override
        public VarSymbol getTargetSymbol() {
            // Since you can do things like `f()[3]`, the field can't be a SymbolRefTree.
            if (indexed instanceof AJCSymbolRef) {
                Symbol sym = ((AJCSymbolRef) indexed).getTargetSymbol();
                if (sym instanceof VarSymbol) {
                    return (VarSymbol) sym;
                }
            }

            log.warn("Unconventional array access {} returning null symbol!", this);
            return null;
        }
    }

    public static class AJCFieldAccess<T extends Symbol> extends AJCSymbolRefTree<T> implements MemberSelectTree {
        @Delegate @Getter private final JCFieldAccess decoratedTree;

        // The lhs of the dot... We're selecting <selected>.<name>. So literals...
        public AJCTree selected;

        protected AJCFieldAccess(JCFieldAccess tree) {
            super(tree);
            decoratedTree = tree;
        }

        protected AJCFieldAccess(JCFieldAccess tree, AJCTree selected) {
            this(tree);
            this.selected = selected;
        }

        @Override
        @SuppressWarnings("unchecked")
        public T getTargetSymbol() {
            return (T) decoratedTree.sym;
        }
    }

    public static class AJCIdent<T extends Symbol> extends AJCSymbolRefTree<T> implements IdentifierTree {
        @Delegate @Getter private final JCIdent decoratedTree;

        protected AJCIdent(JCIdent tree) {
            super(tree);
            decoratedTree = tree;
        }

        @SuppressWarnings("unchecked")
        public T getSym() {
            return (T) decoratedTree.sym;
        }

        @Override
        public T getTargetSymbol() {
            return getSym();
        }
    }

    public static class AJCLiteral extends AJCExpressionTree implements LiteralTree {
        private static final String INVALID_ARGUMENT_TYPE = "Invalid literal type! Expected {} got {}";
        private static final String INVALID_ARGUMENT_TYPE_SIMPLE = "Invalid literal type!";

        /**
         * Sanitise a literal value, returning the value to be passed to the underlying tree node after applying whatever
         * stupid transformations are called for by the tree representation Javac is using this week.
         * Throws an IllegalArgumentException if the input TypeTag is inconsistent with the input value.
         *
         * @param tag TypeTag of the literal being considered.
         * @param value Value of the literal being considered.
         * @return The value that is necessary for creating a JCLiteral to hold this information.
         */
        public static Object sanitiseLiteralValue(TypeTag tag, Object value) {
            if (tag == BOOLEAN) {
                // Booleans are represented as integer 1/0 in the obvious way.
                if (value instanceof Boolean) {
                    if ((Boolean) value) {
                        return 1;
                    } else {
                        return 0;
                    }
                }
            } else if (tag == CHAR) {
                // Chars are represented as integers for... some reason...
                if (value instanceof Character) {
                    // Yes. This *is* how javac does it.
                    return (int) value.toString().charAt(0);
                }
            }

            // A quick check to verify that the type of the input object matches the TypeTag.
            // Apparently the javac authors have never heard of static polymorphism...
            switch (tag) {
                case CLASS:
                    // A String literal. Yes. Apparently they thought that was a good name for the tag, too.
                    if (!(value instanceof String)) {
                        log.fatal(INVALID_ARGUMENT_TYPE, tag, value.getClass().getSimpleName());
                        throw new IllegalArgumentException(INVALID_ARGUMENT_TYPE_SIMPLE);
                    }
                    break;
                case INT:
                    if (!(value instanceof Integer)) {
                        log.fatal(INVALID_ARGUMENT_TYPE, tag, value.getClass().getSimpleName());
                        throw new IllegalArgumentException(INVALID_ARGUMENT_TYPE_SIMPLE);
                    }
                    break;
                case LONG:
                    if (!(value instanceof Long)) {
                        log.fatal(INVALID_ARGUMENT_TYPE, tag, value.getClass().getSimpleName());
                        throw new IllegalArgumentException(INVALID_ARGUMENT_TYPE_SIMPLE);
                    }
                    break;
                case BYTE:
                    if (!(value instanceof Byte)) {
                        log.fatal(INVALID_ARGUMENT_TYPE, tag, value.getClass().getSimpleName());
                        throw new IllegalArgumentException(INVALID_ARGUMENT_TYPE_SIMPLE);
                    }
                    break;
                case CHAR:
                    // ... Because chars are integers, remember...
                    if (!(value instanceof Integer)) {
                        log.fatal(INVALID_ARGUMENT_TYPE, tag, value.getClass().getSimpleName());
                        throw new IllegalArgumentException(INVALID_ARGUMENT_TYPE_SIMPLE);
                    }
                    break;
                case DOUBLE:
                    if (!(value instanceof Double)) {
                        log.fatal(INVALID_ARGUMENT_TYPE, tag, value.getClass().getSimpleName());
                        throw new IllegalArgumentException(INVALID_ARGUMENT_TYPE_SIMPLE);
                    }
                    break;
                case FLOAT:
                    if (!(value instanceof Float)) {
                        log.fatal(INVALID_ARGUMENT_TYPE, tag, value.getClass().getSimpleName());
                        throw new IllegalArgumentException(INVALID_ARGUMENT_TYPE_SIMPLE);
                    }
                    break;
                case SHORT:
                    if (!(value instanceof Short)) {
                        log.fatal(INVALID_ARGUMENT_TYPE, tag, value.getClass().getSimpleName());
                        throw new IllegalArgumentException(INVALID_ARGUMENT_TYPE_SIMPLE);
                    }
                    break;
                case BOOLEAN:
                    // Booleans are secretly ints...
                    if (!(value instanceof Integer)) {
                        log.fatal(INVALID_ARGUMENT_TYPE, tag, value.getClass().getSimpleName());
                        throw new IllegalArgumentException(INVALID_ARGUMENT_TYPE_SIMPLE);
                    }
                    break;
                case BOT:
                    // Null literal.
                    if (value != null) {
                        log.fatal(INVALID_ARGUMENT_TYPE, tag, "Null literal without null value!");
                        throw new IllegalArgumentException(INVALID_ARGUMENT_TYPE_SIMPLE);
                    }
                    break;

                default:
                    log.error(tag);
                    log.fatal(INVALID_ARGUMENT_TYPE, tag, value.getClass().getSimpleName());
                    throw new IllegalArgumentException(INVALID_ARGUMENT_TYPE_SIMPLE);
            }

            return value;
        }

        @Delegate @Getter private final JCLiteral decoratedTree;

        protected AJCLiteral(JCLiteral tree) {
            super(tree);

            tree.value = sanitiseLiteralValue(tree.typetag, tree.value);
            decoratedTree = tree;
            effects = new Effects(EffectSet.NO_EFFECTS);
        }

        /**
         * Update the value to the given value.
         */
        public void setValue(Object value) {
            TypeTag tag = decoratedTree.typetag;

            value = sanitiseLiteralValue(tag, value);

            decoratedTree.value = value;
        }
    }

    /**
     * Base class of type expressions - may not hold an effect annotation.
     */
    public abstract static class AJCTypeExpression extends AJCTree implements ExpressionTree, AJCExpression, AJCSymbolRef<TypeSymbol> {
        @Delegate @Getter private final JCExpression decoratedTree;

        protected AJCTypeExpression(JCExpression tree) {
            super(tree);
            decoratedTree = tree;
        }
    }

    public static class AJCPrimitiveTypeTree extends AJCTypeExpression implements PrimitiveTypeTree {
        @Delegate @Getter private final JCPrimitiveTypeTree decoratedTree;

        protected AJCPrimitiveTypeTree(JCPrimitiveTypeTree tree) {
            super(tree);
            decoratedTree = tree;
        }

        @Override
        public TypeSymbol getTargetSymbol() {
            return TreeUtils.typeKindToType(decoratedTree.getPrimitiveTypeKind()).tsym;
        }
    }

    /**
     * Non-primitive type tree...
     */
    public static class AJCObjectTypeTree extends AJCTypeExpression {
        @Delegate @Getter private final AJCSymbolRefTree<TypeSymbol> underlyingSymbol;

        protected AJCObjectTypeTree(AJCSymbolRefTree<TypeSymbol> tree) {
            super(tree.getDecoratedTree());
            underlyingSymbol = tree;
        }
    }

    /**
     * Class for array types. Represents an array of type elemtype.
     */
    public static class AJCArrayTypeTree extends AJCTypeExpression implements ArrayTypeTree {
        @Delegate @Getter private final JCArrayTypeTree decoratedTree;

        public AJCTypeExpression elemtype;

        protected AJCArrayTypeTree(JCArrayTypeTree tree) {
            super(tree);
            decoratedTree = tree;
        }

        protected AJCArrayTypeTree(JCArrayTypeTree tree, AJCTypeExpression elemtype) {
            this(tree);
            this.elemtype = elemtype;
        }

        @Override
        public TypeSymbol getTargetSymbol() {
            return elemtype.getTargetSymbol();
        }
    }

    public static class AJCTypeUnion extends AJCTypeExpression implements UnionTypeTree {
        @Delegate @Getter private final JCTypeUnion decoratedTree;

        public List<AJCTypeExpression> alternatives;

        protected AJCTypeUnion(JCTypeUnion tree) {
            super(tree);
            decoratedTree = tree;
        }

        protected AJCTypeUnion(JCTypeUnion tree, List<AJCTypeExpression> components) {
            this(tree);
            alternatives = components;
        }

        /**
         * By convention, just return the first one. If type unions get more widespread adoption in Java more work needed
         * here... (ie. The ability to return sets of TypeSymbols).
         */
        @Override
        public TypeSymbol getTargetSymbol() {
            if (!alternatives.isEmpty()) {
                return alternatives.get(0).getTargetSymbol();
            }

            return null;
        }
    }

    public static class AJCAnnotation extends AJCExpressionTree implements AnnotationTree {
        @Delegate @Getter private final JCAnnotation decoratedTree;

        // TODO: Type expression?
        public AJCTree annotationType;
        public List<AJCExpressionTree> args;

        protected AJCAnnotation(JCAnnotation tree) {
            super(tree);
            decoratedTree = tree;
        }

        protected AJCAnnotation(JCAnnotation tree, AJCTree annotationType, List<AJCExpressionTree> args) {
            this(tree);
            this.annotationType = annotationType;
            this.args = args;
        }
    }

    public static class AJCModifiers extends AJCTree {
        @Delegate @Getter private final JCModifiers decoratedTree;

        public List<AJCAnnotation> annotations;

        protected AJCModifiers(JCModifiers tree) {
            super(tree);
            decoratedTree = tree;
        }

        protected AJCModifiers(JCModifiers tree, List<AJCAnnotation> annotations) {
            this(tree);
            this.annotations = annotations;
        }
    }

    // TODO: What does this even represent? o.0
    public static class AJCAnnotatedType extends AJCTypeExpression implements AnnotatedTypeTree {
        @Delegate @Getter private final JCAnnotatedType decoratedTree;

        public AJCTypeExpression underlyingType;

        protected AJCAnnotatedType(JCAnnotatedType tree) {
            super(tree);
            decoratedTree = tree;
        }

        protected AJCAnnotatedType(JCAnnotatedType tree, AJCTypeExpression underlyingType) {
            this(tree);
            this.underlyingType = underlyingType;
        }

        @Override
        public TypeSymbol getTargetSymbol() {
            return underlyingType.getTargetSymbol();
        }
    }

    public static class AJCErroneous extends AJCExpressionTree {
        @Delegate @Getter private final JCErroneous decoratedTree;

        protected AJCErroneous(JCErroneous tree) {
            super(tree);
            decoratedTree = tree;
        }
    }

    public static class AJCLetExpr extends AJCExpressionTree {
        @Delegate @Getter private final LetExpr decoratedTree;

        public List<AJCVariableDecl> defs;
        public AJCExpressionTree expr;

        protected AJCLetExpr(LetExpr tree) {
            super(tree);
            decoratedTree = tree;
        }

        protected AJCLetExpr(LetExpr tree, List<AJCVariableDecl> defs, AJCExpressionTree expr) {
            this(tree);
            this.defs = defs;
            this.expr = expr;
        }
    }

    /**
     * An abstract class for nodes that represent references to symbols - Idents and FieldAccesses, mostly.
     */
    public abstract static class AJCSymbolRefTree<T extends Symbol> extends AJCExpressionTree implements AJCSymbolRef<T> {
        protected AJCSymbolRefTree(JCExpression tree) {
            super(tree);
        }

        @Override
        public abstract T getTargetSymbol();
    }

    /**
     * An interface for nodes that refer to a symbol.
     */
    public interface AJCSymbolRef<T extends Symbol> {
        T getTargetSymbol();
    }

    // Resolving generics for Lombokification...
    private interface AJCVarSymbolRef extends AJCSymbolRef<VarSymbol> {}
    private interface AJCMethodSymbolRef extends AJCSymbolRef<MethodSymbol> {}
    private interface AJCClassSymbolRef extends AJCSymbolRef<ClassSymbol> {}
    private interface AJCTypeSymbolRef extends AJCSymbolRef<TypeSymbol> {}

    /** An interface for tree factories */
    public interface Factory {
        AJCClassDecl ClassDef(AJCModifiers mods, Name name, AJCExpressionTree extending,
                              List<AJCExpressionTree> implementing, List<AJCVariableDecl> fields, List<AJCMethodDecl> methods, List<AJCClassDecl> classes);
        AJCMethodDecl MethodDef(AJCModifiers mods,
                               Name name,
                               AJCTypeExpression restype,
                               AJCVariableDecl recvparam,
                               List<AJCVariableDecl> params,
                               List<AJCExpressionTree> thrown,
                               AJCBlock body,
                               AJCExpressionTree defaultValue);
        AJCMethodDecl MethodDef(AJCModifiers mods, Name name, AJCTypeExpression restype, List<AJCVariableDecl> params, List<AJCExpressionTree> thrown, AJCBlock body, AJCExpressionTree defaultValue);
        AJCVariableDecl VarDef(AJCModifiers mods,
                              Name name,
                              AJCTypeExpression vartype,
                              AJCExpressionTree init);
        AJCEmptyExpression EmptyExpression();
        AJCVariableDecl VarDef(VarSymbol v, AJCExpressionTree init);
        AJCSkip Skip();
        AJCBlock Block(long flags, List<AJCStatement> stats);
        AJCDoWhileLoop DoLoop(AJCBlock body, AJCExpressionTree cond);
        AJCWhileLoop WhileLoop(AJCExpressionTree cond, AJCBlock body);
        AJCForLoop ForLoop(List<AJCStatement> init,
                          AJCExpressionTree cond,
                          List<AJCExpressionStatement> step,
                          AJCBlock body);
        AJCLabeledStatement Labelled(Name label, AJCStatement body);
        AJCSwitch Switch(AJCExpressionTree selector, List<AJCCase> cases);
        AJCCase Case(AJCExpressionTree pat, List<AJCStatement> stats);
        AJCSynchronized Synchronized(AJCExpressionTree lock, AJCBlock body);
        AJCTry Try(AJCBlock body, List<AJCCatch> catchers, AJCBlock finalizer);
        AJCCatch Catch(AJCVariableDecl param, AJCBlock body);
        AJCConditional Conditional(AJCExpressionTree cond,
                                  AJCExpressionTree thenpart,
                                  AJCExpressionTree elsepart);
        AJCIf If(AJCExpressionTree cond, AJCBlock thenpart, AJCBlock elsepart);
        AJCExpressionStatement Exec(AJCExpressionTree expr);
        AJCBreak Break(Name label);
        AJCContinue Continue(Name label);
        AJCReturn Return(AJCExpressionTree expr);
        AJCThrow Throw(AJCExpressionTree expr);
        AJCCall Call(AJCSymbolRefTree<MethodSymbol> fn, List<AJCExpressionTree> args);
        AJCNewClass NewClass(AJCSymbolRefTree<ClassSymbol> clazz, List<AJCExpressionTree> args);
        AJCNewArray NewArray(AJCTypeExpression elemtype, List<AJCExpressionTree> dims, List<AJCExpressionTree> elems);
        AJCAssign Assign(AJCSymbolRefTree<VarSymbol> lhs, AJCExpressionTree rhs);
        AJCAssignOp Assignop(Tag opcode, AJCSymbolRefTree<VarSymbol> lhs, AJCExpressionTree rhs, boolean resolveOperator);
        AJCUnary Unary(Tag opcode, AJCExpressionTree arg, boolean resolveOperator);
        AJCUnaryAsg UnaryAsg(Tag opcode, AJCSymbolRefTree<VarSymbol> arg, boolean resolveOperator);
        AJCBinary Binary(Tag opcode, AJCExpressionTree lhs, AJCExpressionTree rhs, boolean resolveOperator);
        AJCAssignOp Assignop(Tag opcode, AJCSymbolRefTree<VarSymbol> lhs, AJCExpressionTree rhs);
        AJCUnary Unary(Tag opcode, AJCExpressionTree arg);
        AJCUnaryAsg UnaryAsg(Tag opcode, AJCSymbolRefTree<VarSymbol> arg);
        AJCBinary Binary(Tag opcode, AJCExpressionTree lhs, AJCExpressionTree rhs);
        AJCTypeCast TypeCast(AJCTypeExpression clazz, AJCExpressionTree expr);
        AJCInstanceOf InstanceOf(AJCExpressionTree expr, AJCSymbolRef<TypeSymbol> clazz);
        AJCArrayAccess ArrayAccess(AJCExpressionTree indexed, AJCExpressionTree index);
        <T extends Symbol> AJCFieldAccess<T> Select(AJCTree base, T sym);
        <T extends Symbol> AJCIdent<T> Ident(Name idname);
        <T extends Symbol> AJCIdent<T> Ident(T sym);
        AJCLiteral Literal(TypeTag tag, Object value);
        AJCLiteral Literal(Object value);
        AJCPrimitiveTypeTree TypeIdent(TypeTag typetag);
        AJCArrayTypeTree TypeArray(AJCTypeExpression elemtype);
        AJCObjectTypeTree ObjectType(AJCSymbolRefTree<TypeSymbol> ref);
        AJCTypeUnion TypeUnion(List<AJCTypeExpression> components);
        AJCAnnotation Annotation(AJCTree annotationType, List<AJCExpressionTree> args);
        AJCModifiers Modifiers(long flags, List<AJCAnnotation> annotations);
        AJCModifiers Modifiers(long flags);
        AJCAnnotatedType AnnotatedType(AJCTypeExpression underlyingType);
        AJCErroneous Erroneous(List<? extends AJCTree> errs);
        AJCLetExpr LetExpr(List<AJCVariableDecl> defs, AJCExpressionTree expr);
    }

}
TOP

Related Classes of joust.tree.annotatedtree.AJCTree$AJCSwitch

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.