@Override
 public int hashCode() {
   int result = superClassGroup != null ? superClassGroup.hashCode() : 0;
   for (String attribute : getItems().keySet()) {
     // avoid processing values in getItems map to avoid infinite recursion
     // as ATTRIBUTE_ITEM.hashCode() calls group.hashCode()
     result = 31 * result + attribute.hashCode();
   }
   return 31 * result;
 }
 /**
  * INTERNAL: This method will insert the group into the entity hierarchy just below this
  * AttributeGroup.
  *
  * @param group
  */
 public void insertSubClass(CoreAttributeGroup group) {
   if (this == group) {
     return;
   }
   group.superClassGroup = this;
   if (subClasses != null) {
     for (Iterator<CoreAttributeGroup> subClasses = this.subClasses.iterator();
         subClasses.hasNext(); ) {
       CoreAttributeGroup subClass = subClasses.next();
       if (group != subClass && group.getType().isAssignableFrom(subClass.getType())) {
         group.subClasses.add(subClass);
         subClass.superClassGroup = group;
         subClasses.remove();
       }
     }
   } else {
     this.subClasses = new HashSet<CoreAttributeGroup>();
   }
   this.subClasses.add(group);
 }
 @Override
 public boolean equals(Object obj) {
   if (this != obj) {
     if (obj == null) {
       return false;
     }
     CoreAttributeGroup anotherGroup = null;
     try {
       anotherGroup = (CoreAttributeGroup) obj;
     } catch (ClassCastException cce) {
       return false;
     }
     if (hasItems()) {
       if (anotherGroup.hasItems()) {
         if (!getItems().equals(anotherGroup.getItems())) {
           return false;
         }
       } else {
         return false;
       }
     } else {
       if (anotherGroup.hasItems()) {
         return false;
       }
     }
     if (this.superClassGroup != null) {
       if (anotherGroup.superClassGroup != null) {
         return this.superClassGroup.equals(anotherGroup.superClassGroup);
       } else {
         return false;
       }
     } else {
       if (anotherGroup.superClassGroup != null) {
         return false;
       }
       return true;
     }
   } else {
     return true;
   }
 }
 /**
  * INTERNAL: Convert all the class-name-based settings in this Descriptor to actual class-based
  * settings. This method is used when converting a project that has been built with class names to
  * a project with classes.
  *
  * @param classLoader
  */
 public void convertClassNamesToClasses(ClassLoader classLoader) {
   if (this.type == null) {
     try {
       if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) {
         try {
           this.type =
               AccessController.doPrivileged(
                   new PrivilegedClassForName(this.typeName, true, classLoader));
         } catch (PrivilegedActionException exception) {
           throw ValidationException.classNotFoundWhileConvertingClassNames(
               this.typeName, exception.getException());
         }
       } else {
         this.type =
             org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(
                 this.typeName, true, classLoader);
       }
     } catch (ClassNotFoundException exc) {
       throw ValidationException.classNotFoundWhileConvertingClassNames(this.typeName, exc);
     }
     if (this.items != null) {
       for (ATTRIBUTE_ITEM item : this.items.values()) {
         item.convertClassNamesToClasses(classLoader);
       }
     }
     if (this.allsubclasses != null) {
       Map<Object, CoreAttributeGroup> allGroups = new HashMap<Object, CoreAttributeGroup>();
       this.subClasses = new HashSet<CoreAttributeGroup>();
       for (CoreAttributeGroup subClass : allsubclasses.values()) {
         subClass.convertClassNamesToClasses(classLoader);
         allGroups.put(subClass.getType(), subClass);
       }
       this.allsubclasses = allGroups;
       for (CoreAttributeGroup subClass : allsubclasses.values()) {
         if (CoreAttributeItem.orderInheritance(subClass, allGroups)) {
           this.insertSubClass(subClass);
         }
       }
     }
   }
 }
  /**
   * Locate the AttributeGroup where the leaf attribute in the path should be applied to.
   *
   * @param create indicates if intermediate AttributeGroup required within the specified path
   *     should be created as needed. When checking the state of the map callers should set this to
   *     false to avoid changing the state unexpectedly
   */
  protected ATTRIBUTE_ITEM getItem(String[] attributePath, boolean create) {
    ATTRIBUTE_ITEM item = null;
    CoreAttributeGroup<ATTRIBUTE_ITEM, DESCRIPTOR> currentGroup = this;

    for (int index = 0; index < attributePath.length; index++) {
      String attrName = attributePath[index];

      item = currentGroup.getItems().get(attrName);

      // Add missing AttributeGroup
      if (item == null) {
        // If not creating missing AttributeGroups then return null
        if (!create) {
          if (this.superClassGroup != null) {
            return (ATTRIBUTE_ITEM) this.superClassGroup.getItem(attributePath, create);
          }
          return null;
        }

        item = (ATTRIBUTE_ITEM) newItem(currentGroup, attrName);
        currentGroup.getItems().put(attrName, item);
      }

      // Add a AttributeGroup if not at the end of the attributes path
      if (item.getGroup() == null && index < (attributePath.length - 1)) {
        if (!create) {
          return null;
        }
        // XXX-dclarke: Converting the attribute[] into a string and then re-parsing it seems odd
        CoreAttributeGroup newGroup = newGroup(attrName, currentGroup);
        item.setRootGroup(newGroup);
      }

      currentGroup = item.getGroup();
    }

    return item;
  }
 /** Return true if this AttributeGroup is a super-set of the passed in AttributeGroup. */
 public boolean isSupersetOf(CoreAttributeGroup<ATTRIBUTE_ITEM, DESCRIPTOR> anotherGroup) {
   // TODO: should handle the case when the current group has all
   // attributes - then its equivalent to null
   if (anotherGroup == null) {
     return false;
   }
   if (anotherGroup != this) {
     if (hasItems()) {
       if (anotherGroup.hasItems()) {
         Iterator<Map.Entry<String, ATTRIBUTE_ITEM>> otherItemEntries =
             anotherGroup.getItems().entrySet().iterator();
         while (otherItemEntries.hasNext()) {
           Map.Entry<String, ATTRIBUTE_ITEM> otherItemEntry = otherItemEntries.next();
           String otherAttributeName = otherItemEntry.getKey();
           CoreAttributeItem item = this.items.get(otherAttributeName);
           if (item == null) {
             return false;
           }
           CoreAttributeGroup<ATTRIBUTE_ITEM, DESCRIPTOR> group = item.getGroup();
           CoreAttributeGroup<ATTRIBUTE_ITEM, DESCRIPTOR> otherGroup =
               otherItemEntry.getValue().getGroup();
           if (group != null) {
             if (!group.isSupersetOf(otherGroup)) {
               return false;
             }
           } else {
             if (otherGroup != null) {
               return true;
             }
           }
           group = item.getKeyGroup();
           otherGroup = otherItemEntry.getValue().getKeyGroup();
           if (group != null) {
             if (!group.isSupersetOf(otherGroup)) {
               return false;
             }
           } else {
             if (otherGroup != null) {
               return true;
             }
           }
           if (item.getGroups() != null) {
             if (otherItemEntry.getValue().getGroups() == null) {
               return true;
             }
             for (Object next : item.getGroups().values()) {
               CoreAttributeGroup<ATTRIBUTE_ITEM, DESCRIPTOR> element =
                   (CoreAttributeGroup<ATTRIBUTE_ITEM, DESCRIPTOR>) next;
               CoreAttributeGroup<ATTRIBUTE_ITEM, DESCRIPTOR> otherElement =
                   (CoreAttributeGroup<ATTRIBUTE_ITEM, DESCRIPTOR>)
                       otherItemEntry.getValue().getGroups().get(element.getType());
               if (!element.isSupersetOf(otherElement)) {
                 return false;
               }
             }
           }
           if (item.getKeyGroups() != null) {
             if (otherItemEntry.getValue().getKeyGroups() == null) {
               return true;
             }
             for (Object next : item.getKeyGroups().values()) {
               CoreAttributeGroup element = (CoreAttributeGroup) next;
               CoreAttributeGroup otherElement =
                   (CoreAttributeGroup)
                       otherItemEntry.getValue().getKeyGroups().get(element.getType());
               if (!element.isSupersetOf(otherElement)) {
                 return false;
               }
             }
           }
         }
         return true;
       } else {
         return true;
       }
     } else {
       if (anotherGroup.hasItems()) {
         return false;
       } else {
         return true;
       }
     }
   } else {
     return true;
   }
 }
 /**
  * INTERNAL: This method is used internally in the clone processing.
  *
  * @param cloneMap
  * @return
  */
 public CoreAttributeGroup clone(
     Map<
             CoreAttributeGroup<ATTRIBUTE_ITEM, DESCRIPTOR>,
             CoreAttributeGroup<ATTRIBUTE_ITEM, DESCRIPTOR>>
         cloneMap) {
   CoreAttributeGroup clone = cloneMap.get(this);
   if (clone != null) {
     return clone;
   }
   try {
     clone = (CoreAttributeGroup) super.clone();
   } catch (CloneNotSupportedException e) {
     throw new AssertionError(e);
   }
   clone.name = this.name;
   clone.type = this.type;
   clone.typeName = this.typeName;
   clone.isValidated = this.isValidated;
   cloneMap.put(this, clone);
   if (this.allsubclasses != null) {
     for (CoreAttributeGroup group : this.allsubclasses.values()) {
       clone.getSubClassGroups().put(group.getType(), group.clone(cloneMap));
     }
   }
   if (this.superClassGroup != null) {
     clone.superClassGroup = this.superClassGroup.clone(cloneMap);
   }
   if (this.subClasses != null) {
     clone.subClasses = new HashSet<CoreAttributeGroup>();
     for (CoreAttributeGroup group : this.subClasses) {
       clone.subClasses.add(group.clone(cloneMap));
     }
   }
   // all attributes and nested groups should be cloned, too
   clone.items = null;
   if (hasItems()) {
     clone.items = new HashMap<String, ATTRIBUTE_ITEM>();
     for (ATTRIBUTE_ITEM item : this.items.values()) {
       clone.items.put(item.getAttributeName(), item.clone(cloneMap, clone));
     }
   }
   return clone;
 }