com.google.dart.engine.internal.element.ClassElementImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.google.dart.engine.internal.element.ClassElementImpl.java

Source

/*
 * Copyright (c) 2012, the Dart project authors.
 * 
 * Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 * 
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package com.google.dart.engine.internal.element;

import com.google.dart.engine.ast.ClassDeclaration;
import com.google.dart.engine.ast.Identifier;
import com.google.dart.engine.context.AnalysisException;
import com.google.dart.engine.element.ClassElement;
import com.google.dart.engine.element.ConstructorElement;
import com.google.dart.engine.element.Element;
import com.google.dart.engine.element.ElementAnnotation;
import com.google.dart.engine.element.ElementKind;
import com.google.dart.engine.element.ElementVisitor;
import com.google.dart.engine.element.FieldElement;
import com.google.dart.engine.element.LibraryElement;
import com.google.dart.engine.element.MethodElement;
import com.google.dart.engine.element.PropertyAccessorElement;
import com.google.dart.engine.element.ToolkitObjectElement;
import com.google.dart.engine.element.TypeParameterElement;
import com.google.dart.engine.type.InterfaceType;
import com.google.dart.engine.utilities.general.StringUtilities;
import com.google.dart.engine.utilities.translation.DartName;

import org.apache.commons.lang3.ArrayUtils;

import java.util.ArrayList;
import java.util.HashSet;

/**
 * Instances of the class {@code ClassElementImpl} implement a {@code ClassElement}.
 * 
 * @coverage dart.engine.element
 */
public class ClassElementImpl extends ElementImpl implements ClassElement {
    /**
     * An array containing all of the accessors (getters and setters) contained in this class.
     */
    private PropertyAccessorElement[] accessors = PropertyAccessorElementImpl.EMPTY_ARRAY;

    /**
     * An array containing all of the constructors contained in this class.
     */
    private ConstructorElement[] constructors = ConstructorElementImpl.EMPTY_ARRAY;

    /**
     * An array containing all of the fields contained in this class.
     */
    private FieldElement[] fields = FieldElementImpl.EMPTY_ARRAY;

    /**
     * An array containing all of the mixins that are applied to the class being extended in order to
     * derive the superclass of this class.
     */
    private InterfaceType[] mixins = InterfaceType.EMPTY_ARRAY;

    /**
     * An array containing all of the interfaces that are implemented by this class.
     */
    private InterfaceType[] interfaces = InterfaceType.EMPTY_ARRAY;

    /**
     * An array containing all of the methods contained in this class.
     */
    private MethodElement[] methods = MethodElementImpl.EMPTY_ARRAY;

    /**
     * The superclass of the class, or {@code null} if the class does not have an explicit superclass.
     */
    private InterfaceType supertype;

    /**
     * An array containing all of the toolkit objects attached to this class.
     */
    private ToolkitObjectElement[] toolkitObjects = ToolkitObjectElement.EMPTY_ARRAY;

    /**
     * The type defined by the class.
     */
    private InterfaceType type;

    /**
     * An array containing all of the type parameters defined for this class.
     */
    private TypeParameterElement[] typeParameters = TypeParameterElementImpl.EMPTY_ARRAY;

    /**
     * An empty array of class elements.
     */
    public static final ClassElement[] EMPTY_ARRAY = new ClassElement[0];

    /**
     * Initialize a newly created class element to have the given name.
     * 
     * @param name the name of this element
     */
    @DartName("forNode")
    public ClassElementImpl(Identifier name) {
        super(name);
    }

    /**
     * Initialize a newly created class element to have the given name.
     * 
     * @param name the name of this element
     * @param nameOffset the offset of the name of this element in the file that contains the
     *          declaration of this element
     */
    public ClassElementImpl(String name, int nameOffset) {
        super(name, nameOffset);
    }

    @Override
    public <R> R accept(ElementVisitor<R> visitor) {
        return visitor.visitClassElement(this);
    }

