/** Return the map of name to property for a type. */
 protected Map<String, P> getName2Property(C type) {
   Map<String, P> name2property = type2name2property.get(type);
   if (name2property == null) {
     name2property = new HashMap<String, P>();
     type2name2property.put(type, name2property);
     List<P> allProperties = getAttributes(type);
     for (P candidateProperty : allProperties) {
       String name = uml.getName(candidateProperty);
       P oldProperty = name2property.get(name);
       if (oldProperty == null) {
         name2property.put(name, candidateProperty);
       } else {
         C candidateOwner = uml.getOwningClassifier(candidateProperty);
         Collection<? extends C> candidateSupertypes = uml.getAllSupertypes(candidateOwner);
         C oldOwner = uml.getOwningClassifier(oldProperty);
         if (candidateSupertypes.contains(oldOwner)) {
           name2property.put(name, candidateProperty);
         } else {
           Collection<? extends C> oldSupertypes = uml.getAllSupertypes(oldOwner);
           if (!oldSupertypes.contains(candidateOwner)) {; // This should not happen
           }
         }
       }
     }
   }
   return name2property;
 }
 /** 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;
 }