package polyglot.ext.jl.ast;

import polyglot.ast.Local;
import polyglot.ast.Node;
import polyglot.ast.Term;
import polyglot.ast.Precedence;
import polyglot.types.Context;
import polyglot.types.Flags;
import polyglot.types.LocalInstance;
import polyglot.types.SemanticException;
import polyglot.types.TypeSystem;
import polyglot.util.CodeWriter;
import polyglot.util.Position;
import polyglot.visit.PrettyPrinter;
import polyglot.visit.TypeBuilder;
import polyglot.visit.TypeChecker;
import polyglot.visit.CFGBuilder;
import java.util.List;

/** 
 * A local variable expression.
 */
public class Local_c extends Expr_c implements Local
{
  protected String name;
  protected LocalInstance li;

  public Local_c(Position pos, String name) {
    super(pos);
    this.name = name;
  }

  /** Get the precedence of the local. */
  public Precedence precedence() { 
    return Precedence.LITERAL;
  }

  /** Get the name of the local. */
  public String name() {
    return this.name;
  }

  /** Set the name of the local. */
  public Local name(String name) {
    Local_c n = (Local_c) copy();
    n.name = name;
    return n;
  }

  /** Return the access flags of the variable. */
  public Flags flags() {
    return li.flags();
  }

  /** Get the local instance of the local. */
  public LocalInstance localInstance() {
    return li;
  }

  /** Set the local instance of the local. */
  public Local localInstance(LocalInstance li) {
    Local_c n = (Local_c) copy();
    n.li = li;
    return n;
  }

  public Node buildTypes(TypeBuilder tb) throws SemanticException {
      Local_c n = (Local_c) super.buildTypes(tb);

      TypeSystem ts = tb.typeSystem();

      LocalInstance li = ts.localInstance(position(), Flags.NONE,
                                          ts.unknownType(position()), name);
      return n.localInstance(li);
  }

  /** Type check the local. */
  public Node typeCheck(TypeChecker tc) throws SemanticException {
    Context c = tc.context();
    LocalInstance li = c.findLocal(name);
    
    // if the local is defined in an outer class, then it must be final
    if (!c.isLocal(li.name())) {
        // this local is defined in an outer class
        if (!li.flags().isFinal()) {
            throw new SemanticException("Local variable \"" + li.name() + 
                    "\" is accessed from an inner class, and must be declared " +
                    "final.",
                    this.position());                     
        }
    }
    
    return localInstance(li).type(li.type());
  }

  /**
   * Return the first (sub)term performed when evaluating this
   * term.
   */
  public Term entry() {
      return this;
  }

  /**
   * Visit this term in evaluation order.
   */
  public List acceptCFG(CFGBuilder v, List succs) {
      return succs;
  }

  public String toString() {
    return name;
  }

  /** Write the local to an output file. */
  public void prettyPrint(CodeWriter w, PrettyPrinter tr) {
    w.write(name);
  }

  /** Dumps the AST. */
  public void dump(CodeWriter w) {
    super.dump(w);

    w.allowBreak(4, " ");
    w.begin(0);
    w.write("(name " + name + ")");
    w.end();
  }

  public boolean isConstant() {
    return li.isConstant();
  }

  public Object constantValue() {
    return li.constantValue();
  }

}

@@ -3040,24 +3040,134 @@
 r.dump(w);%0A%0A
+    if (li != null) %7B%0A%09w.allowBreak(4, %22 %22);%0A%09w.begin(0);%0A%09w.write(%22(instance %22 + li + %22)%22);%0A%09w.end();%0A    %7D%0A%0A
     w.allowB

Include local instance information when dumping a Local_c.