/**
   * Build the JAXB model and generate the schemas based on tha data
   *
   * @param extraFiles additional files.
   * @return class to {@link QName} resolver.
   */
  private Resolver buildModelAndSchemas(
      Map<String, ApplicationDescription.ExternalGrammar> extraFiles) {

    // Lets get all candidate classes so we can create the JAX-B context
    // include any @XmlSeeAlso references.

    Set<Class> classSet = new HashSet<Class>(seeAlsoClasses);

    for (TypeCallbackPair pair : nameCallbacks) {
      final GenericType genericType = pair.genericType;
      Class<?> clazz = genericType.getRawType();

      // Is this class itself interesting?

      if (clazz.getAnnotation(XmlRootElement.class) != null) {
        classSet.add(clazz);
      } else if (SPECIAL_GENERIC_TYPES.contains(clazz)) {

        Type type = genericType.getType();
        if (type instanceof ParameterizedType) {
          Type parameterType = ((ParameterizedType) type).getActualTypeArguments()[0];
          if (parameterType instanceof Class) {
            classSet.add((Class) parameterType);
          }
        }
      }
    }

    // Create a JAX-B context, and use this to generate us a bunch of
    // schema objects

    JAXBIntrospector introspector = null;

    try {
      JAXBContext context = JAXBContext.newInstance(classSet.toArray(new Class[classSet.size()]));

      final List<StreamResult> results = new ArrayList<StreamResult>();

      context.generateSchema(
          new SchemaOutputResolver() {

            int counter = 0;

            @Override
            public Result createOutput(String namespaceUri, String suggestedFileName) {
              StreamResult result = new StreamResult(new CharArrayWriter());
              result.setSystemId("xsd" + (counter++) + ".xsd");
              results.add(result);
              return result;
            }
          });

      // Store the new files for later use
      //

      for (StreamResult result : results) {
        CharArrayWriter writer = (CharArrayWriter) result.getWriter();
        byte[] contents = writer.toString().getBytes("UTF8");
        extraFiles.put(
            result.getSystemId(),
            new ApplicationDescription.ExternalGrammar(
                MediaType
                    .APPLICATION_XML_TYPE, // I don't think there is a specific media type for XML
                                           // Schema
                contents));
      }

      // Create an introspector
      //

      introspector = context.createJAXBIntrospector();

    } catch (JAXBException e) {
      LOGGER.log(Level.SEVERE, "Failed to generate the schema for the JAX-B elements", e);
    } catch (IOException e) {
      LOGGER.log(
          Level.SEVERE,
          "Failed to generate the schema for the JAX-B elements due to an IO error",
          e);
    }

    // Create introspector

    if (introspector != null) {
      final JAXBIntrospector copy = introspector;

      return new Resolver() {

        public QName resolve(Class type) {

          Object parameterClassInstance = null;
          try {
            Constructor<?> defaultConstructor = type.getDeclaredConstructor();
            defaultConstructor.setAccessible(true);
            parameterClassInstance = defaultConstructor.newInstance();
          } catch (InstantiationException ex) {
            LOGGER.log(Level.FINE, null, ex);
          } catch (IllegalAccessException ex) {
            LOGGER.log(Level.FINE, null, ex);
          } catch (IllegalArgumentException ex) {
            LOGGER.log(Level.FINE, null, ex);
          } catch (InvocationTargetException ex) {
            LOGGER.log(Level.FINE, null, ex);
          } catch (SecurityException ex) {
            LOGGER.log(Level.FINE, null, ex);
          } catch (NoSuchMethodException ex) {
            LOGGER.log(Level.FINE, null, ex);
          }

          if (parameterClassInstance == null) {
            return null;
          }

          try {
            return copy.getElementName(parameterClassInstance);
          } catch (NullPointerException e) {
            // EclipseLink throws an NPE if an object annotated with @XmlType and without the
            // @XmlRootElement
            // annotation is passed as a parameter of #getElementName method.
            return null;
          }
        }
      };
    } else {
      return null; // No resolver created
    }
  }