/*
* 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.main.Reporter;
import polyglot.util.*;
import x10.types.ConstrainedType;
import x10.types.constraints.CConstraint;
import x10.util.CollectionFactory;
/**
* A <code>ClassContextResolver</code> looks up type names qualified with a class name.
* For example, if the class is "A.B", the class context will return the class
* for member class "A.B.C" (if it exists) when asked for "C".
*/
public class ClassContextResolver extends AbstractAccessControlResolver {
protected Type type;
protected Reporter reporter;
/**
* Construct a resolver.
* @param ts The type system.
* @param type The type in whose context we search for member types.
*/
public ClassContextResolver(TypeSystem ts, Type type) {
super(ts);
this.type = type;
this.reporter = ts.extensionInfo().getOptions().reporter;
}
public String toString() {
return "(class-context " + type + ")";
}
/**
* Find a type object in the context of the class.
* @param name The name to search for.
*/
public List<Type> find(Matcher<Type> matcher, Context context) throws SemanticException {
Name name = matcher.name();
if (reporter.should_report(TOPICS, 2))
reporter.report(2, "Looking for " + name + " in " + this);
if (! (type instanceof ClassType)) {
throw new NoClassException(name.toString(), type);
}
ClassType type = (ClassType) this.type;
Type m = null;
QName fullName = null;
QName rawName = null;
if (type.isGloballyAccessible()) {
fullName = QName.make(type.fullName(), name);
QName q = ts.getTransformedClassName(type.def());
rawName = QName.make(q.qualifier(), Name.make(q.name() + "$" + name));
}
if (fullName != null) {
List<Type> sr = null;
// First check the system resolver.
sr = ts.systemResolver().check(fullName);
// Try the raw class file name.
if (sr == null) {
sr = ts.systemResolver().check(rawName);
}
if (sr == null) {
// Go to disk, but only if there is no job for the type.
// If there is a job, all members should be in the resolver
// already.
boolean useLoadedResolver = true;
if (type instanceof ParsedTypeObject) {
ParsedTypeObject pto = (ParsedTypeObject) type;
if (pto.job() != null) {
useLoadedResolver = false;
}
}
if (useLoadedResolver) {
try {
sr = ts.systemResolver().find(rawName);
}
catch (SemanticException e) {
// Not found; will fall through to error handling code
}
}
}
// If we found something, verify that it matches.
if (sr != null) {
for (Type q : sr) {
try {
m = matcher.instantiate(q);
break;
}
catch (SemanticException e) {
// Doesn't match; try again.
}
}
}
}
// Check if the member was explicitly declared.
if (m == null) {
m = type.memberTypeMatching(matcher);
}
// If we found something, make sure it's accessible.
Type baseM = m;
if (Types.isConstrainedType(m)) {
baseM = Types.baseType(m);
}
if (Types.isClass(baseM)) {
ClassType mt = baseM.toClass();
if (! mt.isMember()) {
throw new SemanticException("Class " + mt +
" is not a member class, " +
" but was found in " + type + ".");
}
if (mt.outer().def() != type.def()) {
throw new SemanticException("Class " + mt +
" is not a member class " +
" of " + type + ".");
}
return CollectionUtil.<Type>list(Types.container(m, type));
}
if (m instanceof MemberInstance<?>) {
MemberInstance<?> mi = (MemberInstance<?>) m;
if (! mi.container().equals((Object) type)) {
throw new SemanticException("Type " + mi +
" is not a member " +
" of " + type + ".");
}
}
if (m != null) {
if (context!=null && ! canAccess(m, context.currentClassDef(), context)) { // "x10.lang._.Console" result in a null context. See AbstractAccessControlResolver.find(matcher, null);
throw new SemanticException("Cannot access member type \"" + m + "\".");
}
return CollectionUtil.<Type>list(m);
}
// If we struck out, try the super types.
// Collect all members of the super types.
// Use a Set to eliminate duplicates.
Set<Type> acceptable = CollectionFactory.<Type>newHashSet();
if (type.superClass() != null) {
Type sup = type.superClass();
if (sup instanceof ClassType && matcher.visit(sup)) {
Resolver r = ts.classContextResolver((ClassType) sup, context);
try {
List<Type> n = r.find(matcher);
acceptable.addAll(n);
}
catch (SemanticException e) {
}
}
}
for (Iterator<Type> i = type.interfaces().iterator(); i.hasNext(); ) {
Type sup = (Type) i.next();
if (sup instanceof ClassType && matcher.visit(sup)) {
Resolver r = ts.classContextResolver((ClassType) sup, context);
try {
List<Type> n = r.find(matcher);
acceptable.addAll(n);
}
catch (SemanticException e) {
}
}
}
if (acceptable.size() == 0) {
throw new NoClassException(name.toString(), type);
}
else if (acceptable.size() > 1) {
Set<Type> containers = CollectionFactory.newHashSet(acceptable.size());
for (Type n : acceptable) {
if (n instanceof MemberInstance<?>) {
MemberInstance<?> mi = (MemberInstance<?>) n;
containers.add(mi.container());
}
}
if (containers.size() == 2) {
Iterator<Type> i = containers.iterator();
Type t1 = (Type) i.next();
Type t2 = (Type) i.next();
throw new SemanticException("Member \"" + name +
"\" of " + type + " is ambiguous; it is defined in both " +
t1 + " and " + t2 + ".");
}
else if (containers.size() == 0) {
throw new SemanticException("Member \"" + name +
"\" of " + type + " is ambiguous.");
}
else {
throw new SemanticException("Member \"" + name +
"\" of " + type + " is ambiguous; it is defined in " +
CollectionUtil.listToString(new ArrayList<Type>(containers)) + ".");
}
}
assert acceptable.size() == 1;
List<Type> t = new ArrayList<Type>(acceptable);
if (reporter.should_report(TOPICS, 2))
reporter.report(2, "Found member type " + t);
return t;
}
protected boolean canAccess(Type n, ClassDef accessor, Context context) {
if (n instanceof ConstrainedType)
n = Types.baseType(n);
if (n instanceof MemberInstance<?>) {
return accessor == null || ts.isAccessible((MemberInstance<?>) n, context);
}
return true;
}
/**
* The class in whose context we look.
*/
public Type classType() {
return type;
}
private static final Collection<String> TOPICS =
CollectionUtil.list(Reporter.types, Reporter.resolver);
}