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