private void addType(Type cls, boolean allowArray) {
    if (cls instanceof Class) {
      if (globalAdapters.contains(cls)) {
        return;
      }
      if (((Class<?>) cls).isArray() && !allowArray) {
        addClass(((Class<?>) cls).getComponentType());
      } else {
        addClass((Class<?>) cls);
      }
    } else if (cls instanceof ParameterizedType) {
      final ParameterizedType parameterizedType = (ParameterizedType) cls;
      addType(parameterizedType.getRawType());
      if (!parameterizedType.getRawType().equals(Enum.class)) {
        for (Type t2 : parameterizedType.getActualTypeArguments()) {
          if (shouldTypeBeAdded(t2, parameterizedType)) {
            addType(t2);
          }
        }
      }
    } else if (cls instanceof GenericArrayType) {
      Class<?> ct;
      GenericArrayType gt = (GenericArrayType) cls;
      Type componentType = gt.getGenericComponentType();
      if (componentType instanceof Class) {
        ct = (Class<?>) componentType;
      } else {
        TypeVariable<?> tv = (TypeVariable<?>) componentType;
        Type[] bounds = tv.getBounds();
        if (bounds != null && bounds.length == 1) {
          if (bounds[0] instanceof Class) {
            ct = (Class<?>) bounds[0];
          } else {
            throw new IllegalArgumentException("Unable to determine type for: " + tv);
          }
        } else {
          throw new IllegalArgumentException("Unable to determine type for: " + tv);
        }
      }
      ct = Array.newInstance(ct, 0).getClass();

      addClass(ct);
    } else if (cls instanceof WildcardType) {
      for (Type t : ((WildcardType) cls).getUpperBounds()) {
        addType(t);
      }
      for (Type t : ((WildcardType) cls).getLowerBounds()) {
        addType(t);
      }
    } else if (cls instanceof TypeVariable) {
      for (Type t : ((TypeVariable<?>) cls).getBounds()) {
        addType(t);
      }
    }
  }
  private void addClass(Class<?> cls) {
    if (Throwable.class.isAssignableFrom(cls)) {
      if (!Throwable.class.equals(cls) && !Exception.class.equals(cls)) {
        walkReferences(cls);
      }
      addClass(String.class);
    } else {
      cls = JAXBUtils.getValidClass(cls);
      if (null != cls) {
        if (classes.contains(cls)) {
          return;
        }

        if (!cls.isInterface()) {
          classes.add(cls);
        }

        XmlSeeAlso xsa = cls.getAnnotation(XmlSeeAlso.class);
        if (xsa != null) {
          for (Class<?> c : xsa.value()) {
            addClass(c);
          }
        }
        XmlJavaTypeAdapter xjta = cls.getAnnotation(XmlJavaTypeAdapter.class);
        if (xjta != null) {
          // has an adapter.   We need to inspect the adapter and then
          // return as the adapter will handle the superclass
          // and interfaces and such
          Type t = getTypeFromXmlAdapter(xjta);
          if (t != null) {
            addType(t);
          }
          return;
        }

        if (cls.getSuperclass() != null) {
          // JAXB should do this, but it doesn't always.
          // in particular, older versions of jaxb don't
          addClass(cls.getSuperclass());
        }

        if (!cls.isInterface()) {
          walkReferences(cls);
        }
      }
    }
  }
  @Override
  public void begin(MessagePartInfo part) {
    Class<?> clazz = part.getTypeClass();
    if (clazz == null) {
      return;
    }

    if (Exception.class.isAssignableFrom(clazz)) {
      // exceptions are handled special, make sure we mark it
      part.setProperty(JAXBDataBinding.class.getName() + ".CUSTOM_EXCEPTION", Boolean.TRUE);
    }
    boolean isFromWrapper = part.getMessageInfo().getOperation().isUnwrapped();
    if (isFromWrapper && !Boolean.TRUE.equals(part.getProperty("messagepart.isheader"))) {
      UnwrappedOperationInfo uop = (UnwrappedOperationInfo) part.getMessageInfo().getOperation();
      OperationInfo op = uop.getWrappedOperation();
      MessageInfo inf = null;
      if (uop.getInput() == part.getMessageInfo()) {
        inf = op.getInput();
      } else if (uop.getOutput() == part.getMessageInfo()) {
        inf = op.getOutput();
      }
      if (inf != null && inf.getMessagePart(0).getTypeClass() != null) {
        // if the wrapper has a type class, we don't need to do anything
        // as everything would have been discovered when walking the
        // wrapper type (unless it's a header which wouldn't be in the wrapper)
        return;
      }
    }
    if (isFromWrapper && clazz.isArray() && !Byte.TYPE.equals(clazz.getComponentType())) {
      clazz = clazz.getComponentType();
    }

    Annotation[] a = (Annotation[]) part.getProperty("parameter.annotations");
    checkForAdapter(clazz, a);

    Type genericType = (Type) part.getProperty("generic.type");
    if (genericType != null) {
      boolean isList = Collection.class.isAssignableFrom(clazz);
      if (isFromWrapper) {
        if (genericType instanceof Class && ((Class<?>) genericType).isArray()) {
          Class<?> cl2 = (Class<?>) genericType;
          if (cl2.isArray() && !Byte.TYPE.equals(cl2.getComponentType())) {
            genericType = cl2.getComponentType();
          }
          addType(genericType);
        } else if (!isList) {
          addType(genericType);
        }
      } else {
        addType(genericType, true);
      }

      if (isList && genericType instanceof ParameterizedType) {
        ParameterizedType pt = (ParameterizedType) genericType;
        if (pt.getActualTypeArguments().length > 0
            && pt.getActualTypeArguments()[0] instanceof Class) {

          Class<? extends Object> arrayCls =
              Array.newInstance((Class<?>) pt.getActualTypeArguments()[0], 0).getClass();
          clazz = arrayCls;
          part.setTypeClass(clazz);
          if (isFromWrapper) {
            addType(clazz.getComponentType(), true);
          }
        } else if (pt.getActualTypeArguments().length > 0
            && pt.getActualTypeArguments()[0] instanceof GenericArrayType) {
          GenericArrayType gat = (GenericArrayType) pt.getActualTypeArguments()[0];
          gat.getGenericComponentType();
          Class<? extends Object> arrayCls =
              Array.newInstance((Class<?>) gat.getGenericComponentType(), 0).getClass();
          clazz = Array.newInstance(arrayCls, 0).getClass();
          part.setTypeClass(clazz);
          if (isFromWrapper) {
            addType(clazz.getComponentType(), true);
          }
        }
      }
      if (isFromWrapper && isList) {
        clazz = null;
      }
    }
    if (clazz != null) {
      if (!isFromWrapper
          && clazz.getAnnotation(XmlRootElement.class) == null
          && clazz.getAnnotation(XmlType.class) != null
          && StringUtils.isEmpty(clazz.getAnnotation(XmlType.class).name())) {
        Object ref = JAXBClassLoaderUtils.createTypeReference(part.getName(), clazz);
        if (ref != null) {
          typeReferences.add(ref);
        }
      }

      addClass(clazz);
    }
  }
  void addClass(Class<?> claz) {
    if (Throwable.class.isAssignableFrom(claz)) {
      if (!Throwable.class.equals(claz) && !Exception.class.equals(claz)) {
        walkReferences(claz);
      }
      addClass(String.class);
    } else if (claz.getName().startsWith("java.") || claz.getName().startsWith("javax.")) {
      return;
    } else {
      Class<?> cls = JAXBUtils.getValidClass(claz);
      if (cls == null
          && ReflectionUtil.getDeclaredConstructors(claz).length > 0
          && !Modifier.isAbstract(claz.getModifiers())) {
        if (LOG.isLoggable(Level.INFO)) {
          LOG.info(
              "Class "
                  + claz.getName()
                  + " does not have a default constructor which JAXB requires.");
        }
        // there is no init(), but other constructors
        Object factory = createFactory(claz, ReflectionUtil.getDeclaredConstructors(claz)[0]);
        unmarshallerProperties.put("com.sun.xml.bind.ObjectFactory", factory);
        cls = claz;
      }
      if (null != cls) {
        if (classes.contains(cls)) {
          return;
        }

        if (!cls.isInterface()) {
          classes.add(cls);
        }

        XmlSeeAlso xsa = cls.getAnnotation(XmlSeeAlso.class);
        if (xsa != null) {
          for (Class<?> c : xsa.value()) {
            addClass(c);
          }
        }
        XmlJavaTypeAdapter xjta = cls.getAnnotation(XmlJavaTypeAdapter.class);
        if (xjta != null) {
          // has an adapter.   We need to inspect the adapter and then
          // return as the adapter will handle the superclass
          // and interfaces and such
          Type t = Utils.getTypeFromXmlAdapter(xjta);
          if (t != null) {
            addType(t);
          }
          return;
        }

        if (cls.getSuperclass() != null) {
          // JAXB should do this, but it doesn't always.
          // in particular, older versions of jaxb don't
          addClass(cls.getSuperclass());
        }

        if (!cls.isInterface()) {
          walkReferences(cls);
        }
      }
    }
  }