    /**
     * Set the toolkit specific information objects attached to this class.
     * 
     * @param toolkitObjects the toolkit objects attached to this class
     */
    public void addToolkitObjects(ToolkitObjectElement toolkitObject) {
        ((ToolkitObjectElementImpl) toolkitObject).setEnclosingElement(this);
        toolkitObjects = ArrayUtils.add(toolkitObjects, toolkitObject);
    }

    @Override
    public PropertyAccessorElement[] getAccessors() {
        return accessors;
    }

    @Override
    public InterfaceType[] getAllSupertypes() {
        ArrayList<InterfaceType> list = new ArrayList<InterfaceType>();
        collectAllSupertypes(list);
        return list.toArray(new InterfaceType[list.size()]);
    }

    @Override
    public ElementImpl getChild(String identifier) {
        //
        // The casts in this method are safe because the set methods would have thrown a CCE if any of
        // the elements in the arrays were not of the expected types.
        //
        for (PropertyAccessorElement accessor : accessors) {
            if (((PropertyAccessorElementImpl) accessor).getIdentifier().equals(identifier)) {
                return (PropertyAccessorElementImpl) accessor;
            }
        }
        for (ConstructorElement constructor : constructors) {
            if (((ConstructorElementImpl) constructor).getIdentifier().equals(identifier)) {
                return (ConstructorElementImpl) constructor;
            }
        }
        for (FieldElement field : fields) {
            if (((FieldElementImpl) field).getIdentifier().equals(identifier)) {
                return (FieldElementImpl) field;
            }
        }
        for (MethodElement method : methods) {
            if (((MethodElementImpl) method).getIdentifier().equals(identifier)) {
                return (MethodElementImpl) method;
            }
        }
        for (TypeParameterElement typeParameter : typeParameters) {
            if (((TypeParameterElementImpl) typeParameter).getIdentifier().equals(identifier)) {
                return (TypeParameterElementImpl) typeParameter;
            }
        }
        return null;
    }

    @Override
    public ConstructorElement[] getConstructors() {
        return constructors;
    }

    @Override
    public FieldElement getField(String name) {
        for (FieldElement fieldElement : fields) {
            if (name.equals(fieldElement.getName())) {
                return fieldElement;
            }
        }
        return null;
    }

    @Override
    public FieldElement[] getFields() {
        return fields;
    }

    @Override
    public PropertyAccessorElement getGetter(String getterName) {
        for (PropertyAccessorElement accessor : accessors) {
            if (accessor.isGetter() && accessor.getName().equals(getterName)) {
                return accessor;
            }
        }
        return null;
    }

    @Override
    public InterfaceType[] getInterfaces() {
        return interfaces;
    }

    @Override
    public ElementKind getKind() {
        return ElementKind.CLASS;
    }

    @Override
    public MethodElement getMethod(String methodName) {
        for (MethodElement method : methods) {
            if (method.getName().equals(methodName)) {
                return method;
            }
        }
        return null;
    }

    @Override
    public MethodElement[] getMethods() {
        return methods;
    }

    @Override
    public InterfaceType[] getMixins() {
        return mixins;
    }

    @Override
    public ConstructorElement getNamedConstructor(String name) {
        for (ConstructorElement element : getConstructors()) {
            String elementName = element.getName();
            if (elementName != null && elementName.equals(name)) {
                return element;
            }
        }
        return null;
    }

    @Override
    public ClassDeclaration getNode() throws AnalysisException {
        return getNodeMatching(ClassDeclaration.class);
    }

    @Override
    public PropertyAccessorElement getSetter(String setterName) {
        // TODO (jwren) revisit- should we append '=' here or require clients to include it?
        // Do we need the check for isSetter below?
        if (!StringUtilities.endsWithChar(setterName, '=')) {
            setterName += '=';
        }
        for (PropertyAccessorElement accessor : accessors) {
            if (accessor.isSetter() && accessor.getName().equals(setterName)) {
                return accessor;
            }
        }
        return null;
    }

    @Override
    public InterfaceType getSupertype() {
        return supertype;
    }

