blob: c9f05fd355834d28e39ae13f610d4b44b7f82f59 [file] [log] [blame]
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.google.dart.compiler.type;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Lists;
import com.google.common.collect.MapMaker;
import com.google.dart.compiler.resolver.ClassElement;
import com.google.dart.compiler.resolver.Element;
import com.google.dart.compiler.resolver.ElementKind;
import com.google.dart.compiler.resolver.Elements;
import com.google.dart.compiler.resolver.FieldElement;
import com.google.dart.compiler.resolver.MethodElement;
import com.google.dart.compiler.util.apache.StringUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* An interface type.
*/
@VisibleForTesting
public class InterfaceTypeImplementation extends AbstractType implements InterfaceType {
private final ClassElement element;
private final List<Type> arguments;
private final Map<ClassElement, Object> subClasses = new MapMaker().weakKeys().makeMap();
InterfaceTypeImplementation(ClassElement element, List<Type> arguments) {
this.element = element;
this.arguments = arguments;
}
@Override
public ClassElement getElement() {
return element;
}
@Override
public List<Type> getArguments() {
return arguments;
}
@Override
public String toString() {
if (getArguments().isEmpty()) {
return getElement().getName();
} else {
StringBuilder sb = new StringBuilder();
sb.append(getElement().getName());
Types.printTypesOn(sb, getArguments(), "<", ">");
return sb.toString();
}
}
@Override
public boolean hasDynamicTypeArgs() {
for (Type t : getArguments()) {
if (t.getKind() == TypeKind.DYNAMIC) {
return true;
}
}
return false;
}
@Override
public boolean isRaw() {
return getArguments().size() != getElement().getTypeParameters().size();
}
@Override
public InterfaceType subst(List<Type> arguments, List<Type> parameters) {
if (arguments.isEmpty() && parameters.isEmpty()) {
return this;
}
List<Type> substitutedArguments = Types.subst(getArguments(), arguments, parameters);
return new InterfaceTypeImplementation(getElement(), substitutedArguments);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof InterfaceType) {
InterfaceType other = (InterfaceType) obj;
return getElement().equals(other.getElement()) && getArguments().equals(other.getArguments());
}
return false;
}
@Override
public int hashCode() {
int hashCode = 31;
hashCode += getElement().hashCode();
hashCode += 31 * hashCode + getArguments().hashCode();
return hashCode;
}
@Override
public InterfaceType asRawType() {
return new InterfaceTypeImplementation(getElement(), Arrays.<Type>asList());
}
@Override
public TypeKind getKind() {
return TypeKind.INTERFACE;
}
@Override
public Member lookupMember(String name) {
Member member = lookupMember0(this, name);
if (member != null) {
return member;
}
InterfaceType supertype = getSupertype();
if (supertype != null) {
member = supertype.lookupMember(name);
if (member != null) {
return member;
}
}
for (InterfaceType intrface : getInterfaces()) {
member = intrface.lookupMember(name);
if (member != null) {
return member;
}
}
for (InterfaceType mixin : getMixins()) {
member = lookupMember0(mixin, name);
if (member != null) {
return member;
}
}
return null;
}
private static Member lookupMember0(InterfaceType type, String name) {
Element element = type.getElement().lookupLocalElement(name);
if (element != null) {
return new MemberImplementation(type, element);
}
return null;
}
private InterfaceType getSupertype() {
InterfaceType supertype = getElement().getSupertype();
if (supertype == null) {
return null;
} else {
return supertype.subst(getArguments(), getElement().getTypeParameters());
}
}
private List<InterfaceType> getInterfaces() {
List<InterfaceType> result = new ArrayList<InterfaceType>();
List<Type> typeArguments = getArguments();
List<Type> typeParameters = getElement().getTypeParameters();
for (InterfaceType type : getElement().getInterfaces()) {
result.add(type.subst(typeArguments, typeParameters));
}
return result;
}
private List<InterfaceType> getMixins() {
List<InterfaceType> result = new ArrayList<InterfaceType>();
List<Type> typeArguments = getArguments();
List<Type> typeParameters = getElement().getTypeParameters();
for (InterfaceType type : getElement().getMixins()) {
result.add(type.subst(typeArguments, typeParameters));
}
return result;
}
private static class MemberImplementation implements Member {
private final InterfaceType holder;
private final Element member;
MemberImplementation(InterfaceType holder, Element member) {
this.holder = holder;
this.member = member;
}
@Override
public InterfaceType getHolder() {
return holder;
}
@Override
public Element getElement() {
return member;
}
@Override
public Type getType() {
List<Type> typeArguments = getHolder().getArguments();
List<Type> typeParameters = getHolder().getElement().getTypeParameters();
return getElement().getType().subst(typeArguments, typeParameters);
}
@Override
public Type getSetterType() {
Element element = getElement();
if (!ElementKind.of(element).equals(ElementKind.FIELD)) {
return getType();
}
FieldElement field = (FieldElement) element;
MethodElement setter = field.getSetter();
if (setter == null) {
setter = Elements.lookupFieldElementSetter(holder.getElement(), member.getName());
if (setter == null) {
setter = Elements.lookupFieldElementSetter(holder.getElement(), "setter " + member.getName());
}
if (setter == null) {
return null;
}
}
if (setter.getParameters().size() == 0) {
return null;
}
Type setterType = setter.getParameters().get(0).getType();
List<Type> typeArguments = getHolder().getArguments();
List<Type> typeParameters = getHolder().getElement().getTypeParameters();
return setterType.subst(typeArguments, typeParameters);
}
@Override
public Type getGetterType() {
Element element = getElement();
if (!ElementKind.of(element).equals(ElementKind.FIELD)) {
return getType();
}
FieldElement field = (FieldElement) element;
MethodElement getter = field.getGetter();
if (getter == null) {
String name = member.getName();
name = StringUtils.stripStart(name, "setter ");
getter = Elements.lookupFieldElementGetter(holder.getElement(), name);
if (getter == null) {
return null;
}
}
Type getterType = getter.getReturnType();
List<Type> typeArguments = getHolder().getArguments();
List<Type> typeParameters = getHolder().getElement().getTypeParameters();
return getterType.subst(typeArguments, typeParameters);
}
}
@Override
public void registerSubClass(ClassElement subClass) {
subClasses.put(subClass, this);
}
@Override
public void unregisterSubClass(ClassElement subClass) {
subClasses.remove(subClass);
}
@Override
public List<Member> lookupSubTypeMembers(String name) {
List<Member> members = Lists.newArrayList();
fillSubTypeMember(members, name);
return members;
}
private void fillSubTypeMember(List<Member> members, String name) {
{
Member member = lookupMember(name);
if (member != null) {
members.add(member);
}
}
for (ClassElement subClass : subClasses.keySet()) {
InterfaceType type = subClass.getType();
if (type instanceof InterfaceTypeImplementation) {
((InterfaceTypeImplementation)type).fillSubTypeMember(members, name);
}
}
}
}