/*
  For each inherited method identifier (message pattern - vm signature minus the return type)
  	if current method exists
  		if current's vm signature does not match an inherited signature then complain
  		else compare current's exceptions & visibility against each inherited method
  	else
  		if inherited methods = 1
  			if inherited is abstract && type is NOT an interface or abstract, complain
  		else
  			if vm signatures do not match complain
  			else
  				find the concrete implementation amongst the abstract methods (can only be 1)
  				if one exists then
  					it must be a public instance method
  					compare concrete's exceptions against each abstract method
  				else
  					complain about missing implementation only if type is NOT an interface or abstract
  */
  void checkMethods() {
    boolean mustImplementAbstractMethods = this.type.isClass() && !this.type.isAbstract();
    boolean skipInheritedMethods =
        mustImplementAbstractMethods
            && this.type.superInterfaces() == NoSuperInterfaces
            && this.type.superclass() != null
            && !this.type
                .superclass()
                .isAbstract(); // have a single concrete superclass so only check overridden methods
    char[][] methodSelectors = this.inheritedMethods.keyTable;
    nextSelector:
    for (int s = methodSelectors.length; --s >= 0; ) {
      if (methodSelectors[s] == null) continue nextSelector;

      MethodBinding[] current = (MethodBinding[]) this.currentMethods.get(methodSelectors[s]);
      if (current == null && skipInheritedMethods) continue nextSelector;

      MethodBinding[] inherited = (MethodBinding[]) this.inheritedMethods.valueTable[s];
      if (inherited.length == 1 && current == null) { // handle the common case
        if (mustImplementAbstractMethods && inherited[0].isAbstract())
          checkAbstractMethod(inherited[0]);
        continue nextSelector;
      }

      int index = -1;
      MethodBinding[] matchingInherited = new MethodBinding[inherited.length];
      if (current != null) {
        for (int i = 0, length1 = current.length; i < length1; i++) {
          while (index >= 0)
            matchingInherited[index--] =
                null; // clear the previous contents of the matching methods
          MethodBinding currentMethod = current[i];
          for (int j = 0, length2 = inherited.length; j < length2; j++) {
            MethodBinding inheritedMethod = inherited[j];
            if (inheritedMethod != null && areParametersEqual(currentMethod, inheritedMethod)) {
              matchingInherited[++index] = inheritedMethod;
              inherited[j] = null; // do not want to find it again
            }
          }
          if (index >= 0)
            this.checkAgainstInheritedMethods(
                currentMethod, matchingInherited, index + 1); // pass in the length of matching
        }
      }

      for (int i = 0, length = inherited.length; i < length; i++) {
        while (index >= 0)
          matchingInherited[index--] = null; // clear the previous contents of the matching methods
        MethodBinding inheritedMethod = inherited[i];
        if (inheritedMethod != null) {
          matchingInherited[++index] = inheritedMethod;
          for (int j = i + 1; j < length; j++) {
            if (inherited[j] != null && areParametersEqual(inheritedMethod, inherited[j])) {
              matchingInherited[++index] = inherited[j];
              inherited[j] = null; // do not want to find it again
            }
          }
        }
        if (index > 0)
          this.checkInheritedMethods(
              matchingInherited, index + 1); // pass in the length of matching
        else if (mustImplementAbstractMethods && index == 0 && matchingInherited[0].isAbstract())
          checkAbstractMethod(matchingInherited[0]);
      }
    }
  }