private Binding findImport(char[][] compoundName, int length) {
    recordQualifiedReference(compoundName);

    Binding binding = this.environment.getTopLevelPackage(compoundName[0]);
    int i = 1;
    foundNothingOrType:
    if (binding != null) {
      PackageBinding packageBinding = (PackageBinding) binding;
      while (i < length) {
        binding = packageBinding.getTypeOrPackage(compoundName[i++]);
        if (binding == null || !binding.isValidBinding()) {
          binding = null;
          break foundNothingOrType;
        }
        if (!(binding instanceof PackageBinding)) break foundNothingOrType;

        packageBinding = (PackageBinding) binding;
      }
      return packageBinding;
    }

    ReferenceBinding type;
    if (binding == null) {
      if (compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4)
        return new ProblemReferenceBinding(
            CharOperation.subarray(compoundName, 0, i), null, ProblemReasons.NotFound);
      type =
          findType(
              compoundName[0], this.environment.defaultPackage, this.environment.defaultPackage);
      if (type == null || !type.isValidBinding())
        return new ProblemReferenceBinding(
            CharOperation.subarray(compoundName, 0, i), null, ProblemReasons.NotFound);
      i = 1; // reset to look for member types inside the default package type
    } else {
      type = (ReferenceBinding) binding;
    }

    while (i < length) {
      type =
          (ReferenceBinding)
              this.environment.convertToRawType(
                  type, false /*do not force conversion of enclosing types*/); // type imports are
      // necessarily raw for all
      // except last
      if (!type.canBeSeenBy(this.fPackage))
        return new ProblemReferenceBinding(
            CharOperation.subarray(compoundName, 0, i), type, ProblemReasons.NotVisible);

      char[] name = compoundName[i++];
      // does not look for inherited member types on purpose, only immediate members
      type = type.getMemberType(name);
      if (type == null)
        return new ProblemReferenceBinding(
            CharOperation.subarray(compoundName, 0, i), null, ProblemReasons.NotFound);
    }
    if (!type.canBeSeenBy(this.fPackage))
      return new ProblemReferenceBinding(compoundName, type, ProblemReasons.NotVisible);
    return type;
  }
 /**
  * Resolve the supertypes for the supplied source type. Inform the requestor of the resolved
  * supertypes using: connect(ISourceType suppliedType, IGenericType superclass, IGenericType[]
  * superinterfaces)
  *
  * @param suppliedType
  */
 public void resolve(IGenericType suppliedType) {
   try {
     if (suppliedType.isBinaryType()) {
       BinaryTypeBinding binaryTypeBinding =
           this.lookupEnvironment.cacheBinaryType(
               (IBinaryType) suppliedType,
               false /*don't need field and method (bug 125067)*/,
               null /*no access restriction*/);
       remember(suppliedType, binaryTypeBinding);
       // We still need to add superclasses and superinterfaces bindings (see
       // https://bugs.eclipse.org/bugs/show_bug.cgi?id=53095)
       int startIndex = this.typeIndex;
       for (int i = startIndex; i <= this.typeIndex; i++) {
         IGenericType igType = this.typeModels[i];
         if (igType != null && igType.isBinaryType()) {
           CompilationUnitDeclaration previousUnitBeingCompleted =
               this.lookupEnvironment.unitBeingCompleted;
           // fault in its hierarchy...
           try {
             // ensure that unitBeingCompleted is set so that we don't get an AbortCompilation for
             // a missing type
             // (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=213249 )
             if (previousUnitBeingCompleted == null) {
               this.lookupEnvironment.unitBeingCompleted = FakeUnit;
             }
             ReferenceBinding typeBinding = this.typeBindings[i];
             typeBinding.superclass();
             typeBinding.superInterfaces();
           } catch (AbortCompilation e) {
             // classpath problem for this type: ignore
           } finally {
             this.lookupEnvironment.unitBeingCompleted = previousUnitBeingCompleted;
           }
         }
       }
       this.superTypesOnly = true;
       reportHierarchy(this.builder.getType(), null, binaryTypeBinding);
     } else {
       org.aspectj.org.eclipse.jdt.core.ICompilationUnit cu =
           ((SourceTypeElementInfo) suppliedType).getHandle().getCompilationUnit();
       if (cu != null) {
         HashSet localTypes = new HashSet();
         localTypes.add(cu.getPath().toString());
         this.superTypesOnly = true;
         resolve(new Openable[] {(Openable) cu}, localTypes, null);
       }
     }
   } catch (
       AbortCompilation
           e) { // ignore this exception for now since it typically means we cannot find
                // java.lang.Object
   } finally {
     reset();
   }
 }
  /*
   * Creates the super class handle of the given type.
   * Returns null if the type has no super class.
   * Adds the simple name to the hierarchy missing types if the class is not found and returns null.
   */
  private IType findSuperClass(IGenericType type, ReferenceBinding typeBinding) {
    ReferenceBinding superBinding = typeBinding.superclass();

    if (superBinding != null) {
      superBinding = (ReferenceBinding) superBinding.erasure();
      if (typeBinding.isHierarchyInconsistent()) {
        if (superBinding.problemId() == ProblemReasons.NotFound) {
          this.hasMissingSuperClass = true;
          this.builder.hierarchy.missingTypes.add(
              new String(superBinding.sourceName)); // note: this could be Map$Entry
          return null;
        } else if ((superBinding.id == TypeIds.T_JavaLangObject)) {
          char[] superclassName;
          char separator;
          if (type instanceof IBinaryType) {
            superclassName = ((IBinaryType) type).getSuperclassName();
            separator = '/';
          } else if (type instanceof ISourceType) {
            superclassName = ((ISourceType) type).getSuperclassName();
            separator = '.';
          } else if (type instanceof HierarchyType) {
            superclassName = ((HierarchyType) type).superclassName;
            separator = '.';
          } else {
            return null;
          }

          if (superclassName
              != null) { // check whether subclass of Object due to broken hierarchy (as opposed to
                         // explicitly extending it)
            int lastSeparator = CharOperation.lastIndexOf(separator, superclassName);
            char[] simpleName =
                lastSeparator == -1
                    ? superclassName
                    : CharOperation.subarray(
                        superclassName, lastSeparator + 1, superclassName.length);
            if (!CharOperation.equals(simpleName, TypeConstants.OBJECT)) {
              this.hasMissingSuperClass = true;
              this.builder.hierarchy.missingTypes.add(new String(simpleName));
              return null;
            }
          }
        }
      }
      for (int t = this.typeIndex; t >= 0; t--) {
        if (TypeBinding.equalsEquals(this.typeBindings[t], superBinding)) {
          return this.builder.getHandle(this.typeModels[t], superBinding);
        }
      }
    }
    return null;
  }
  // helper method for findSingleStaticImport()
  private MethodBinding findStaticMethod(ReferenceBinding currentType, char[] selector) {
    if (!currentType.canBeSeenBy(this)) return null;

    do {
      currentType.initializeForStaticImports();
      MethodBinding[] methods = currentType.getMethods(selector);
      if (methods != Binding.NO_METHODS) {
        for (int i = methods.length; --i >= 0; ) {
          MethodBinding method = methods[i];
          if (method.isStatic() && method.canBeSeenBy(this.fPackage)) return method;
        }
      }
    } while ((currentType = currentType.superclass()) != null);
    return null;
  }
 private boolean subTypeOfType(ReferenceBinding subType, ReferenceBinding typeBinding) {
   if (typeBinding == null || subType == null) return false;
   if (TypeBinding.equalsEquals(subType, typeBinding)) return true;
   ReferenceBinding superclass = subType.superclass();
   if (superclass != null) superclass = (ReferenceBinding) superclass.erasure();
   //	if (superclass != null && superclass.id == TypeIds.T_JavaLangObject &&
   // subType.isHierarchyInconsistent()) return false;
   if (subTypeOfType(superclass, typeBinding)) return true;
   ReferenceBinding[] superInterfaces = subType.superInterfaces();
   if (superInterfaces != null) {
     for (int i = 0, length = superInterfaces.length; i < length; i++) {
       ReferenceBinding superInterface = (ReferenceBinding) superInterfaces[i].erasure();
       if (subTypeOfType(superInterface, typeBinding)) return true;
     }
   }
   return false;
 }
  private Binding findSingleStaticImport(char[][] compoundName, int mask) {
    Binding binding = findImport(compoundName, compoundName.length - 1);
    if (!binding.isValidBinding()) return binding;

    char[] name = compoundName[compoundName.length - 1];
    if (binding instanceof PackageBinding) {
      Binding temp = ((PackageBinding) binding).getTypeOrPackage(name);
      if (temp != null
          && temp
              instanceof
              ReferenceBinding) // must resolve to a member type or field, not a top level type
      return new ProblemReferenceBinding(
            compoundName, (ReferenceBinding) temp, ProblemReasons.InvalidTypeForStaticImport);
      return binding; // cannot be a package, error is caught in sender
    }

    // look to see if its a static field first
    ReferenceBinding type = (ReferenceBinding) binding;
    FieldBinding field = (mask & Binding.FIELD) != 0 ? findField(type, name, null, true) : null;
    if (field != null) {
      if (field.problemId() == ProblemReasons.Ambiguous
          && ((ProblemFieldBinding) field).closestMatch.isStatic())
        return field; // keep the ambiguous field instead of a possible method match
      if (field.isValidBinding() && field.isStatic() && field.canBeSeenBy(type, null, this))
        return field;
    }

    // look to see if there is a static method with the same selector
    MethodBinding method = (mask & Binding.METHOD) != 0 ? findStaticMethod(type, name) : null;
    if (method != null) return method;

    type = findMemberType(name, type);
    if (type == null || !type.isStatic()) {
      if (field != null && !field.isValidBinding() && field.problemId() != ProblemReasons.NotFound)
        return field;
      return new ProblemReferenceBinding(compoundName, type, ProblemReasons.NotFound);
    }
    if (type.isValidBinding() && !type.canBeSeenBy(this.fPackage))
      return new ProblemReferenceBinding(compoundName, type, ProblemReasons.NotVisible);
    if (type.problemId() == ProblemReasons.NotVisible) // ensure compoundName is correct
    return new ProblemReferenceBinding(
          compoundName, ((ProblemReferenceBinding) type).closestMatch, ProblemReasons.NotVisible);
    return type;
  }
  private ReferenceBinding typeToRecord(TypeBinding type) {
    if (type == null) return null;
    while (type.isArrayType()) type = ((ArrayBinding) type).leafComponentType();

    switch (type.kind()) {
      case Binding.BASE_TYPE:
      case Binding.TYPE_PARAMETER:
      case Binding.WILDCARD_TYPE:
      case Binding.INTERSECTION_TYPE:
      case Binding.INTERSECTION_TYPE18: // constituents would have been recorded.
      case Binding.POLY_TYPE: // not a real type, will mutate into one, hopefully soon.
        return null;
      case Binding.PARAMETERIZED_TYPE:
      case Binding.RAW_TYPE:
        type = type.erasure();
    }
    ReferenceBinding refType = (ReferenceBinding) type;
    if (refType.isLocalType()) return null;
    return refType;
  }
  public void storeDependencyInfo() {
    // add the type hierarchy of each referenced supertype
    // cannot do early since the hierarchy may not be fully resolved
    for (int i = 0; i < this.referencedSuperTypes.size; i++) { // grows as more types are added
      ReferenceBinding type = (ReferenceBinding) this.referencedSuperTypes.elementAt(i);
      if (!this.referencedTypes.containsIdentical(type)) this.referencedTypes.add(type);

      if (!type.isLocalType()) {
        ReferenceBinding enclosing = type.enclosingType();
        if (enclosing != null) recordSuperTypeReference(enclosing);
      }
      ReferenceBinding superclass = type.superclass();
      if (superclass != null) recordSuperTypeReference(superclass);
      ReferenceBinding[] interfaces = type.superInterfaces();
      if (interfaces != null)
        for (int j = 0, length = interfaces.length; j < length; j++)
          recordSuperTypeReference(interfaces[j]);
    }

    for (int i = 0, l = this.referencedTypes.size; i < l; i++) {
      ReferenceBinding type = (ReferenceBinding) this.referencedTypes.elementAt(i);
      if (!type.isLocalType())
        recordQualifiedReference(
            type.isMemberType()
                ? CharOperation.splitOn('.', type.readableName())
                : type.compoundName);
    }

    int size = this.qualifiedReferences.size;
    char[][][] qualifiedRefs = new char[size][][];
    for (int i = 0; i < size; i++) qualifiedRefs[i] = this.qualifiedReferences.elementAt(i);
    this.referenceContext.compilationResult.qualifiedReferences = qualifiedRefs;

    size = this.simpleNameReferences.size;
    char[][] simpleRefs = new char[size][];
    for (int i = 0; i < size; i++) simpleRefs[i] = this.simpleNameReferences.elementAt(i);
    this.referenceContext.compilationResult.simpleNameReferences = simpleRefs;

    size = this.rootReferences.size;
    char[][] rootRefs = new char[size][];
    for (int i = 0; i < size; i++) rootRefs[i] = this.rootReferences.elementAt(i);
    this.referenceContext.compilationResult.rootReferences = rootRefs;
  }
  /**
   * Resolve the supertypes for the types contained in the given openables (ICompilationUnits and/or
   * IClassFiles). Inform the requestor of the resolved supertypes for each supplied source type
   * using: connect(ISourceType suppliedType, IGenericType superclass, IGenericType[]
   * superinterfaces)
   *
   * <p>Also inform the requestor of the supertypes of each additional requested super type which is
   * also a source type instead of a binary type.
   *
   * @param openables
   * @param localTypes
   * @param monitor
   */
  public void resolve(Openable[] openables, HashSet localTypes, IProgressMonitor monitor) {
    try {
      int openablesLength = openables.length;
      CompilationUnitDeclaration[] parsedUnits = new CompilationUnitDeclaration[openablesLength];
      boolean[] hasLocalType = new boolean[openablesLength];
      org.aspectj.org.eclipse.jdt.core.ICompilationUnit[] cus =
          new org.aspectj.org.eclipse.jdt.core.ICompilationUnit[openablesLength];
      int unitsIndex = 0;

      CompilationUnitDeclaration focusUnit = null;
      ReferenceBinding focusBinaryBinding = null;
      IType focus = this.builder.getType();
      Openable focusOpenable = null;
      if (focus != null) {
        if (focus.isBinary()) {
          focusOpenable = (Openable) focus.getClassFile();
        } else {
          focusOpenable = (Openable) focus.getCompilationUnit();
        }
      }

      // build type bindings
      Parser parser = new Parser(this.lookupEnvironment.problemReporter, true);
      final boolean isJava8 = this.options.sourceLevel >= ClassFileConstants.JDK1_8;
      for (int i = 0; i < openablesLength; i++) {
        Openable openable = openables[i];
        if (openable instanceof org.aspectj.org.eclipse.jdt.core.ICompilationUnit) {
          org.aspectj.org.eclipse.jdt.core.ICompilationUnit cu =
              (org.aspectj.org.eclipse.jdt.core.ICompilationUnit) openable;

          // contains a potential subtype as a local or anonymous type?
          boolean containsLocalType = false;
          if (localTypes == null) { // case of hierarchy on region
            containsLocalType = true;
          } else {
            IPath path = cu.getPath();
            containsLocalType =
                cu.isWorkingCopy()
                    ? true /* presume conservatively */
                    : localTypes.contains(path.toString());
          }

          // build parsed unit
          CompilationUnitDeclaration parsedUnit = null;
          if (cu.isOpen()) {
            // create parsed unit from source element infos
            CompilationResult result =
                new CompilationResult(
                    (ICompilationUnit) cu, i, openablesLength, this.options.maxProblemsPerUnit);
            SourceTypeElementInfo[] typeInfos = null;
            try {
              IType[] topLevelTypes = cu.getTypes();
              int topLevelLength = topLevelTypes.length;
              if (topLevelLength == 0)
                continue; // empty cu: no need to parse (see
                          // https://bugs.eclipse.org/bugs/show_bug.cgi?id=65677)
              typeInfos = new SourceTypeElementInfo[topLevelLength];
              for (int j = 0; j < topLevelLength; j++) {
                IType topLevelType = topLevelTypes[j];
                typeInfos[j] =
                    (SourceTypeElementInfo) ((JavaElement) topLevelType).getElementInfo();
              }
            } catch (JavaModelException e) {
              // types/cu exist since cu is opened
            }
            int flags =
                !containsLocalType
                    ? SourceTypeConverter.MEMBER_TYPE | (isJava8 ? SourceTypeConverter.METHOD : 0)
                    : SourceTypeConverter.FIELD_AND_METHOD
                        | SourceTypeConverter.MEMBER_TYPE
                        | SourceTypeConverter.LOCAL_TYPE;
            parsedUnit =
                SourceTypeConverter.buildCompilationUnit(
                    typeInfos, flags, this.lookupEnvironment.problemReporter, result);

            // We would have got all the necessary local types by now and hence there is no further
            // need
            // to parse the method bodies. Parser.getMethodBodies, which is called latter in this
            // function,
            // will not parse the method statements if ASTNode.HasAllMethodBodies is set.
            if (containsLocalType) parsedUnit.bits |= ASTNode.HasAllMethodBodies;
          } else {
            // create parsed unit from file
            IFile file = (IFile) cu.getResource();
            ICompilationUnit sourceUnit =
                this.builder.createCompilationUnitFromPath(openable, file);
            CompilationResult unitResult =
                new CompilationResult(
                    sourceUnit, i, openablesLength, this.options.maxProblemsPerUnit);
            parsedUnit = parser.dietParse(sourceUnit, unitResult);
          }

          if (parsedUnit != null) {
            hasLocalType[unitsIndex] = containsLocalType;
            cus[unitsIndex] = cu;
            parsedUnits[unitsIndex++] = parsedUnit;
            try {
              this.lookupEnvironment.buildTypeBindings(parsedUnit, null /*no access restriction*/);
              if (openable.equals(focusOpenable)) {
                focusUnit = parsedUnit;
              }
            } catch (AbortCompilation e) {
              // classpath problem for this type: ignore
            }
          }
        } else {
          // cache binary type binding
          ClassFile classFile = (ClassFile) openable;
          IBinaryType binaryType =
              (IBinaryType) JavaModelManager.getJavaModelManager().getInfo(classFile.getType());
          if (binaryType == null) {
            // create binary type from file
            if (classFile.getPackageFragmentRoot().isArchive()) {
              binaryType = this.builder.createInfoFromClassFileInJar(classFile);
            } else {
              IResource file = classFile.resource();
              binaryType = this.builder.createInfoFromClassFile(classFile, file);
            }
          }
          if (binaryType != null) {
            try {
              BinaryTypeBinding binaryTypeBinding =
                  this.lookupEnvironment.cacheBinaryType(
                      binaryType,
                      false /*don't need field and method (bug 125067)*/,
                      null /*no access restriction*/);
              remember(binaryType, binaryTypeBinding);
              if (openable.equals(focusOpenable)) {
                focusBinaryBinding = binaryTypeBinding;
              }
            } catch (AbortCompilation e) {
              // classpath problem for this type: ignore
            }
          }
        }
      }

      // remember type declaration of focus if local/anonymous early (see
      // https://bugs.eclipse.org/bugs/show_bug.cgi?id=210498)
      TypeDeclaration focusLocalType = null;
      if (focus != null
          && focusBinaryBinding == null
          && focusUnit != null
          && ((Member) focus).getOuterMostLocalContext() != null) {
        focusLocalType = new ASTNodeFinder(focusUnit).findType(focus);
      }

      for (int i = 0; i <= this.typeIndex; i++) {
        IGenericType suppliedType = this.typeModels[i];
        if (suppliedType != null && suppliedType.isBinaryType()) {
          CompilationUnitDeclaration previousUnitBeingCompleted =
              this.lookupEnvironment.unitBeingCompleted;
          // fault in its hierarchy...
          try {
            // ensure that unitBeingCompleted is set so that we don't get an AbortCompilation for a
            // missing type
            // (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=213249 )
            if (previousUnitBeingCompleted == null) {
              this.lookupEnvironment.unitBeingCompleted = FakeUnit;
            }
            ReferenceBinding typeBinding = this.typeBindings[i];
            typeBinding.superclass();
            typeBinding.superInterfaces();
          } catch (AbortCompilation e) {
            // classpath problem for this type: ignore
          } finally {
            this.lookupEnvironment.unitBeingCompleted = previousUnitBeingCompleted;
          }
        }
      }

      // complete type bindings (i.e. connect super types)
      for (int i = 0; i < unitsIndex; i++) {
        CompilationUnitDeclaration parsedUnit = parsedUnits[i];
        if (parsedUnit != null) {
          try {
            if (hasLocalType[i]) { // NB: no-op if method bodies have been already parsed
              if (monitor != null && monitor.isCanceled()) throw new OperationCanceledException();
              parser.getMethodBodies(parsedUnit);
            }
          } catch (AbortCompilation e) {
            // classpath problem for this type: don't try to resolve (see
            // https://bugs.eclipse.org/bugs/show_bug.cgi?id=49809)
            hasLocalType[i] = false;
          }
        }
      }
      // complete type bindings and build fields and methods only for local types
      // (in this case the constructor is needed when resolving local types)
      // (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=145333)
      try {
        this.lookupEnvironment.completeTypeBindings(parsedUnits, hasLocalType, unitsIndex);
        // remember type bindings
        for (int i = 0; i < unitsIndex; i++) {
          CompilationUnitDeclaration parsedUnit = parsedUnits[i];
          if (parsedUnit != null && !parsedUnit.hasErrors()) {
            boolean containsLocalType = hasLocalType[i];
            if (containsLocalType) {
              if (monitor != null && monitor.isCanceled()) throw new OperationCanceledException();
              parsedUnit.scope.faultInTypes();
              parsedUnit.resolve();
            }

            rememberAllTypes(parsedUnit, cus[i], containsLocalType);
          }
        }
      } catch (AbortCompilation e) {
        // skip it silently
      }
      worked(monitor, 1);

      // if no potential subtype was a real subtype of the binary focus type, no need to go further
      // (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=54043)
      if (focusBinaryBinding == null && focus != null && focus.isBinary()) {
        char[] fullyQualifiedName = focus.getFullyQualifiedName().toCharArray();
        focusBinaryBinding =
            this.lookupEnvironment.getCachedType(CharOperation.splitOn('.', fullyQualifiedName));
        // https://bugs.eclipse.org/bugs/show_bug.cgi?id=436155
        // When local types are requested, it's likely a type is found from cache without
        // the hierarchy being resolved completely, so consider that factor too
        if (focusBinaryBinding == null
            || (focusBinaryBinding.tagBits & TagBits.HasUnresolvedSuperclass) != 0) return;
      }

      reportHierarchy(focus, focusLocalType, focusBinaryBinding);

    } catch (
        ClassCastException
            e) { // work-around for 1GF5W1S - can happen in case duplicates are fed to the hierarchy
                 // with binaries hiding sources
    } catch (
        AbortCompilation
            e) { // ignore this exception for now since it typically means we cannot find
                 // java.lang.Object
      if (TypeHierarchy.DEBUG) e.printStackTrace();
    } finally {
      reset();
    }
  }
  /*
   * Reports the hierarchy from the remembered bindings.
   * Note that 'binaryTypeBinding' is null if focus type is a source type.
   */
  private void reportHierarchy(
      IType focus, TypeDeclaration focusLocalType, ReferenceBinding binaryTypeBinding) {

    // set focus type binding
    if (focus != null) {
      if (binaryTypeBinding != null) {
        // binary type
        this.focusType = binaryTypeBinding;
      } else {
        // source type
        if (focusLocalType != null) {
          // anonymous or local type
          this.focusType = focusLocalType.binding;
        } else {
          // top level or member type
          char[] fullyQualifiedName = focus.getFullyQualifiedName().toCharArray();
          setFocusType(CharOperation.splitOn('.', fullyQualifiedName));
        }
      }
    }

    // be resilient and fix super type bindings
    fixSupertypeBindings();

    int objectIndex = -1;
    IProgressMonitor progressMonitor = this.builder.hierarchy.progressMonitor;
    for (int current = this.typeIndex; current >= 0; current--) {
      if (progressMonitor != null && progressMonitor.isCanceled())
        throw new OperationCanceledException();

      ReferenceBinding typeBinding = this.typeBindings[current];

      // java.lang.Object treated at the end
      if (typeBinding.id == TypeIds.T_JavaLangObject) {
        objectIndex = current;
        continue;
      }

      IGenericType suppliedType = this.typeModels[current];

      if (!subOrSuperOfFocus(typeBinding)) {
        continue; // ignore types outside of hierarchy
      }

      IType superclass;
      if (typeBinding.isInterface()) { // do not connect interfaces to Object
        superclass = null;
      } else {
        superclass = findSuperClass(suppliedType, typeBinding);
      }
      IType[] superinterfaces = findSuperInterfaces(suppliedType, typeBinding);

      this.builder.connect(
          suppliedType,
          this.builder.getHandle(suppliedType, typeBinding),
          superclass,
          superinterfaces);
    }
    // add java.lang.Object only if the super class is not missing
    if (objectIndex > -1 && (!this.hasMissingSuperClass || this.focusType == null)) {
      IGenericType objectType = this.typeModels[objectIndex];
      this.builder.connect(
          objectType,
          this.builder.getHandle(objectType, this.typeBindings[objectIndex]),
          null,
          null);
    }
  }
  private void remember(IType type, ReferenceBinding typeBinding) {
    if (((CompilationUnit) type.getCompilationUnit()).isOpen()) {
      try {
        IGenericType genericType = (IGenericType) ((JavaElement) type).getElementInfo();
        remember(genericType, typeBinding);
      } catch (JavaModelException e) {
        // cannot happen since element is open
        return;
      }
    } else {
      if (typeBinding == null) return;
      boolean isAnonymous = false;
      try {
        isAnonymous = type.isAnonymous();
      } catch (JavaModelException jme) {
        // Ignore
      }
      if (typeBinding instanceof SourceTypeBinding) {
        TypeDeclaration typeDeclaration = ((SourceTypeBinding) typeBinding).scope.referenceType();

        // simple super class name
        char[] superclassName = null;
        TypeReference superclass;
        if ((typeDeclaration.bits & ASTNode.IsAnonymousType) != 0) {
          superclass = typeDeclaration.allocation.type;
        } else {
          superclass = typeDeclaration.superclass;
        }
        if (superclass != null) {
          char[][] typeName = superclass.getTypeName();
          superclassName = typeName == null ? null : typeName[typeName.length - 1];
        }

        // simple super interface names
        char[][] superInterfaceNames = null;
        TypeReference[] superInterfaces = typeDeclaration.superInterfaces;
        if (superInterfaces != null) {
          int length = superInterfaces.length;
          superInterfaceNames = new char[length][];
          for (int i = 0; i < length; i++) {
            TypeReference superInterface = superInterfaces[i];
            char[][] typeName = superInterface.getTypeName();
            superInterfaceNames[i] = typeName[typeName.length - 1];
          }
        }

        HierarchyType hierarchyType =
            new HierarchyType(
                type,
                typeDeclaration.name,
                typeDeclaration.binding.modifiers,
                superclassName,
                superInterfaceNames,
                isAnonymous);
        remember(hierarchyType, typeDeclaration.binding);
      } else {
        HierarchyType hierarchyType =
            new HierarchyType(
                type,
                typeBinding.sourceName(),
                typeBinding.modifiers,
                typeBinding.superclass().sourceName(),
                new char[][] {typeBinding.superInterfaces()[0].sourceName()},
                isAnonymous);
        remember(hierarchyType, typeBinding);
      }
    }
  }
  /*
   * For all type bindings that have hierarchy problems, artificially fix their superclass/superInterfaces so that the connection
   * can be made.
   */
  private void fixSupertypeBindings() {
    for (int current = this.typeIndex; current >= 0; current--) {
      ReferenceBinding typeBinding = this.typeBindings[current];
      if ((typeBinding.tagBits & TagBits.HierarchyHasProblems) == 0) continue;

      if (typeBinding instanceof SourceTypeBinding) {
        if (typeBinding instanceof LocalTypeBinding) {
          LocalTypeBinding localTypeBinding = (LocalTypeBinding) typeBinding;
          QualifiedAllocationExpression allocationExpression =
              localTypeBinding.scope.referenceContext.allocation;
          TypeReference type;
          if (allocationExpression != null
              && (type = allocationExpression.type) != null
              && type.resolvedType != null) {
            localTypeBinding.setSuperClass((ReferenceBinding) type.resolvedType);
            continue;
          }
        }
        ClassScope scope = ((SourceTypeBinding) typeBinding).scope;
        if (scope != null) {
          TypeDeclaration typeDeclaration = scope.referenceContext;
          TypeReference superclassRef = typeDeclaration == null ? null : typeDeclaration.superclass;
          TypeBinding superclass = superclassRef == null ? null : superclassRef.resolvedType;
          if (superclass != null) {
            superclass = superclass.closestMatch();
          }
          if (superclass instanceof ReferenceBinding) {
            // ensure we are not creating a cycle (see
            // https://bugs.eclipse.org/bugs/show_bug.cgi?id=215681 )
            if (!(subTypeOfType((ReferenceBinding) superclass, typeBinding))) {
              ((SourceTypeBinding) typeBinding).setSuperClass((ReferenceBinding) superclass);
            }
          }

          TypeReference[] superInterfaces =
              typeDeclaration == null ? null : typeDeclaration.superInterfaces;
          int length;
          ReferenceBinding[] interfaceBindings = typeBinding.superInterfaces();
          if (superInterfaces != null
              && (length = superInterfaces.length)
                  > (interfaceBindings == null
                      ? 0
                      : interfaceBindings
                          .length)) { // check for interfaceBindings being null (see
                                      // https://bugs.eclipse.org/bugs/show_bug.cgi?id=139689)
            interfaceBindings = new ReferenceBinding[length];
            int index = 0;
            for (int i = 0; i < length; i++) {
              TypeBinding superInterface = superInterfaces[i].resolvedType;
              if (superInterface != null) {
                superInterface = superInterface.closestMatch();
              }
              if (superInterface instanceof ReferenceBinding) {
                // ensure we are not creating a cycle (see
                // https://bugs.eclipse.org/bugs/show_bug.cgi?id=215681 )
                if (!(subTypeOfType((ReferenceBinding) superInterface, typeBinding))) {
                  interfaceBindings[index++] = (ReferenceBinding) superInterface;
                }
              }
            }
            if (index < length)
              System.arraycopy(
                  interfaceBindings, 0, interfaceBindings = new ReferenceBinding[index], 0, index);
            ((SourceTypeBinding) typeBinding).setSuperInterfaces(interfaceBindings);
          }
        }
      } else if (typeBinding instanceof BinaryTypeBinding) {
        try {
          typeBinding.superclass();
        } catch (AbortCompilation e) {
          // allow subsequent call to superclass() to succeed so that we don't have to catch
          // AbortCompilation everywhere
          ((BinaryTypeBinding) typeBinding).tagBits &= ~TagBits.HasUnresolvedSuperclass;
          this.builder.hierarchy.missingTypes.add(
              new String(typeBinding.superclass().sourceName()));
          this.hasMissingSuperClass = true;
        }
        try {
          typeBinding.superInterfaces();
        } catch (AbortCompilation e) {
          // allow subsequent call to superInterfaces() to succeed so that we don't have to catch
          // AbortCompilation everywhere
          ((BinaryTypeBinding) typeBinding).tagBits &= ~TagBits.HasUnresolvedSuperinterfaces;
        }
      }
    }
  }
  /*
   * Returns the handles of the super interfaces of the given type.
   * Adds the simple name to the hierarchy missing types if an interface is not found (but don't put null in the returned array)
   */
  private IType[] findSuperInterfaces(IGenericType type, ReferenceBinding typeBinding) {
    char[][] superInterfaceNames;
    char separator;
    if (type instanceof IBinaryType) {
      superInterfaceNames = ((IBinaryType) type).getInterfaceNames();
      separator = '/';
    } else if (type instanceof ISourceType) {
      ISourceType sourceType = (ISourceType) type;
      if (sourceType.isAnonymous()) { // if anonymous type
        if (typeBinding.superInterfaces() != null && typeBinding.superInterfaces().length > 0) {
          superInterfaceNames = new char[][] {sourceType.getSuperclassName()};
        } else {
          superInterfaceNames = sourceType.getInterfaceNames();
        }
      } else {
        if (TypeDeclaration.kind(sourceType.getModifiers()) == TypeDeclaration.ANNOTATION_TYPE_DECL)
          superInterfaceNames =
              new char[][] {TypeConstants.CharArray_JAVA_LANG_ANNOTATION_ANNOTATION};
        else superInterfaceNames = sourceType.getInterfaceNames();
      }
      separator = '.';
    } else if (type instanceof HierarchyType) {
      HierarchyType hierarchyType = (HierarchyType) type;
      if (hierarchyType.isAnonymous()) { // if anonymous type
        if (typeBinding.superInterfaces() != null && typeBinding.superInterfaces().length > 0) {
          superInterfaceNames = new char[][] {hierarchyType.superclassName};
        } else {
          superInterfaceNames = hierarchyType.superInterfaceNames;
        }
      } else {
        superInterfaceNames = hierarchyType.superInterfaceNames;
      }
      separator = '.';
    } else {
      return null;
    }

    ReferenceBinding[] interfaceBindings = typeBinding.superInterfaces();
    int bindingIndex = 0;
    int bindingLength = interfaceBindings == null ? 0 : interfaceBindings.length;
    int length = superInterfaceNames == null ? 0 : superInterfaceNames.length;
    IType[] superinterfaces = new IType[length];
    int index = 0;
    next:
    for (int i = 0; i < length; i++) {
      char[] superInterfaceName = superInterfaceNames[i];
      int end = superInterfaceName.length;

      // find the end of simple name
      int genericStart = CharOperation.indexOf(Signature.C_GENERIC_START, superInterfaceName);
      if (genericStart != -1) end = genericStart;

      // find the start of simple name
      int lastSeparator = CharOperation.lastIndexOf(separator, superInterfaceName, 0, end);
      int start = lastSeparator + 1;

      // case of binary inner type -> take the last part
      int lastDollar = CharOperation.lastIndexOf('$', superInterfaceName, start);
      if (lastDollar != -1) start = lastDollar + 1;

      char[] simpleName = CharOperation.subarray(superInterfaceName, start, end);

      if (bindingIndex < bindingLength) {
        ReferenceBinding interfaceBinding =
            (ReferenceBinding) interfaceBindings[bindingIndex].erasure();

        // ensure that the binding corresponds to the interface defined by the user
        if (CharOperation.equals(simpleName, interfaceBinding.sourceName)) {
          bindingIndex++;
          for (int t = this.typeIndex; t >= 0; t--) {
            if (TypeBinding.equalsEquals(this.typeBindings[t], interfaceBinding)) {
              IType handle = this.builder.getHandle(this.typeModels[t], interfaceBinding);
              if (handle != null) {
                superinterfaces[index++] = handle;
                continue next;
              }
            }
          }
        }
      }
      this.builder.hierarchy.missingTypes.add(new String(simpleName));
    }
    if (index != length)
      System.arraycopy(superinterfaces, 0, superinterfaces = new IType[index], 0, index);
    return superinterfaces;
  }
  /**
   * Checks for duplicates. If all ok, records the importBinding returns -1 when this import is
   * flagged as duplicate.
   *
   * @param importBinding
   * @param typesBySimpleNames
   * @param importReference
   * @param compoundName
   * @return -1 when this import is flagged as duplicate, importPtr otherwise.
   */
  private int checkAndRecordImportBinding(
      Binding importBinding,
      HashtableOfType typesBySimpleNames,
      ImportReference importReference,
      char[][] compoundName) {
    ReferenceBinding conflictingType = null;
    if (importBinding instanceof MethodBinding) {
      conflictingType = (ReferenceBinding) getType(compoundName, compoundName.length);
      if (!conflictingType.isValidBinding()
          || (importReference.isStatic() && !conflictingType.isStatic())) conflictingType = null;
    }
    // collisions between an imported static field & a type should be checked according to spec...
    // but currently not by javac
    final char[] name = compoundName[compoundName.length - 1];
    if (importBinding instanceof ReferenceBinding || conflictingType != null) {
      ReferenceBinding referenceBinding =
          conflictingType == null ? (ReferenceBinding) importBinding : conflictingType;
      ReferenceBinding typeToCheck =
          referenceBinding.problemId() == ProblemReasons.Ambiguous
              ? ((ProblemReferenceBinding) referenceBinding).closestMatch
              : referenceBinding;
      if (importReference.isTypeUseDeprecated(typeToCheck, this))
        problemReporter().deprecatedType(typeToCheck, importReference);

      ReferenceBinding existingType = typesBySimpleNames.get(name);
      if (existingType != null) {
        // duplicate test above should have caught this case, but make sure
        if (TypeBinding.equalsEquals(existingType, referenceBinding)) {
          // https://bugs.eclipse.org/bugs/show_bug.cgi?id=302865
          // Check all resolved imports to see if this import qualifies as a duplicate
          for (int j = 0; j < this.importPtr; j++) {
            ImportBinding resolved = this.tempImports[j];
            if (resolved instanceof ImportConflictBinding) {
              ImportConflictBinding importConflictBinding = (ImportConflictBinding) resolved;
              if (TypeBinding.equalsEquals(
                  importConflictBinding.conflictingTypeBinding, referenceBinding)) {
                if (!importReference.isStatic()) {
                  // resolved is implicitly static
                  problemReporter().duplicateImport(importReference);
                  recordImportBinding(
                      new ImportBinding(compoundName, false, importBinding, importReference));
                }
              }
            } else if (resolved.resolvedImport == referenceBinding) {
              if (importReference.isStatic() != resolved.isStatic()) {
                problemReporter().duplicateImport(importReference);
                recordImportBinding(
                    new ImportBinding(compoundName, false, importBinding, importReference));
              }
            }
          }
          return -1;
        }
        // either the type collides with a top level type or another imported type
        for (int j = 0, length = this.topLevelTypes.length; j < length; j++) {
          if (CharOperation.equals(this.topLevelTypes[j].sourceName, existingType.sourceName)) {
            problemReporter().conflictingImport(importReference);
            return -1;
          }
        }
        if (importReference.isStatic()
            && importBinding instanceof ReferenceBinding
            && compilerOptions().sourceLevel >= ClassFileConstants.JDK1_8) {
          // 7.5.3 says nothing about collision of single static imports and JDK8 tolerates them,
          // though use is flagged.
          for (int j = 0; j < this.importPtr; j++) {
            ImportBinding resolved = this.tempImports[j];
            if (resolved.isStatic()
                && resolved.resolvedImport instanceof ReferenceBinding
                && importBinding != resolved.resolvedImport) {
              if (CharOperation.equals(
                  name, resolved.compoundName[resolved.compoundName.length - 1])) {
                ReferenceBinding type = (ReferenceBinding) resolved.resolvedImport;
                resolved.resolvedImport =
                    new ProblemReferenceBinding(
                        new char[][] {name}, type, ProblemReasons.Ambiguous);
                return -1;
              }
            }
          }
        }
        problemReporter().duplicateImport(importReference);
        return -1;
      }
      typesBySimpleNames.put(name, referenceBinding);
    } else if (importBinding instanceof FieldBinding) {
      for (int j = 0; j < this.importPtr; j++) {
        ImportBinding resolved = this.tempImports[j];
        // find other static fields with the same name
        if (resolved.isStatic()
            && resolved.resolvedImport instanceof FieldBinding
            && importBinding != resolved.resolvedImport) {
          if (CharOperation.equals(name, resolved.compoundName[resolved.compoundName.length - 1])) {
            if (compilerOptions().sourceLevel >= ClassFileConstants.JDK1_8) {
              // 7.5.3 says nothing about collision of single static imports and JDK8 tolerates
              // them, though use is flagged.
              FieldBinding field = (FieldBinding) resolved.resolvedImport;
              resolved.resolvedImport =
                  new ProblemFieldBinding(
                      field, field.declaringClass, name, ProblemReasons.Ambiguous);
              return -1;
            } else {
              problemReporter().duplicateImport(importReference);
              return -1;
            }
          }
        }
      }
    }
    if (conflictingType == null) {
      recordImportBinding(new ImportBinding(compoundName, false, importBinding, importReference));
    } else {
      recordImportBinding(
          new ImportConflictBinding(compoundName, importBinding, conflictingType, importReference));
    }
    return this.importPtr;
  }
  void buildTypeBindings(AccessRestriction accessRestriction) {
    this.topLevelTypes =
        new SourceTypeBinding[0]; // want it initialized if the package cannot be resolved
    boolean firstIsSynthetic = false;
    if (this.referenceContext.compilationResult.compilationUnit != null) {
      char[][] expectedPackageName =
          this.referenceContext.compilationResult.compilationUnit.getPackageName();
      if (expectedPackageName != null
          && !CharOperation.equals(this.currentPackageName, expectedPackageName)) {

        // only report if the unit isn't structurally empty
        if (this.referenceContext.currentPackage != null
            || this.referenceContext.types != null
            || this.referenceContext.imports != null) {
          problemReporter().packageIsNotExpectedPackage(this.referenceContext);
        }
        this.currentPackageName =
            expectedPackageName.length == 0 ? CharOperation.NO_CHAR_CHAR : expectedPackageName;
      }
    }
    if (this.currentPackageName == CharOperation.NO_CHAR_CHAR) {
      // environment default package is never null
      this.fPackage = this.environment.defaultPackage;
    } else {
      if ((this.fPackage = this.environment.createPackage(this.currentPackageName)) == null) {
        if (this.referenceContext.currentPackage != null) {
          problemReporter()
              .packageCollidesWithType(
                  this.referenceContext); // only report when the unit has a package statement
        }
        // ensure fPackage is not null
        this.fPackage = this.environment.defaultPackage;
        return;
      } else if (this.referenceContext.isPackageInfo()) {
        // resolve package annotations now if this is "package-info.java".
        if (this.referenceContext.types == null || this.referenceContext.types.length == 0) {
          this.referenceContext.types = new TypeDeclaration[1];
          this.referenceContext.createPackageInfoType();
          firstIsSynthetic = true;
        }
        // ensure the package annotations are copied over before resolution
        if (this.referenceContext.currentPackage != null
            && this.referenceContext.currentPackage.annotations != null) {
          this.referenceContext.types[0].annotations =
              this.referenceContext.currentPackage.annotations;
        }
      }
      recordQualifiedReference(this.currentPackageName); // always dependent on your own package
    }

    // Skip typeDeclarations which know of previously reported errors
    TypeDeclaration[] types = this.referenceContext.types;
    int typeLength = (types == null) ? 0 : types.length;
    this.topLevelTypes = new SourceTypeBinding[typeLength];
    int count = 0;
    nextType:
    for (int i = 0; i < typeLength; i++) {
      TypeDeclaration typeDecl = types[i];
      if (this.environment.isProcessingAnnotations && this.environment.isMissingType(typeDecl.name))
        throw new SourceTypeCollisionException(); // resolved a type ref before APT generated the
      // type
      ReferenceBinding typeBinding = this.fPackage.getType0(typeDecl.name);
      recordSimpleReference(typeDecl.name); // needed to detect collision cases
      if (typeBinding != null
          && typeBinding.isValidBinding()
          && !(typeBinding instanceof UnresolvedReferenceBinding)) {
        // if its an unresolved binding - its fixed up whenever its needed, see
        // UnresolvedReferenceBinding.resolve()
        if (this.environment.isProcessingAnnotations)
          throw new SourceTypeCollisionException(); // resolved a type ref before APT generated the
        // type
        // if a type exists, check that its a valid type
        // it can be a NotFound problem type if its a secondary type referenced before its primary
        // type found in additional units
        // and it can be an unresolved type which is now being defined
        problemReporter().duplicateTypes(this.referenceContext, typeDecl);
        continue nextType;
      }
      if (this.fPackage != this.environment.defaultPackage
          && this.fPackage.getPackage(typeDecl.name) != null) {
        // if a package exists, it must be a valid package - cannot be a NotFound problem package
        // this is now a warning since a package does not really 'exist' until it contains a type,
        // see JLS v2, 7.4.3
        problemReporter().typeCollidesWithPackage(this.referenceContext, typeDecl);
      }

      if ((typeDecl.modifiers & ClassFileConstants.AccPublic) != 0) {
        char[] mainTypeName;
        if ((mainTypeName = this.referenceContext.getMainTypeName())
                != null // mainTypeName == null means that implementor of ICompilationUnit decided
            // to return null
            && !CharOperation.equals(mainTypeName, typeDecl.name)) {
          problemReporter().publicClassMustMatchFileName(this.referenceContext, typeDecl);
          // tolerate faulty main type name (91091), allow to proceed into type construction
        }
      }

      ClassScope child = new ClassScope(this, typeDecl);
      SourceTypeBinding type = child.buildType(null, this.fPackage, accessRestriction);
      if (firstIsSynthetic && i == 0) type.modifiers |= ClassFileConstants.AccSynthetic;
      if (type != null) this.topLevelTypes[count++] = type;
    }

    // shrink topLevelTypes... only happens if an error was reported
    if (count != this.topLevelTypes.length)
      System.arraycopy(
          this.topLevelTypes, 0, this.topLevelTypes = new SourceTypeBinding[count], 0, count);
  }
  /*
   * INTERNAL USE-ONLY
   * Innerclasses get their name computed as they are generated, since some may not
   * be actually outputed if sitting inside unreachable code.
   */
  public char[] computeConstantPoolName(LocalTypeBinding localType) {
    if (localType.constantPoolName != null) {
      return localType.constantPoolName;
    }
    // delegates to the outermost enclosing classfile, since it is the only one with a global vision
    // of its innertypes.

    if (this.constantPoolNameUsage == null) this.constantPoolNameUsage = new HashtableOfType();

    ReferenceBinding outerMostEnclosingType =
        localType.scope.outerMostClassScope().enclosingSourceType();

    // ensure there is not already such a local type name defined by the user
    int index = 0;
    char[] candidateName;
    boolean isCompliant15 = compilerOptions().complianceLevel >= ClassFileConstants.JDK1_5;
    while (true) {
      if (localType.isMemberType()) {
        if (index == 0) {
          candidateName =
              CharOperation.concat(
                  localType.enclosingType().constantPoolName(), localType.sourceName, '$');
        } else {
          // in case of collision, then member name gets extra $1 inserted
          // e.g. class X { { class L{} new X(){ class L{} } } }
          candidateName =
              CharOperation.concat(
                  localType.enclosingType().constantPoolName(),
                  '$',
                  String.valueOf(index).toCharArray(),
                  '$',
                  localType.sourceName);
        }
      } else if (localType.isAnonymousType()) {
        if (isCompliant15) {
          // AspectJ Extension start
          char[] extraInsert = null;
          if (outerMostEnclosingType instanceof SourceTypeBinding) {
            SourceTypeBinding sourceTypeBinding = (SourceTypeBinding) outerMostEnclosingType;
            ClassScope classScope = sourceTypeBinding.scope;
            if (classScope != null) {
              TypeDeclaration typeDeclaration = classScope.referenceContext;
              if (typeDeclaration != null) {
                extraInsert = typeDeclaration.getLocalTypeNameSuffix();
              }
            }
          }
          if (extraInsert != null) { // AspectJ Extension end
            candidateName =
                CharOperation.concat(
                    localType.enclosingType.constantPoolName(),
                    '$',
                    extraInsert,
                    '$',
                    String.valueOf(index + 1).toCharArray());
          } else {
            // from 1.5 on, use immediately enclosing type name
            candidateName =
                CharOperation.concat(
                    localType.enclosingType.constantPoolName(),
                    String.valueOf(index + 1).toCharArray(),
                    '$');
          } // AspectJ extension, closing }
        } else {
          candidateName =
              CharOperation.concat(
                  outerMostEnclosingType.constantPoolName(),
                  String.valueOf(index + 1).toCharArray(),
                  '$');
        }
      } else {
        // local type
        if (isCompliant15) {
          candidateName =
              CharOperation.concat(
                  CharOperation.concat(
                      localType.enclosingType().constantPoolName(),
                      String.valueOf(index + 1).toCharArray(),
                      '$'),
                  localType.sourceName);
        } else {
          candidateName =
              CharOperation.concat(
                  outerMostEnclosingType.constantPoolName(),
                  '$',
                  String.valueOf(index + 1).toCharArray(),
                  '$',
                  localType.sourceName);
        }
      }
      if (this.constantPoolNameUsage.get(candidateName) != null) {
        index++;
      } else {
        this.constantPoolNameUsage.put(candidateName, localType);
        break;
      }
    }
    return candidateName;
  }