/**
   * Convenience method for building a Map of package to classes.
   *
   * @return
   */
  private Map<String, ArrayList<JavaClass>> buildPackageToJavaClassMap() {
    Map<String, ArrayList<JavaClass>> theMap = new HashMap<String, ArrayList<JavaClass>>();
    Map<String, ArrayList<JavaClass>> xmlBindingsMap = new HashMap<String, ArrayList<JavaClass>>();

    XmlBindings xmlBindings;
    for (String packageName : xmlBindingMap.keySet()) {
      xmlBindings = xmlBindingMap.get(packageName);
      ArrayList classes = new ArrayList<JavaClass>();
      // add binding classes - the Java Model will be used to get a
      // JavaClass via class name
      JavaTypes jTypes = xmlBindings.getJavaTypes();
      if (jTypes != null) {
        for (JavaType javaType : jTypes.getJavaType()) {
          classes.add(jModelInput.getJavaModel().getClass(javaType.getName()));
        }
      }

      // add any enum types to the class list
      XmlEnums xmlEnums = xmlBindings.getXmlEnums();
      if (xmlEnums != null) {
        for (XmlEnum xmlEnum : xmlEnums.getXmlEnum()) {
          classes.add(jModelInput.getJavaModel().getClass(xmlEnum.getJavaEnum()));
        }
      }

      theMap.put(packageName, classes);
      xmlBindingsMap.put(packageName, new ArrayList(classes));
    }

    // add any other classes that aren't declared via external metadata
    for (JavaClass jClass : jModelInput.getJavaClasses()) {
      // need to verify that the class isn't already in the bindings file list
      String pkg = jClass.getPackageName();
      ArrayList<JavaClass> existingXmlBindingsClasses = xmlBindingsMap.get(pkg);
      ArrayList<JavaClass> allExistingClasses = theMap.get(pkg);
      if (existingXmlBindingsClasses != null) {
        if (!classExistsInArray(jClass, existingXmlBindingsClasses)) {
          allExistingClasses.add(jClass);
        }
      } else {
        if (allExistingClasses != null) {
          allExistingClasses.add(jClass);
        } else {
          ArrayList classes = new ArrayList<JavaClass>();
          classes.add(jClass);
          theMap.put(pkg, classes);
        }
      }
    }

    return theMap;
  }
  /**
   * Process XmlBindings on a per package basis for a given AnnotationsPorcessor instance.
   *
   * @param annotationsProcessor
   */
  public void processXML(
      AnnotationsProcessor annotationsProcessor,
      JavaModelInput jModelInput,
      TypeMappingInfo[] typeMappingInfos,
      JavaClass[] originalJavaClasses) {
    this.jModelInput = jModelInput;
    this.aProcessor = annotationsProcessor;
    Map<String, XmlEnum> xmlEnumMap = new HashMap<String, XmlEnum>();
    annotationsProcessor.init(originalJavaClasses, typeMappingInfos);

    // build a map of packages to JavaClass so we only process the
    // JavaClasses for a given package additional classes - i.e. ones from
    // packages not listed in XML - will be processed later
    Map<String, ArrayList<JavaClass>> pkgToClassMap = buildPackageToJavaClassMap();

    // process each XmlBindings in the map
    XmlBindings xmlBindings;
    for (String packageName : xmlBindingMap.keySet()) {
      ArrayList classesToProcess = pkgToClassMap.get(packageName);
      if (classesToProcess == null) {
        getLogger()
            .logWarning("jaxb_metadata_warning_no_classes_to_process", new Object[] {packageName});
        continue;
      }

      xmlBindings = xmlBindingMap.get(packageName);

      // handle @XmlSchema override
      NamespaceInfo nsInfo = processXmlSchema(xmlBindings, packageName);
      if (nsInfo != null) {
        annotationsProcessor.addPackageToNamespaceMapping(packageName, nsInfo);
      }

      // build an array of JavaModel classes to process
      JavaClass[] javaClasses =
          (JavaClass[]) classesToProcess.toArray(new JavaClass[classesToProcess.size()]);

      // handle xml-enums
      // build a map of enum class names to XmlEnum objects
      XmlEnums xmlEnums = xmlBindings.getXmlEnums();
      if (xmlEnums != null) {
        for (XmlEnum xmlEnum : xmlEnums.getXmlEnum()) {
          xmlEnumMap.put(xmlEnum.getJavaEnum(), xmlEnum);
        }
      }

      // pre-build the TypeInfo objects
      Map<String, TypeInfo> typeInfoMap = annotationsProcessor.preBuildTypeInfo(javaClasses);

      // handle package-level xml-schema-types
      List<XmlSchemaType> xmlSchemaTypes = null;
      XmlSchemaTypes sTypes = xmlBindings.getXmlSchemaTypes();
      if (sTypes != null) {
        xmlSchemaTypes = sTypes.getXmlSchemaType();
      } else {
        xmlSchemaTypes = new ArrayList<XmlSchemaType>();
      }
      // handle package-level xml-schema-type
      if (xmlBindings.getXmlSchemaType() != null) {
        xmlSchemaTypes.add(xmlBindings.getXmlSchemaType());
      }
      // process each xml-schema-type entry
      for (XmlSchemaType sType : xmlSchemaTypes) {
        JavaClass jClass = aProcessor.getHelper().getJavaClass(sType.getType());
        if (jClass != null) {
          aProcessor.processSchemaType(
              sType.getName(), sType.getNamespace(), jClass.getQualifiedName());
        }
      }

      nsInfo = annotationsProcessor.getPackageToNamespaceMappings().get(packageName);

      JavaTypes jTypes = xmlBindings.getJavaTypes();
      if (jTypes != null) {
        for (JavaType javaType : jTypes.getJavaType()) {
          TypeInfo info = typeInfoMap.get(javaType.getName());

          // package/class override order:
          // 1 - xml class-level
          // 2 - java object class-level
          // 3 - xml package-level
          // 4 - package-info.java

          // handle class-level @XmlJavaTypeAdapter override
          if (javaType.getXmlJavaTypeAdapter() != null) {
            info.setXmlJavaTypeAdapter(javaType.getXmlJavaTypeAdapter());
          }

          // handle class-level @XmlAccessorOrder override
          if (javaType.isSetXmlAccessorOrder()) {
            info.setXmlAccessOrder(javaType.getXmlAccessorOrder());
          } else if (!info.isSetXmlAccessOrder()) {
            // handle package-level @XmlAccessorOrder override
            if (xmlBindings.isSetXmlAccessorOrder()) {
              info.setXmlAccessOrder(xmlBindings.getXmlAccessorOrder());
            } else {
              // finally, check the NamespaceInfo
              info.setXmlAccessOrder(nsInfo.getAccessOrder());
            }
          }

          // handle class-level @XmlAccessorType override
          if (javaType.isSetXmlAccessorType()) {
            info.setXmlAccessType(javaType.getXmlAccessorType());
          } else if (!info.isSetXmlAccessType()) {
            if (xmlBindings.isSetXmlAccessorType()) {
              // handle package-level @XmlAccessorType override
              info.setXmlAccessType(xmlBindings.getXmlAccessorType());
            } else {
              // finally, check the NamespaceInfo
              info.setXmlAccessType(nsInfo.getAccessType());
            }
          }

          // handle @XmlInlineBinaryData override
          if (javaType.isSetXmlInlineBinaryData()) {
            info.setInlineBinaryData(javaType.isXmlInlineBinaryData());
          }

          // handle @XmlTransient override
          if (javaType.isSetXmlTransient()) {
            info.setXmlTransient(javaType.isXmlTransient());
          }
          // handle @XmlRootElement
          if (javaType.getXmlRootElement() != null) {
            info.setXmlRootElement(javaType.getXmlRootElement());
          }
          // handle @XmlSeeAlso override
          if (javaType.getXmlSeeAlso() != null && javaType.getXmlSeeAlso().size() > 0) {
            info.setXmlSeeAlso(javaType.getXmlSeeAlso());
          }
          // handle @XmlType override
          if (javaType.getXmlType() != null) {
            info.setXmlType(javaType.getXmlType());
          }
          // handle @XmlCustomizer override
          if (javaType.getXmlCustomizer() != null) {
            info.setXmlCustomizer(javaType.getXmlCustomizer());
          }
        }
      }

      // apply package-level @XmlJavaTypeAdapters
      if (xmlBindings.getXmlJavaTypeAdapters() != null) {
        Map<String, TypeInfo> typeInfos = aProcessor.getTypeInfosForPackage(packageName);
        for (TypeInfo tInfo : typeInfos.values()) {
          List<XmlJavaTypeAdapter> adapters =
              xmlBindings.getXmlJavaTypeAdapters().getXmlJavaTypeAdapter();
          for (XmlJavaTypeAdapter xja : adapters) {
            JavaClass adapterClass = jModelInput.getJavaModel().getClass(xja.getValue());
            JavaClass boundType = jModelInput.getJavaModel().getClass(xja.getType());
            if (boundType != null) {
              tInfo.addPackageLevelAdapterClass(adapterClass, boundType);
            }
          }
        }
      }

      // post-build the TypeInfo objects
      javaClasses = annotationsProcessor.postBuildTypeInfo(javaClasses);

      // now trigger the annotations processor to process the classes
      annotationsProcessor.processJavaClasses(javaClasses);

      // get the generated TypeInfo
      Map<String, TypeInfo> typeInfosForPackage =
          annotationsProcessor.getTypeInfosForPackage(packageName);

      // update xml-enum info if necessary
      for (String key : typeInfosForPackage.keySet()) {
        TypeInfo tInfo = typeInfosForPackage.get(key);
        if (tInfo.isEnumerationType()) {
          EnumTypeInfo etInfo = (EnumTypeInfo) tInfo;
          XmlEnum xmlEnum = xmlEnumMap.get(etInfo.getClassName());
          if (xmlEnum != null) {
            JavaClass restrictionClass = aProcessor.getHelper().getJavaClass(xmlEnum.getValue());
            // default to String if necessary
            if (restrictionClass == null) {
              restrictionClass = jModelInput.getJavaModel().getClass(String.class);
            }
            etInfo.setRestrictionBase(aProcessor.getSchemaTypeFor(restrictionClass));
            for (XmlEnumValue xmlEnumValue : xmlEnum.getXmlEnumValue()) {
              // overwrite any existing entries (from annotations)
              etInfo.addJavaFieldToXmlEnumValuePair(
                  true, xmlEnumValue.getJavaEnumValue(), xmlEnumValue.getValue());
            }
          }
        }
      }

      // update TypeInfo objects based on the JavaTypes
      jTypes = xmlBindings.getJavaTypes();
      if (jTypes != null) {
        for (JavaType javaType : jTypes.getJavaType()) {
          processJavaType(javaType, typeInfosForPackage.get(javaType.getName()), nsInfo);
        }
      }
      // remove the entry for this package from the map
      pkgToClassMap.remove(packageName);
    }

    // now process remaining classes
    Iterator<ArrayList<JavaClass>> classIt = pkgToClassMap.values().iterator();
    while (classIt.hasNext()) {
      ArrayList<JavaClass> jClassList = classIt.next();
      JavaClass[] jClassArray = (JavaClass[]) jClassList.toArray(new JavaClass[jClassList.size()]);
      annotationsProcessor.buildNewTypeInfo(jClassArray);
      annotationsProcessor.processJavaClasses(jClassArray);
    }

    // need to ensure that any bound types (from XmlJavaTypeAdapter) have
    // TypeInfo objects built for them - SchemaGenerator will require a
    // descriptor for each
    Map<String, TypeInfo> typeInfos = (Map<String, TypeInfo>) aProcessor.getTypeInfo().clone();
    for (String key : typeInfos.keySet()) {
      JavaClass[] jClassArray;
      TypeInfo tInfo = typeInfos.get(key);
      for (Property prop : tInfo.getPropertyList()) {
        if (prop.isSetXmlJavaTypeAdapter()) {
          jClassArray = new JavaClass[] {prop.getActualType()};
          aProcessor.buildNewTypeInfo(jClassArray);
        }
      }
    }
    aProcessor.finalizeProperties();
    aProcessor.createElementsForTypeMappingInfo();
  }