/*
* This file is part of the X10 project (http://x10-lang.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* This file was originally derived from the Polyglot extensible compiler framework.
*
* (C) Copyright 2000-2007 Polyglot project group, Cornell University
* (C) Copyright IBM Corporation 2007-2012.
*/
package polyglot.types;
import java.util.*;
import polyglot.frontend.Globals;
import polyglot.frontend.Job;
import polyglot.util.*;
import x10.types.FunctionType_c;
import x10.types.MethodInstance;
import x10.types.X10ClassDef;
import x10.types.X10ClassType;
import x10.types.X10ParsedClassType;
/**
* A <code>ClassType</code> represents a class -- either loaded from a
* classpath, parsed from a source file, or obtained from other source.
*/
public abstract class ClassType_c extends ReferenceType_c implements ClassType
{
private static final long serialVersionUID = 3894454742809763539L;
protected Ref<? extends X10ClassDef> def;
/** Used for deserializing types. */
protected ClassType_c() { }
public ClassType_c(TypeSystem ts, Position pos, Position errorPos, Ref<? extends X10ClassDef> def) {
super(ts, pos, errorPos);
this.def = def;
}
public X10ClassDef def() {
return def.get();
}
public boolean equalsImpl(TypeObject t) {
if (t instanceof ClassType_c) {
Ref<? extends ClassDef> thisDef = def;
Ref<? extends ClassDef> thatDef = ((ClassType_c) t).def;
return thisDef.get() == thatDef.get();
}
return false;
}
protected transient Resolver memberCache;
public Resolver resolver() {
if (memberCache == null) {
memberCache = new AnotherCachingResolver(ts.createClassContextResolver(this),
ts.extensionInfo().getOptions().reporter);
}
return memberCache;
}
public ClassType_c copy() {
ClassType_c n = (ClassType_c) super.copy();
n.memberCache = null;
return n;
}
public abstract Job job();
/** Get the class's kind. */
public abstract ClassDef.Kind kind();
/** Get the class's outer class, or null if a top-level class. */
public abstract ClassType outer();
/** Get the short name of the class, if possible. */
public abstract Name name();
/** Get the container class if a member class. */
public ContainerType container() {
if (! isMember())
throw new InternalCompilerError("Non-member class " + this + " cannot have container classes.");
if (outer() == null)
throw new InternalCompilerError("Member class " + this + " must have an outer class.");
return outer();
}
/** Get the full name of the class, if possible. */
public QName fullName() {
if (isTopLevel()) {
Name name = name();
return QName.make(package_() != null ? package_().fullName() : null, name);
}
else if (isMember()) {
Name name = name();
return QName.make(container().fullName(), name);
}
else if (isLocal()) {
return QName.make(null, name());
}
else if (isAnonymous()) {
return QName.make(null, Name.make("<anonymous class>"));
}
else {
return QName.make(null, Name.make("<unknown class>"));
}
}
public boolean isTopLevel() { return kind() == ClassDef.TOP_LEVEL; }
public boolean isMember() { return kind() == ClassDef.MEMBER; }
public boolean isLocal() { return kind() == ClassDef.LOCAL; }
public boolean isAnonymous() { return kind() == ClassDef.ANONYMOUS; }
public boolean isGloballyAccessible() {
return kind() == ClassDef.TOP_LEVEL || (kind() == ClassDef.MEMBER && outer().isGloballyAccessible());
}
public boolean isNested() {
// Implement this way rather than with ! isTopLevel() so that
// extensions can add more kinds.
return kind() == ClassDef.MEMBER || kind() == ClassDef.LOCAL || kind() == ClassDef.ANONYMOUS;
}
public boolean isInnerClass() {
// it's an inner class if it is not an interface, it is a nested
// class, and it is not explicitly or implicitly static.
return !flags().isInterface() && isNested() && !flags().isStatic() && !inStaticContext();
}
public boolean isClass() { return true; }
public X10ClassType toClass() { return (X10ClassType) this; }
/** Get the class's package. */
public abstract Package package_();
/** Get the class's flags. */
public abstract Flags flags();
/** Get the class's constructors. */
public abstract List<ConstructorInstance> constructors();
/** Get the class's member classes. */
public abstract List<ClassType> memberClasses();
/** Get the class's methods. */
public abstract List<MethodInstance> methods();
/** Get the class's fields. */
public abstract List<FieldInstance> fields();
/** Get the class's interfaces. */
public abstract List<Type> interfaces();
/** Get the class's super type. */
public abstract Type superClass();
/** Get a list of all the class's MemberInstances. */
public List<MemberInstance<?>> members() {
List<MemberInstance<?>> l = new ArrayList<MemberInstance<?>>();
l.addAll(methods());
l.addAll(fields());
l.addAll(constructors());
l.addAll(memberClasses());
return l;
}
/** Get a member class of the class by name. */
public ClassType memberClassMatching(Matcher<Type> matcher) {
for (ClassType t : memberClasses()) {
try {
Type n = matcher.instantiate(t);
if (n instanceof ClassType)
return (ClassType) n;
}
catch (SemanticException e) {
}
}
return null;
}
public Type memberTypeMatching(Matcher<Type> matcher) {
return memberClassMatching(matcher);
}
public String translate(Resolver c) {
if (isTopLevel()) {
if (package_() == null) {
return name().toString();
}
// Use the short name if it is unique.
if (c != null && !typeSystem().extensionInfo().getOptions().fully_qualified_names) {
try {
List<Type> xl = c.find(ts.TypeMatcher(name()));
for (Type x : xl) {
if (x instanceof ClassType && def().equals(((ClassType) x).def())) {
return name().toString();
}
}
}
catch (SemanticException e) {
}
}
return package_().translate(c) + "." + name();
}
else if (isMember()) {
// Use only the short name if the outer class is anonymous.
if (container().toClass().isAnonymous()) {
return name().toString();
}
// Use the short name if it is unique.
if (c != null && !typeSystem().extensionInfo().getOptions().fully_qualified_names) {
try {
List<Type> xl = c.find(ts.TypeMatcher(name()));
for (Type x : xl) {
if (x instanceof ClassType && def().equals(((ClassType) x).def())) {
return name().toString();
}
}
}
catch (SemanticException e) {
}
}
return container().translate(c) + "." + name();
}
else if (isLocal()) {
return name().toString();
}
else {
throw new InternalCompilerError("Cannot translate an anonymous class.");
}
}
public String typeToString() {
if (isTopLevel()) {
if (package_() != null) {
return package_() + "." + name();
}
return name().toString();
}
else if (isMember()) {
return container().toString() + "." + name();
}
else if (isLocal()) {
return name().toString();
}
else if (isAnonymous()) {
return "<anonymous class>";
}
else {
return "<unknown class>";
}
}
/** Pretty-print the name of this class to w. */
public void print(CodeWriter w) {
// XXX This code duplicates the logic of toString.
if (isTopLevel()) {
if (package_() != null) {
package_().print(w);
w.write(".");
w.allowBreak(2, 3, "", 0);
}
w.write(name().toString());
final X10ParsedClassType baseType = Types.myBaseType(this);
if (baseType!=null
&& !(baseType instanceof FunctionType_c)) {
List<Type> typeArguments = baseType.typeArguments();
if (typeArguments != null && typeArguments.size() > 0) {
w.write("[");
w.allowBreak(2, 2, "", 0); // miser mode
w.begin(0);
for (Iterator<Type> i = typeArguments.iterator(); i.hasNext(); ) {
Type t = i.next();
t.print(w);
if (i.hasNext()) {
w.write(",");
w.allowBreak(0, " ");
}
}
w.write("]");
w.end();
}
}
} else if (isMember()) {
container().print(w);
w.write(".");
w.allowBreak(2, 3, "", 0);
w.write(name().toString());
} else if (isLocal()) {
w.write(name().toString());
} else if (isAnonymous()) {
w.write("<anonymous class>");
} else {
w.write("<unknown class>");
}
}
public boolean isEnclosed(ClassType maybe_outer) {
if (isTopLevel())
return false;
else if (outer() != null)
return outer().equals((Object) maybe_outer) ||
outer().isEnclosed(maybe_outer);
else
throw new InternalCompilerError("Non top-level classes " +
"must have outer classes.");
}
/**
* Return true if an object of the class has
* an enclosing instance of <code>encl</code>.
*/
public boolean hasEnclosingInstance(ClassType encl) {
if (this.typeEquals(encl, ts.emptyContext())) {
// object o is the zeroth lexically enclosing instance of itself.
return true;
}
if (!isInnerClass() || inStaticContext()) {
// this class is not an inner class, or was declared in a static
// context; it cannot have an enclosing
// instance of anything.
return false;
}
// see if the immediately lexically enclosing class has an
// appropriate enclosing instance
return this.outer().hasEnclosingInstance(encl);
}
}