package polyglot.ext.jl.ast;

import polyglot.ast.Node;
import polyglot.ast.Precedence;
import polyglot.ast.Special;
import polyglot.ast.TypeNode;
import polyglot.types.ClassType;
import polyglot.types.Context;
import polyglot.types.SemanticException;
import polyglot.types.TypeSystem;
import polyglot.util.CodeWriter;
import polyglot.util.Position;
import polyglot.visit.NodeVisitor;
import polyglot.visit.PrettyPrinter;
import polyglot.visit.TypeChecker;
import polyglot.visit.CFGBuilder;
import java.util.List;

/**
 * A <code>Special</code> is an immutable representation of a
 * reference to <code>this</code> or <code>super</code in Java.  This
 * reference can be optionally qualified with a type such as 
 * <code>Foo.this</code>.
 */
public class Special_c extends Expr_c implements Special
{
    protected Special.Kind kind;
    protected TypeNode qualifier;

    public Special_c(Position pos, Special.Kind kind, TypeNode qualifier) {
	super(pos);
	this.kind = kind;
	this.qualifier = qualifier;
    }

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

    /** Get the kind of the special expression, either this or super. */
    public Special.Kind kind() {
	return this.kind;
    }

    /** Set the kind of the special expression, either this or super. */
    public Special kind(Special.Kind kind) {
	Special_c n = (Special_c) copy();
	n.kind = kind;
	return n;
    }

    /** Get the qualifier of the special expression. */
    public TypeNode qualifier() {
	return this.qualifier;
    }

    /** Set the qualifier of the special expression. */
    public Special qualifier(TypeNode qualifier) {
	Special_c n = (Special_c) copy();
	n.qualifier = qualifier;
	return n;
    }

    /** Reconstruct the expression. */
    protected Special_c reconstruct(TypeNode qualifier) {
	if (qualifier != this.qualifier) {
	    Special_c n = (Special_c) copy();
	    n.qualifier = qualifier;
	    return n;
	}

	return this;
    }

    /** Visit the children of the expression. */
    public Node visitChildren(NodeVisitor v) {
	TypeNode qualifier = (TypeNode) visitChild(this.qualifier, v);
	return reconstruct(qualifier);
    }

    /** Type check the expression. */
    public Node typeCheck(TypeChecker tc) throws SemanticException {
        TypeSystem ts = tc.typeSystem();
        Context c = tc.context();

        ClassType t;

        if (qualifier == null) {
            // an unqualified "this" or "super"
            t = c.currentClass();
        }
        else {    
	    if (! qualifier.type().isClass()) {
		throw new SemanticException("Qualified " + kind +
		    " expression must be of a class type",
		    qualifier.position());
	    }

            t = qualifier.type().toClass();

            if (!c.currentClass().hasEnclosingInstance(t)) {
                throw new SemanticException("The nested class \"" + 
                            c.currentClass() + "\" does not have " +
                            "an enclosing instance of type \"" +
                            t + "\".", qualifier.position());
            }
	}

        if (c.inStaticContext() && ts.equals(t, c.currentClass())) {
            // trying to access "this" or "super" from a static context.
            throw new SemanticException("Cannot access a non-static " +
                "field or method, or refer to \"this\" or \"super\" " + 
                "from a static context.",  this.position());
        }

	if (kind == THIS) {
	    return type(t);
	}
	else {
	    return type(t.superType());
	}
    }

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

    public String toString() {
	return (qualifier != null ? qualifier + "." : "") + kind;
    }

    /** Write the expression to an output file. */
    public void prettyPrint(CodeWriter w, PrettyPrinter tr) {
	if (qualifier != null) {
	    print(qualifier, w, tr);
	    w.write(".");
	}

	w.write(kind.toString());
    }

    public void dump(CodeWriter w) {
      super.dump(w);

      if (kind != null) {
        w.allowBreak(4, " ");
        w.begin(0);
        w.write("(kind " + kind + ")");
        w.end();
      }
    }
}

@@ -34,483 +34,160 @@
 ort 
-polyglot.ast.Node;%0Aimport polyglot.ast.Precedence;%0Aimport polyglot.ast.Special;%0Aimport polyglot.ast.TypeNode;%0Aimport polyglot.types.ClassType;%0Aimport polyglot.types.Context;%0Aimport polyglot.types.SemanticException;%0Aimport polyglot.types.TypeSystem;%0Aimport polyglot.util.CodeWriter;%0Aimport polyglot.util.Position;%0Aimport polyglot.visit.NodeVisitor;%0Aimport polyglot.visit.PrettyPrinter;%0Aimport polyglot.visit.TypeChecker;%0Aimport polyglot.visit.CFGBuilder;%0Aimport java.util.List
+java.util.List;%0A%0Aimport polyglot.ast.*;%0Aimport polyglot.types.*;%0Aimport polyglot.util.CodeWriter;%0Aimport polyglot.util.Position;%0Aimport polyglot.visit.*
 ;%0A%0A/
@@ -3194,16 +3194,35 @@
 %09%7D%0A%09else
+ if (kind == SUPER)
  %7B%0A%09    
@@ -3252,16 +3252,37 @@
 ());%0A%09%7D%0A
+        return this;%0A
     %7D%0A%0A 

make more extensible by not assuming that This and Super and the only Special kinds