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