@SuppressWarnings("PMD.AvoidReassigningParameters") // makes code simpler
  private NeutralSchema parseComplexType(
      XmlSchemaComplexType schemaComplexType, NeutralSchema complexSchema, XmlSchema schema) {

    if ((schemaComplexType.getContentModel() != null)
        && (schemaComplexType.getContentModel().getContent() != null)) {
      XmlSchemaContent content = schemaComplexType.getContentModel().getContent();
      if (content instanceof XmlSchemaComplexContentExtension) {
        XmlSchemaComplexContentExtension schemaComplexContent =
            (XmlSchemaComplexContentExtension) content;
        XmlSchemaComplexType complexBaseType = getComplexBaseType(schemaComplexContent, schema);
        if (complexBaseType != null) {
          complexSchema = parseComplexType(complexBaseType, complexSchema, schema);
        }
        this.parseFields(schemaComplexContent, complexSchema, schema);

      } else if (content instanceof XmlSchemaSimpleContentExtension) {
        QName baseTypeName = ((XmlSchemaSimpleContentExtension) content).getBaseTypeName();
        NeutralSchema simpleContentSchema = schemaFactory.createSchema(baseTypeName);
        complexSchema.addField(complexSchema.getType(), simpleContentSchema);

        parseAttributes(
            ((XmlSchemaSimpleContentExtension) content).getAttributes(), complexSchema, schema);
      }
    }

    // Annotations are inherited by ComplexType fields so we need to parse these first.
    parseAnnotations(complexSchema, schemaComplexType);

    this.parseFields(schemaComplexType, complexSchema, schema);

    // Check for ChoiceSchemas. We only support complex types that contain choice if choice is
    // the ONLY element. If we find one, swap out the current ComplexSchema object that contains
    // this single choice schema for the actual choice schema itself.
    for (NeutralSchema ns : complexSchema.getFields().values()) {
      if (ns instanceof ChoiceSchema) {
        if (complexSchema.getFields().size() > 1 && !mergedChoiceSchemas.contains(ns)) {
          throw new RuntimeException(
              "Choice elements are only supported on complex objects with no other fields: "
                  + schemaComplexType.getName());
        } else if (!mergedChoiceSchemas.contains(ns)) {
          ns.setType(complexSchema.getType());
          complexSchema.getAppInfo();
          mergedChoiceSchemas.add(ns);
          if (ns.getType().equals("serviceDescriptorType")) {
            ns.addAnnotation(complexSchema.getAppInfo());
          }
          return ns;
        }
      }
    }

    return complexSchema;
  }
  private NeutralSchema parseSimpleType(
      XmlSchemaSimpleType schemaSimpleType, XmlSchema schema, String name) {

    NeutralSchema ns = recursiveParseSimpleType(schemaSimpleType, schema);
    if (schemaSimpleType.getName() == null) {
      // Type defined in-line. Need to capture it in the element schema set.
      int i = 1;
      NeutralSchema existing = elementSchemas.get(name + i);
      while (existing != null && !schemasEqual(ns, existing)) {
        i++;
        existing = schemas.get(name + i);
      }
      ns.setType(name + i);
      elementSchemas.put(name + i, ns);
    }
    return ns;
  }
  private NeutralSchema recursiveParseSimpleType(
      XmlSchemaSimpleType schemaSimpleType, XmlSchema schema) {
    NeutralSchema simpleSchema = null;

    String simpleTypeName = schemaSimpleType.getName();

    if (NeutralSchemaType.isPrimitive(schemaSimpleType.getQName())) {
      simpleSchema = getSchemaFactory().createSchema(schemaSimpleType.getQName());

    } else if (NeutralSchemaType.exists(schemaSimpleType.getBaseSchemaTypeName())) {

      if (NeutralSchemaType.isPrimitive(schemaSimpleType.getBaseSchemaTypeName())) {
        simpleSchema = getSchemaFactory().createSchema(schemaSimpleType.getBaseSchemaTypeName());

      } else {
        XmlSchemaSimpleType simpleBaseType =
            getSimpleBaseType(schemaSimpleType.getBaseSchemaTypeName(), schema);
        if (simpleBaseType != null) {

          if (simpleTypeName == null) {
            simpleTypeName = simpleBaseType.getName();
          }
          simpleSchema = getSchemaFactory().createSchema(simpleTypeName);
        }
      }

    } else if (schemaSimpleType.getContent() != null
        && schemaSimpleType.getContent() instanceof XmlSchemaSimpleTypeList) {

      ListSchema listSchema = (ListSchema) getSchemaFactory().createSchema("list");

      XmlSchemaSimpleTypeList content = (XmlSchemaSimpleTypeList) schemaSimpleType.getContent();
      NeutralSchema listContentSchema = null;

      if (content.getItemType() != null) {
        listContentSchema = parseSimpleType(content.getItemType(), schema, null);

      } else {
        QName itemTypeName = content.getItemTypeName();
        listContentSchema = getSchemaFactory().createSchema(itemTypeName.getLocalPart());
      }
      listSchema.getList().add(listContentSchema);
      listSchema.updateAnnotations();
      return listSchema;

    } else if (getSimpleContentTypeName(schemaSimpleType) != null) {

      if (NeutralSchemaType.isPrimitive(getSimpleContentTypeName(schemaSimpleType))) {
        simpleSchema = getSchemaFactory().createSchema(getSimpleContentTypeName(schemaSimpleType));

      } else {

        XmlSchemaSimpleType simpleBaseType =
            getSimpleBaseType(getSimpleContentTypeName(schemaSimpleType), schema);
        simpleSchema = recursiveParseSimpleType(simpleBaseType, schema);
      }
    }

    if (simpleSchema != null && schemaSimpleType.getContent() != null) {

      if (schemaSimpleType.getContent() instanceof XmlSchemaSimpleTypeRestriction) {

        XmlSchemaSimpleTypeRestriction simpleRestrictedContent =
            (XmlSchemaSimpleTypeRestriction) schemaSimpleType.getContent();
        XmlSchemaObjectCollection facets = simpleRestrictedContent.getFacets();
        List<String> tokens = new ArrayList<String>();
        for (int i = 0; i < facets.getCount(); i++) {
          XmlSchemaObject facetObject = facets.getItem(i);

          if (facetObject instanceof XmlSchemaEnumerationFacet) {
            XmlSchemaEnumerationFacet enumerationFacet = (XmlSchemaEnumerationFacet) facetObject;
            tokens.add(enumerationFacet.getValue().toString());
          } else if (facetObject instanceof XmlSchemaFacet) {
            XmlSchemaFacet facet = (XmlSchemaFacet) facetObject;
            String facetPropertyName = NeutralSchemaType.lookupPropertyName(facet);
            simpleSchema.getProperties().put(facetPropertyName, facet.getValue().toString());
          }
        }

        if (tokens.size() > 0) {
          // Token Schema
          Collections.sort(tokens); // sort so we can binary search
          simpleSchema.getProperties().put(TokenSchema.TOKENS, tokens);
        }
      }
    }

    parseAnnotations(simpleSchema, schemaSimpleType);

    if ((simpleSchema != null) && (simpleTypeName != null)) {
      simpleSchema.setType(simpleTypeName);
    }

    return simpleSchema;
  }