    @Override
    public ToolkitObjectElement[] getToolkitObjects() {
        return toolkitObjects;
    }

    @Override
    public InterfaceType getType() {
        return type;
    }

    @Override
    public TypeParameterElement[] getTypeParameters() {
        return typeParameters;
    }

    @Override
    public ConstructorElement getUnnamedConstructor() {
        for (ConstructorElement element : getConstructors()) {
            String name = element.getDisplayName();
            if (name == null || name.isEmpty()) {
                return element;
            }
        }
        return null;
    }

    @Override
    public boolean hasNonFinalField() {
        ArrayList<ClassElement> classesToVisit = new ArrayList<ClassElement>();
        HashSet<ClassElement> visitedClasses = new HashSet<ClassElement>();
        classesToVisit.add(this);
        while (!classesToVisit.isEmpty()) {
            ClassElement currentElement = classesToVisit.remove(0);
            if (visitedClasses.add(currentElement)) {
                // check fields
                for (FieldElement field : currentElement.getFields()) {
                    if (!field.isFinal() && !field.isConst() && !field.isStatic() && !field.isSynthetic()) {
                        return true;
                    }
                }
                // check mixins
                for (InterfaceType mixinType : currentElement.getMixins()) {
                    ClassElement mixinElement = mixinType.getElement();
                    classesToVisit.add(mixinElement);
                }
                // check super
                InterfaceType supertype = currentElement.getSupertype();
                if (supertype != null) {
                    ClassElement superElement = supertype.getElement();
                    if (superElement != null) {
                        classesToVisit.add(superElement);
                    }
                }
            }
        }
        // not found
        return false;
    }

    @Override
    public boolean hasReferenceToSuper() {
        return hasModifier(Modifier.REFERENCES_SUPER);
    }

