/** Return true if requiredParameters is an exact match for candidateParameters. */
 protected boolean exactlyMatches(
     List<? extends PM> requiredParameters, List<? extends PM> candidateParameters) {
   int iMax = requiredParameters.size();
   if (iMax != candidateParameters.size()) {
     return false;
   }
   for (int i = 0; i < iMax; i++) {
     PM requiredParameter = requiredParameters.get(i);
     PM candidateParameter = candidateParameters.get(i);
     C requiredType = uml.getOCLType(requiredParameter);
     C candidateType = uml.getOCLType(candidateParameter);
     if (requiredType != candidateType) {
       return false;
     }
   }
   return true;
 }
 /** Return the map of name to operator or list of operations for a type. */
 protected Map<String, Object> getName2OperationOrOperations(C type) {
   Map<String, Object> name2operationOrOperations = type2name2operationOrOperations.get(type);
   if (name2operationOrOperations == null) {
     name2operationOrOperations = new HashMap<String, Object>();
     type2name2operationOrOperations.put(type, name2operationOrOperations);
     List<O> allOperations = getOperations(type);
     for (O candidateOperation : allOperations) {
       String name = uml.getName(candidateOperation);
       Object overloadOrOverloads = name2operationOrOperations.get(name);
       if (overloadOrOverloads == null) {
         name2operationOrOperations.put(name, candidateOperation);
       } else {
         List<O> overloads;
         if (overloadOrOverloads instanceof List<?>) {
           @SuppressWarnings("unchecked")
           List<O> castOperations = (List<O>) overloadOrOverloads;
           overloads = castOperations;
         } else {
           overloads = new ArrayList<O>();
           name2operationOrOperations.put(name, overloads);
           @SuppressWarnings("unchecked")
           O castOperation = (O) overloadOrOverloads;
           overloads.add(castOperation);
         }
         C candidateOwner = uml.getOwningClassifier(candidateOperation);
         Collection<? extends C> candidateSupertypes = uml.getAllSupertypes(candidateOwner);
         List<PM> candidateParameters = uml.getParameters(candidateOperation);
         int iMax = candidateParameters.size();
         int j = overloads.size();
         while (--j >= 0) { // Reverse count to allow remove()
           O oldOperation = overloads.get(j);
           List<PM> oldParameters = uml.getParameters(oldOperation);
           if (iMax == oldParameters.size()) {
             int i = 0;
             for (; i < iMax; i++) {
               PM candidateParameter = candidateParameters.get(i);
               PM oldParameter = oldParameters.get(i);
               C oldType = uml.getOCLType(oldParameter);
               C candidateType = uml.getOCLType(candidateParameter);
               if (oldType != candidateType) {
                 break;
               }
             }
             if (i >= iMax) {
               C oldOwner = uml.getOwningClassifier(oldOperation);
               if (candidateSupertypes.contains(oldOwner)) {
                 overloads.remove(j);
               } else {
                 Collection<? extends C> oldSupertypes = uml.getAllSupertypes(oldOwner);
                 if (oldSupertypes.contains(candidateOwner)) {
                   break;
                 }
               }
             }
           }
         }
         if (j < 0) {
           overloads.add(candidateOperation);
         }
       }
     }
   }
   return name2operationOrOperations;
 }