Package com.google.dart.engine.internal.element

Source Code of com.google.dart.engine.internal.element.ClassElementImpl

/*
* 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;
  }

}
TOP

Related Classes of com.google.dart.engine.internal.element.ClassElementImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.