    @Override
    public boolean hasStaticMember() {
        for (MethodElement method : methods) {
            if (method.isStatic()) {
                return true;
            }
        }
        for (PropertyAccessorElement accessor : accessors) {
            if (accessor.isStatic()) {
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean isAbstract() {
        return hasModifier(Modifier.ABSTRACT);
    }

    @Override
    public boolean isEnum() {
        return hasModifier(Modifier.ENUM);
    }

    @Override
    public boolean isOrInheritsProxy() {
        return safeIsOrInheritsProxy(this, new HashSet<ClassElement>());
    }

    @Override
    public boolean isProxy() {
        for (ElementAnnotation annotation : getMetadata()) {
            if (annotation.isProxy()) {
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean isTypedef() {
        return hasModifier(Modifier.TYPEDEF);
    }

    @Override
    public boolean isValidMixin() {
        return hasModifier(Modifier.MIXIN);
    }

    @Override
    public MethodElement lookUpConcreteMethod(String methodName, LibraryElement library) {
        return internalLookUpConcreteMethod(methodName, library, true);
    }

    @Override
    public PropertyAccessorElement lookUpGetter(String getterName, LibraryElement library) {
        return internalLookUpGetter(getterName, library, true);
    }

    @Override
    public PropertyAccessorElement lookUpInheritedConcreteGetter(String getterName, LibraryElement library) {
        return internalLookUpConcreteGetter(getterName, library, false);
    }

    @Override
    public MethodElement lookUpInheritedConcreteMethod(String methodName, LibraryElement library) {
        return internalLookUpConcreteMethod(methodName, library, false);
    }

    @Override
    public PropertyAccessorElement lookUpInheritedConcreteSetter(String setterName, LibraryElement library) {
        return internalLookUpConcreteSetter(setterName, library, false);
    }

    @Override
    public MethodElement lookUpInheritedMethod(String methodName, LibraryElement library) {
        return internalLookUpMethod(methodName, library, false);
    }

    @Override
    public MethodElement lookUpMethod(String methodName, LibraryElement library) {
        return internalLookUpMethod(methodName, library, true);
    }

    @Override
    public PropertyAccessorElement lookUpSetter(String setterName, LibraryElement library) {
        return internalLookUpSetter(setterName, library, true);
    }

    /**
     * Set whether this class is abstract to correspond to the given value.
     * 
     * @param isAbstract {@code true} if the class is abstract
     */
    public void setAbstract(boolean isAbstract) {
        setModifier(Modifier.ABSTRACT, isAbstract);
    }

    /**
     * Set the accessors contained in this class to the given accessors.
     * 
     * @param accessors the accessors contained in this class
     */
    public void setAccessors(PropertyAccessorElement[] accessors) {
        for (PropertyAccessorElement accessor : accessors) {
            ((PropertyAccessorElementImpl) accessor).setEnclosingElement(this);
        }
        this.accessors = accessors;
    }

    /**
     * Set the constructors contained in this class to the given constructors.
     * 
     * @param constructors the constructors contained in this class
     */
    public void setConstructors(ConstructorElement[] constructors) {
        for (ConstructorElement constructor : constructors) {
            ((ConstructorElementImpl) constructor).setEnclosingElement(this);
        }
        this.constructors = constructors;
    }

    /**
     * Set whether this class is defined by an enum declaration to correspond to the given value.
     * 
     * @param isEnum {@code true} if the class is defined by an enum declaration
     */
    public void setEnum(boolean isEnum) {
        setModifier(Modifier.ENUM, isEnum);
    }

    /**
     * Set the fields contained in this class to the given fields.
     * 
     * @param fields the fields contained in this class
     */
    public void setFields(FieldElement[] fields) {
        for (FieldElement field : fields) {
            ((FieldElementImpl) field).setEnclosingElement(this);
        }
        this.fields = fields;
    }

    /**
     * Set whether this class references 'super' to the given value.
     * 
     * @param isReferencedSuper {@code true} references 'super'
     */
    public void setHasReferenceToSuper(boolean isReferencedSuper) {
        setModifier(Modifier.REFERENCES_SUPER, isReferencedSuper);
    }

    /**
     * Set the interfaces that are implemented by this class to the given types.
     * 
     * @param the interfaces that are implemented by this class
     */
    public void setInterfaces(InterfaceType[] interfaces) {
        this.interfaces = interfaces;
    }

    /**
     * Set the methods contained in this class to the given methods.
     * 
     * @param methods the methods contained in this class
     */
    public void setMethods(MethodElement[] methods) {
        for (MethodElement method : methods) {
            ((MethodElementImpl) method).setEnclosingElement(this);
        }
        this.methods = methods;
    }

    /**
     * Set the mixins that are applied to the class being extended in order to derive the superclass
     * of this class to the given types.
     * 
     * @param mixins the mixins that are applied to derive the superclass of this class
     */
    public void setMixins(InterfaceType[] mixins) {
        this.mixins = mixins;
    }

    /**
     * Set the superclass of the class to the given type.
     * 
     * @param supertype the superclass of the class
     */
    public void setSupertype(InterfaceType supertype) {
        this.supertype = supertype;
    }

    /**
     * Set the type defined by the class to the given type.
     * 
     * @param type the type defined by the class
     */
    public void setType(InterfaceType type) {
        this.type = type;
    }

    /**
     * Set whether this class is defined by a typedef construct to correspond to the given value.
     * 
     * @param isTypedef {@code true} if the class is defined by a typedef construct
     */
    public void setTypedef(boolean isTypedef) {
        setModifier(Modifier.TYPEDEF, isTypedef);
    }

    /**
     * Set the type parameters defined for this class to the given type parameters.
     * 
     * @param typeParameters the type parameters defined for this class
     */
    public void setTypeParameters(TypeParameterElement[] typeParameters) {
        for (TypeParameterElement typeParameter : typeParameters) {
            ((TypeParameterElementImpl) typeParameter).setEnclosingElement(this);
        }
        this.typeParameters = typeParameters;
    }

    /**
     * Set whether this class is a valid mixin to correspond to the given value.
     * 
     * @param isValidMixin {@code true} if this class can be used as a mixin
     */
    public void setValidMixin(boolean isValidMixin) {
        setModifier(Modifier.MIXIN, isValidMixin);
    }

    @Override
    public void visitChildren(ElementVisitor<?> visitor) {
        super.visitChildren(visitor);
        safelyVisitChildren(accessors, visitor);
        safelyVisitChildren(constructors, visitor);
        safelyVisitChildren(fields, visitor);
        safelyVisitChildren(methods, visitor);
        safelyVisitChildren(toolkitObjects, visitor);
        safelyVisitChildren(typeParameters, visitor);
    }

    @Override
    protected void appendTo(StringBuilder builder) {
        String name = getDisplayName();
        if (name == null) {
            builder.append("{unnamed class}");
        } else {
            builder.append(name);
        }
        int variableCount = typeParameters.length;
        if (variableCount > 0) {
            builder.append("<");
            for (int i = 0; i < variableCount; i++) {
                if (i > 0) {
                    builder.append(", ");
                }
                ((TypeParameterElementImpl) typeParameters[i]).appendTo(builder);
            }
            builder.append(">");
        }
    }

    private void collectAllSupertypes(ArrayList<InterfaceType> supertypes) {
        ArrayList<InterfaceType> typesToVisit = new ArrayList<InterfaceType>();
        ArrayList<ClassElement> visitedClasses = new ArrayList<ClassElement>();
        typesToVisit.add(this.getType());
        while (!typesToVisit.isEmpty()) {
            InterfaceType currentType = typesToVisit.remove(0);
            ClassElement currentElement = currentType.getElement();
            if (!visitedClasses.contains(currentElement)) {
                visitedClasses.add(currentElement);
                if (currentType != this.getType()) {
                    supertypes.add(currentType);
                }
                InterfaceType supertype = currentType.getSuperclass();
                if (supertype != null) {
                    typesToVisit.add(supertype);
                }
                for (InterfaceType type : currentElement.getInterfaces()) {
                    typesToVisit.add(type);
                }
                for (InterfaceType type : currentElement.getMixins()) {
                    ClassElement element = type.getElement();
                    if (!visitedClasses.contains(element)) {
                        supertypes.add(type);
                    }
                }
            }
        }
    }

    private PropertyAccessorElement internalLookUpConcreteGetter(String getterName, LibraryElement library,
            boolean includeThisClass) {
        PropertyAccessorElement getter = internalLookUpGetter(getterName, library, includeThisClass);
        while (getter != null && getter.isAbstract()) {
            Element definingClass = getter.getEnclosingElement();
            if (!(definingClass instanceof ClassElementImpl)) {
                return null;
            }
            getter = ((ClassElementImpl) definingClass).internalLookUpGetter(getterName, library, false);
        }
        return getter;
    }

    private MethodElement internalLookUpConcreteMethod(String methodName, LibraryElement library,
            boolean includeThisClass) {
        MethodElement method = internalLookUpMethod(methodName, library, includeThisClass);
        while (method != null && method.isAbstract()) {
            ClassElement definingClass = method.getEnclosingElement();
            if (definingClass == null) {
                return null;
            }
            method = definingClass.lookUpInheritedMethod(methodName, library);
        }
        return method;
    }

    private PropertyAccessorElement internalLookUpConcreteSetter(String setterName, LibraryElement library,
            boolean includeThisClass) {
        PropertyAccessorElement setter = internalLookUpSetter(setterName, library, includeThisClass);
        while (setter != null && setter.isAbstract()) {
            Element definingClass = setter.getEnclosingElement();
            if (!(definingClass instanceof ClassElementImpl)) {
                return null;
            }
            setter = ((ClassElementImpl) definingClass).internalLookUpSetter(setterName, library, false);
        }
        return setter;
    }

    private PropertyAccessorElement internalLookUpGetter(String getterName, LibraryElement library,
            boolean includeThisClass) {
        HashSet<ClassElement> visitedClasses = new HashSet<ClassElement>();
        ClassElement currentElement = this;
        if (includeThisClass) {
            PropertyAccessorElement element = currentElement.getGetter(getterName);
            if (element != null && element.isAccessibleIn(library)) {
                return element;
            }
        }
        while (currentElement != null && visitedClasses.add(currentElement)) {
            for (InterfaceType mixin : currentElement.getMixins()) {
                ClassElement mixinElement = mixin.getElement();
                if (mixinElement != null) {
                    PropertyAccessorElement element = mixinElement.getGetter(getterName);
                    if (element != null && element.isAccessibleIn(library)) {
                        return element;
                    }
                }
            }
            InterfaceType supertype = currentElement.getSupertype();
            if (supertype == null) {
                return null;
            }
            currentElement = supertype.getElement();
            PropertyAccessorElement element = currentElement.getGetter(getterName);
            if (element != null && element.isAccessibleIn(library)) {
                return element;
            }
        }
        return null;
    }

    private MethodElement internalLookUpMethod(String methodName, LibraryElement library,
            boolean includeThisClass) {
        HashSet<ClassElement> visitedClasses = new HashSet<ClassElement>();
        ClassElement currentElement = this;
        if (includeThisClass) {
            MethodElement element = currentElement.getMethod(methodName);
            if (element != null && element.isAccessibleIn(library)) {
                return element;
            }
        }
        while (currentElement != null && visitedClasses.add(currentElement)) {
            for (InterfaceType mixin : currentElement.getMixins()) {
                ClassElement mixinElement = mixin.getElement();
                if (mixinElement != null) {
                    MethodElement element = mixinElement.getMethod(methodName);
                    if (element != null && element.isAccessibleIn(library)) {
                        return element;
                    }
                }
            }
            InterfaceType supertype = currentElement.getSupertype();
            if (supertype == null) {
                return null;
            }
            currentElement = supertype.getElement();
            MethodElement element = currentElement.getMethod(methodName);
            if (element != null && element.isAccessibleIn(library)) {
                return element;
            }
        }
        return null;
    }

    private PropertyAccessorElement internalLookUpSetter(String setterName, LibraryElement library,
            boolean includeThisClass) {
        HashSet<ClassElement> visitedClasses = new HashSet<ClassElement>();
        ClassElement currentElement = this;
        if (includeThisClass) {
            PropertyAccessorElement element = currentElement.getSetter(setterName);
            if (element != null && element.isAccessibleIn(library)) {
                return element;
            }
        }
        while (currentElement != null && visitedClasses.add(currentElement)) {
            for (InterfaceType mixin : currentElement.getMixins()) {
                ClassElement mixinElement = mixin.getElement();
                if (mixinElement != null) {
                    PropertyAccessorElement element = mixinElement.getSetter(setterName);
                    if (element != null && element.isAccessibleIn(library)) {
                        return element;
                    }
                }
            }
            InterfaceType supertype = currentElement.getSupertype();
            if (supertype == null) {
                return null;
            }
            currentElement = supertype.getElement();
            PropertyAccessorElement element = currentElement.getSetter(setterName);
            if (element != null && element.isAccessibleIn(library)) {
                return element;
            }
        }
        return null;
    }

    private boolean safeIsOrInheritsProxy(ClassElement classElt, HashSet<ClassElement> visitedClassElts) {
        if (visitedClassElts.contains(classElt)) {
            return false;
        }
        visitedClassElts.add(classElt);
        if (classElt.isProxy()) {
            return true;
        } else if (classElt.getSupertype() != null
                && safeIsOrInheritsProxy(classElt.getSupertype().getElement(), visitedClassElts)) {
            return true;
        }
        InterfaceType[] supertypes = classElt.getInterfaces();
        for (int i = 0; i < supertypes.length; i++) {
            if (safeIsOrInheritsProxy(supertypes[i].getElement(), visitedClassElts)) {
                return true;
            }
        }
        supertypes = classElt.getMixins();
        for (int i = 0; i < supertypes.length; i++) {
            if (safeIsOrInheritsProxy(supertypes[i].getElement(), visitedClassElts)) {
                return true;
            }
        }
        return false;
    }

}