@Override
  protected void processElement(final EjbJarMetaData ejbJarMetaData, XMLStreamReader reader)
      throws XMLStreamException {
    final EjbJarElement ejbJarElement = EjbJarElement.forName(reader.getLocalName());
    switch (ejbJarElement) {
      case MODULE_NAME:
        // only EJB 3.1 allows module-name
        if (ejbJarMetaData.isEJB31()) {
          String moduleName = getElementText(reader);
          ejbJarMetaData.setModuleName(moduleName);
        } else {
          throw unexpectedElement(reader);
        }
        break;

      case ENTERPRISE_BEANS:
        EnterpriseBeansMetaData enterpriseBeans =
            EnterpriseBeansMetaDataParser.parse(reader, ejbJarMetaData.getEjbJarVersion());
        if (enterpriseBeans != null) {
          // setup the bi-directional relationship
          enterpriseBeans.setEjbJarMetaData(ejbJarMetaData);
        }
        ejbJarMetaData.setEnterpriseBeans(enterpriseBeans);
        break;

      case INTERCEPTORS:
        // only applicable for EJB 3.x
        if (ejbJarMetaData.isEJB3x()) {
          InterceptorsMetaData intercpetors = InterceptorsMetaDataParser.INSTANCE.parse(reader);
          ejbJarMetaData.setInterceptors(intercpetors);
        } else {
          throw unexpectedElement(reader);
        }
        break;

      case RELATIONSHIPS:
        RelationsMetaData relations = RelationsMetaDataParser.parse(reader);
        ejbJarMetaData.setRelationships(relations);
        break;

      case ASSEMBLY_DESCRIPTOR:
        AssemblyDescriptorMetaDataParser assemblyDescriptorParser =
            AssemblyDescriptorMetaDataParserFactory.getParser(ejbJarMetaData.getEjbJarVersion());
        ejbJarMetaData.setAssemblyDescriptor(assemblyDescriptorParser.parse(reader));
        break;

      case EJB_CLIENT_JAR:
        String ejbClientJar = getElementText(reader);
        ejbJarMetaData.setEjbClientJar(ejbClientJar);
        break;

      default:
        Accessor<DescriptionGroupMetaData> accessor =
            new Accessor<DescriptionGroupMetaData>() {
              @Override
              public DescriptionGroupMetaData get() {
                DescriptionGroupMetaData descriptionGroup = ejbJarMetaData.getDescriptionGroup();
                if (descriptionGroup == null) {
                  descriptionGroup = new DescriptionGroupMetaData();
                  ejbJarMetaData.setDescriptionGroup(descriptionGroup);
                }
                return descriptionGroup;
              }
            };
        if (DescriptionGroupMetaDataParser.parse(reader, accessor)) break;
        throw unexpectedElement(reader);
    }
  }
  public static WebMetaData parse(
      XMLStreamReader reader, DTDInfo info, boolean validation, PropertyReplacer propertyReplacer)
      throws XMLStreamException {
    if (reader == null) throw new IllegalArgumentException("Null reader");
    if (info == null) throw new IllegalArgumentException("Null info");

    reader.require(START_DOCUMENT, null, null);

    // Read until the first start element
    while (reader.hasNext() && reader.next() != START_ELEMENT) ;

    String schemaLocation = readSchemaLocation(reader);

    Version version = null;
    if (info.getPublicID() != null) {
      version = Version.fromPublicID(info.getPublicID());
    }
    if (version == null && info.getSystemID() != null) {
      version = Version.fromSystemID(info.getSystemID());
    }
    if (version == null && schemaLocation != null) {
      version = Version.fromSystemID(schemaLocation);
    }
    if (version == null) {
      // Look at the version attribute
      String versionString = null;
      final int count = reader.getAttributeCount();
      for (int i = 0; i < count; i++) {
        if (attributeHasNamespace(reader, i)) {
          continue;
        }
        final Attribute attribute = Attribute.forName(reader.getAttributeLocalName(i));
        if (attribute == Attribute.VERSION) {
          versionString = reader.getAttributeValue(i);
        }
      }
      if ("2.4".equals(versionString)) {
        version = Version.SERVLET_2_4;
      } else if ("2.5".equals(versionString)) {
        version = Version.SERVLET_2_5;
      } else if ("3.0".equals(versionString)) {
        version = Version.SERVLET_3_0;
      }
    }

    if (version == null) version = Version.SERVLET_3_0;
    // throw new IllegalStateException("Cannot obtain servlet version");

    WebMetaData wmd = null;
    switch (version) {
      case SERVLET_2_2:
        wmd = new Web22MetaData();
        break;
      case SERVLET_2_3:
        wmd = new Web23MetaData();
        break;
      case SERVLET_2_4:
        wmd = new Web24MetaData();
        break;
      case SERVLET_2_5:
        wmd = new Web25MetaData();
        break;
      case SERVLET_3_0:
        wmd = new Web30MetaData();
        break;
    }

    // Set the publicId / systemId
    if (info != null) wmd.setDTD(info.getBaseURI(), info.getPublicID(), info.getSystemID());

    // Set the schema location if we have one
    if (schemaLocation != null) wmd.setSchemaLocation(schemaLocation);

    // Handle attributes
    final int count = reader.getAttributeCount();
    for (int i = 0; i < count; i++) {
      final String value = reader.getAttributeValue(i);
      if (attributeHasNamespace(reader, i)) {
        continue;
      }
      final Attribute attribute = Attribute.forName(reader.getAttributeLocalName(i));
      switch (attribute) {
        case ID:
          {
            wmd.setId(value);
            break;
          }
        case VERSION:
          {
            wmd.setVersion(value);
            break;
          }
        case METADATA_COMPLETE:
          {
            if (wmd instanceof Web25MetaData || wmd instanceof Web30MetaData) {
              if (Boolean.TRUE.equals(Boolean.valueOf(value))) {
                if (wmd instanceof Web25MetaData) {
                  ((Web25MetaData) wmd).setMetadataComplete(true);
                }
                if (wmd instanceof Web30MetaData) {
                  ((Web30MetaData) wmd).setMetadataComplete(true);
                }
              }
            } else {
              throw unexpectedAttribute(reader, i);
            }
            break;
          }
        default:
          throw unexpectedAttribute(reader, i);
      }
    }

    DescriptionGroupMetaData descriptionGroup = new DescriptionGroupMetaData();
    EnvironmentRefsGroupMetaData env = new EnvironmentRefsGroupMetaData();
    // Handle elements
    while (reader.hasNext() && reader.nextTag() != END_ELEMENT) {
      if (WebCommonMetaDataParser.parse(reader, wmd, propertyReplacer)) {
        continue;
      }
      if (EnvironmentRefsGroupMetaDataParser.parse(reader, env, propertyReplacer)) {
        if (wmd.getJndiEnvironmentRefsGroup() == null) {
          wmd.setJndiEnvironmentRefsGroup(env);
        }
        continue;
      }
      if (DescriptionGroupMetaDataParser.parse(reader, descriptionGroup)) {
        if (wmd.getDescriptionGroup() == null) {
          wmd.setDescriptionGroup(descriptionGroup);
        }
        continue;
      }
      final Element element = Element.forName(reader.getLocalName());
      switch (element) {
        case ABSOLUTE_ORDERING:
          if (wmd instanceof Web30MetaData) {
            ((Web30MetaData) wmd)
                .setAbsoluteOrdering(
                    AbsoluteOrderingMetaDataParser.parse(reader, propertyReplacer));
          } else {
            throw unexpectedElement(reader);
          }
          break;
        case MODULE_NAME:
          if (wmd instanceof Web30MetaData) {
            ((Web30MetaData) wmd).setModuleName(getElementText(reader, propertyReplacer));
          } else {
            throw unexpectedElement(reader);
          }
          break;
        case TAGLIB:
          if (wmd instanceof Web22MetaData || wmd instanceof Web23MetaData) {
            JspConfigMetaData jspConfig = wmd.getJspConfig();
            if (jspConfig == null) {
              jspConfig = new JspConfigMetaData();
              wmd.setJspConfig(jspConfig);
            }
            List<TaglibMetaData> taglibs = jspConfig.getTaglibs();
            if (taglibs == null) {
              taglibs = new ArrayList<TaglibMetaData>();
              jspConfig.setTaglibs(taglibs);
            }
            taglibs.add(TaglibMetaDataParser.parse(reader, propertyReplacer));
          } else {
            throw unexpectedElement(reader);
          }
          break;
        default:
          throw unexpectedElement(reader);
      }
    }

    return wmd;
  }