/**
   * Finds nested XML schema definition and compiles it to a schema type system instance.
   *
   * @param wsdl
   * @return
   * @throws MojoExecutionException
   */
  private SchemaTypeSystem compileXsd(XmlObject wsdl) throws MojoExecutionException {
    // extract namespaces defined on wsdl-level:
    String[] namespacesWsdl = extractNamespacesOnWsdlLevel(wsdl);

    // calc the namespace-prefix of the schema-tag, default ""
    String schemaNsPrefix = extractSchemaNamespacePrefix(wsdl);

    // extract each schema-element and add missing namespaces defined on wsdl-level
    String[] schemas = getNestedSchemas(wsdl, namespacesWsdl, schemaNsPrefix);

    XmlObject[] xsd = new XmlObject[schemas.length];
    try {
      for (int i = 0; i < schemas.length; i++) {
        xsd[i] =
            XmlObject.Factory.parse(
                schemas[i],
                (new XmlOptions())
                    .setLoadLineNumbers()
                    .setLoadMessageDigest()
                    .setCompileDownloadUrls());
      }
    } catch (Exception e) {
      throw new MojoExecutionException("Failed to parse XSD schema", e);
    }

    SchemaTypeSystem schemaTypeSystem = null;
    try {
      schemaTypeSystem =
          XmlBeans.compileXsd(xsd, XmlBeans.getContextTypeLoader(), new XmlOptions());
    } catch (XmlException e) {
      for (Object error : e.getErrors()) {
        getLog().error("Line " + ((XmlError) error).getLine() + ": " + error.toString());
      }
      throw new MojoExecutionException("Failed to compile XSD schema", e);
    } catch (Exception e) {
      throw new MojoExecutionException("Failed to compile XSD schema", e);
    }
    return schemaTypeSystem;
  }
  @Override
  protected void checkMessageStructure() throws MessageValidationException {
    boolean messageStructureFailure = false;
    try {
      // Create a pseudo XML message
      XmlObject pseudoMessage = XmlObject.Factory.newInstance();
      XmlCursor pmCursor = pseudoMessage.newCursor();
      pmCursor.toNextToken();
      pmCursor.beginElement(profile.getMessageStructureID(), "urn:hl7-org:v2xml");
      BufferedReader br = new BufferedReader(new StringReader(message.getMessageAsString()));
      String line = null;
      while ((line = br.readLine()) != null) {
        if (!line.matches("\\s*")) {
          String fieldSep = ((Er7Message) message).getFieldSeparatorChar();
          try {
            int idx = line.indexOf(fieldSep);
            if (idx == -1 && line.length() <= 3) {
              idx = 3;
            }
            line = line.substring(0, idx);
            if (!line.startsWith("Z")) {
              pmCursor.beginElement(line, "urn:hl7-org:v2xml");
              pmCursor.toNextToken();
            }
          } catch (StringIndexOutOfBoundsException e) {
            if (line.length() > 3) {
              System.out.println(line);
            }
          }
        }
      }
      pmCursor.dispose();

      // Create a schema
      StreamSource xsltStream =
          new StreamSource(
              MessageStructureValidationV2Er7.class
                  .getClassLoader()
                  .getResourceAsStream(MessageValidationConstants.XSLT_CHECK_STRUCTURE));
      Transformer t = TransformerFactory.newInstance().newTransformer(xsltStream);
      t.setParameter("groups", "false");
      t.setParameter("xml", "false");

      StreamSource src = new StreamSource(profile.getDocument().newInputStream());
      ByteArrayOutputStream out = new ByteArrayOutputStream();
      t.transform(src, new StreamResult(out));
      XmlObject schemaDoc =
          XmlObject.Factory.parse(
              new ByteArrayInputStream(out.toByteArray()), (new XmlOptions()).setLoadLineNumbers());
      // pseudoMessage.save(new File("tmp/mu/PseudoMessage.xml"));
      // schemaDoc.save(new File("tmp/mu/Schema.xsd"));
      // Load the schema
      SchemaTypeLoader sLoader = null;
      Collection<Object> compErrors = new ArrayList<Object>();
      XmlOptions schemaOptions = new XmlOptions();
      schemaOptions.setErrorListener(compErrors);
      XmlObject[] schemas = new XmlObject[1];

      schemas[0] = schemaDoc;
      sLoader = XmlBeans.compileXsd(schemas, sLoader, schemaOptions);

      // Load the Message
      XmlObject xobj =
          sLoader.parse(pseudoMessage.toString(), null, (new XmlOptions()).setLoadLineNumbers());

      // Validate the Message against the schema
      Collection<XmlValidationError> errors = new ArrayList<XmlValidationError>();
      xobj.validate(new XmlOptions().setErrorListener(errors));
      Iterator<XmlValidationError> it = errors.iterator();
      while (it.hasNext()) {
        XmlValidationError xve = it.next();
        messageFailures.add(interpretSchemaError(xve));
        messageStructureFailure = true;
      }
    } catch (XmlException xmle) {
      // This type of exception is thrown when the generated schema is
      // ambiguous
      MessageFailureV2 mf = new MessageFailureV2(message.getEncoding());
      mf.setDescription(
          "The message validation can't be performed because the profile is ambiguous."
              + " Possible reasons for this problem include an ambiguous message definition"
              + " specified in the standard or an ambiguous message definition caused by the"
              + " user changing the Usage settings for segments during profile creation."
              + " Remember that a segment with the same name MUST be separated by at least one"
              + " non-optional segment with a different name.");
      mf.setFailureSeverity(ErrorSeverityConstants.FATAL);
      mf.setFailureType(AssertionTypeV2Constants.AMBIGUOUS_PROFILE);
      messageFailures.add(mf);
    } catch (Exception e) {
      throw new MessageValidationException(e.getMessage());
    } finally {
      if (!messageStructureFailure) {
        MessageFailureV2 mf = new MessageFailureV2(message.getEncoding());
        mf.setDescription("The message structure at the segment level is correct.");
        mf.setFailureSeverity(ErrorSeverityConstants.NORMAL);
        mf.setFailureType(AssertionTypeV2Constants.CHECKED);
        messageFailures.add(mf);
      }
    }
  }