// Parser.cpp,v 1.36 2004/11/19 23:32:54 kitty Exp

#include "ACEXML/parser/parser/Parser.h"

#if !defined (__ACEXML_INLINE__)
# include "ACEXML/parser/parser/Parser.i"
#endif /* __ACEXML_INLINE__ */

#include "ace/ACE.h"
#include "ACEXML/common/Transcode.h"
#include "ACEXML/common/AttributesImpl.h"
#include "ACEXML/common/StrCharStream.h"
#include "ACEXML/common/StreamFactory.h"
#include "ACEXML/parser/parser/ParserInternals.h"
#include "ace/OS_NS_string.h"
#include "ace/OS_NS_strings.h"

static const ACEXML_Char default_attribute_type[] = ACE_TEXT ("CDATA");
static const ACEXML_Char empty_string[] = { 0 };

const ACEXML_Char
ACEXML_Parser::simple_parsing_feature_[] = ACE_TEXT ("Simple");

const ACEXML_Char
ACEXML_Parser::namespaces_feature_[] = ACE_TEXT ("http://xml.org/sax/features/namespaces");

const ACEXML_Char
ACEXML_Parser::namespace_prefixes_feature_[] = ACE_TEXT ("http://xml.org/sax/features/namespace-prefixes");

const ACEXML_Char
ACEXML_Parser::validation_feature_[] = ACE_TEXT ("http://xml.org/sax/features/validation");

ACEXML_Parser::ACEXML_Parser (void)
  :   dtd_handler_ (0),
      entity_resolver_ (0),
      content_handler_ (0),
      error_handler_ (0),
      doctype_ (0),
      current_ (0),
      alt_stack_ (MAXPATHLEN),
      nested_namespace_ (0),
      ref_state_ (ACEXML_ParserInt::INVALID),
      external_subset_ (0),
      external_entity_ (0),
      has_pe_refs_ (0),
      standalone_ (0),
      external_dtd_ (0),
      internal_dtd_ (0),
      simple_parsing_ (0),
      validate_ (1),
      namespaces_(1),
      namespace_prefixes_ (0)
{
}

ACEXML_Parser::~ACEXML_Parser (void)
{

}

int
ACEXML_Parser::initialize(ACEXML_InputSource* input)
{
  // Initialize namespace support
  if (this->xml_namespace_.init() == -1)
    {
      ACE_ERROR ((LM_ERROR,
                  ACE_TEXT ("Error initializing namespace support\n")));
      return -1;
    }
  for (int i = 0; i < 5; ++i)
    {
      if (this->predef_entities_.add_entity (ACEXML_ParserInt::predef_ent_[i],
                                             ACEXML_ParserInt::predef_val_[i])
          != 0)
        {
          ACE_ERROR ((LM_DEBUG,
                      ACE_TEXT ("Error adding entity %s to Manager\n"),
                      ACEXML_ParserInt::predef_ent_[i]));
          return -1;
        }
    }
  return this->switch_input (input, input->getSystemId());
}

void
ACEXML_Parser::parse (const ACEXML_Char *systemId ACEXML_ENV_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  ACEXML_InputSource* input = 0;
  ACE_NEW (input, ACEXML_InputSource (systemId));
  this->parse (input ACEXML_ENV_ARG_PARAMETER);
}

void
ACEXML_Parser::parse (ACEXML_InputSource *input ACEXML_ENV_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  if (input == 0)
    {
      this->fatal_error(ACE_TEXT ("Invalid input source")
                        ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK;
    }
  if (this->content_handler_ == 0)
    {
      this->fatal_error (ACE_TEXT ("No content handlers defined. Exiting..")
                         ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK;
    }

  if (this->validate_ && this->dtd_handler_ == 0)
    {
      this->fatal_error (ACE_TEXT ("No DTD handlers defined. Exiting..")
                         ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK;
    }


  if (this->initialize(input) == -1)
    {
      this->fatal_error (ACE_TEXT ("Failed to initialize parser state")
                         ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK;
    }
  // Set up Locator.
  this->content_handler_->setDocumentLocator (this->current_->getLocator());

  int xmldecl_defined = 0;
  ACEXML_Char fwd = this->get();  // Consume '<'
  if (fwd == '<' && this->peek() == '?')
    {
      this->get();      // Consume '?'
      fwd = this->peek();
      if (fwd == 'x' && !xmldecl_defined)
        {
          this->parse_xml_decl (ACEXML_ENV_SINGLE_ARG_PARAMETER);
          ACEXML_CHECK;
          xmldecl_defined = 1;
        }
    }
  // We need a XMLDecl in a Valid XML document
  if (this->validate_ && !xmldecl_defined)
    {
      this->fatal_error (ACE_TEXT ("Expecting an XMLDecl at the beginning of")
                         ACE_TEXT (" a valid document")
                         ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK;
    }
  this->content_handler_->startDocument (ACEXML_ENV_SINGLE_ARG_PARAMETER);
  ACEXML_CHECK;

  int doctype_defined = 0;
  for (int prolog_done = 0; prolog_done == 0; )
    {
      // Expect a '<' only if we have encountered a XMLDecl, or we are
      // looping through Misc blocks.
      if (xmldecl_defined)
        {
          if (this->skip_whitespace () != '<')
            {
              this->fatal_error (ACE_TEXT ("Expecting '<' at the beginning of ")
                                 ACE_TEXT ("Misc section")
                                 ACEXML_ENV_ARG_PARAMETER);
              ACEXML_CHECK;
            }
          fwd = this->peek();
        }
      switch (fwd)
        {
          case '?':
            this->get();
            this->parse_processing_instruction (ACEXML_ENV_SINGLE_ARG_PARAMETER);
            ACEXML_CHECK;
            xmldecl_defined = 1;
            break;
          case '!':
            this->get();
            fwd = this->peek ();
            if (fwd == 'D' && !doctype_defined)       // DOCTYPE
              {
                // This will also take care of the trailing MISC block if any.
                this->parse_doctypedecl (ACEXML_ENV_SINGLE_ARG_PARAMETER);
                ACEXML_CHECK;
                doctype_defined = 1;
                // Now that we have a DOCTYPE Decl defined, we shouldn't
                // accept XML Decl any longer
                xmldecl_defined = 1;
              }
            else if (fwd == 'D')
              {
                this->fatal_error (ACE_TEXT ("Duplicate DOCTYPE declaration")
                                   ACEXML_ENV_ARG_PARAMETER);
                ACEXML_CHECK;
              }
            else if (fwd == '-')  // COMMENT
              {
                if (this->parse_comment () < 0)
                  {
                    this->fatal_error(ACE_TEXT ("Invalid comment in document")
                                      ACEXML_ENV_ARG_PARAMETER);
                    ACEXML_CHECK;
                  }
                xmldecl_defined = 1;
              }
            break;
          case 0:
            this->fatal_error (ACE_TEXT ("Unexpected end-of-file")
                               ACEXML_ENV_ARG_PARAMETER);
            ACEXML_CHECK;
          default:                // Root element begins
            prolog_done = 1;
            break;
        }
    }

  if (this->validate_ && !doctype_defined)
    {
      this->warning (ACE_TEXT ("No doctypeDecl in valid document")
                     ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK;
    }

  // Now parse root element.
  this->parse_element (1 ACEXML_ENV_ARG_PARAMETER);
  ACEXML_CHECK;

  this->content_handler_->endDocument (ACEXML_ENV_SINGLE_ARG_PARAMETER);
  ACEXML_CHECK;

  // Reset the parser state
  this->reset();

}

int
ACEXML_Parser::parse_doctypedecl (ACEXML_ENV_SINGLE_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  if (this->parse_token (ACE_TEXT ("DOCTYPE")) < 0)
    {
      this->fatal_error(ACE_TEXT ("Expecting keyword DOCTYPE in a doctypedecl")
                        ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }

  ACEXML_Char nextch = 0;
  if (this->skip_whitespace_count (&nextch) == 0)
    {
      this->fatal_error(ACE_TEXT ("Expecting a space between DOCTYPE keyword ")
                        ACE_TEXT ("and name") ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }

  this->doctype_ = this->parse_name ();
  if (this->doctype_ == 0)
    {
      this->fatal_error(ACE_TEXT ("Invalid DOCTYPE name")
                        ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }
  int count = this->skip_whitespace_count (&nextch);

  if (nextch == 'S' || nextch == 'P') // ExternalID defined
    {
      if (count == 0)
        {
          this->fatal_error(ACE_TEXT ("Expecting a space between DOCTYPE")
                            ACE_TEXT ("keyword and name")
                            ACEXML_ENV_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
        }
      this->external_dtd_ = 1;
      this->parse_external_dtd (ACEXML_ENV_SINGLE_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }

  nextch = this->skip_whitespace ();
  switch (nextch)
    {
      case '[':
        this->internal_dtd_ = 1;  // Internal DTD definition
        this->parse_internal_dtd (ACEXML_ENV_SINGLE_ARG_PARAMETER);
        ACEXML_CHECK_RETURN (-1);
        break;
      case '>':                   // End of DTD definition
        // This is an XML document without a doctypedecl.
        if (this->validate_ && !this->external_dtd_)
          {
            this->fatal_error (ACE_TEXT ("No DTD defined")
                               ACEXML_ENV_ARG_PARAMETER);
            ACEXML_CHECK_RETURN (-1);
          }
        return 0;
      case '0':
        this->fatal_error (ACE_TEXT ("Unexpected end-of-file")
                           ACEXML_ENV_ARG_PARAMETER);
        ACEXML_CHECK_RETURN (-1);
      default:
        break;
    }

  if (this->skip_whitespace() != '>')
    {
      this->fatal_error(ACE_TEXT ("Expecting '>' at end of doctypedecl")
                        ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }
  return 0;
}

int
ACEXML_Parser::parse_internal_dtd (ACEXML_ENV_SINGLE_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  this->ref_state_ = ACEXML_ParserInt::IN_INT_DTD;
  ACEXML_Char nextch = this->skip_whitespace ();
  do {
    switch (nextch)
      {
        case '<':
          nextch = this->get();
          switch (nextch)
            {
              case '!':
                this->parse_markup_decl (ACEXML_ENV_SINGLE_ARG_PARAMETER);
                ACEXML_CHECK_RETURN (-1);
                break;
              case '?':
                this->parse_processing_instruction (ACEXML_ENV_SINGLE_ARG_PARAMETER);
                ACEXML_CHECK_RETURN (-1);
                break;
              default:
                this->fatal_error (ACE_TEXT ("Invalid internal subset")
                                   ACEXML_ENV_ARG_PARAMETER);
                ACEXML_CHECK_RETURN (-1);
                break;
            }
          break;
        case '%':
          this->has_pe_refs_ = 1;
          this->parse_PE_reference (ACEXML_ENV_SINGLE_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
          break;
        case ']':                 // End of internal definitions.
          return 0;
        case '&':
          this->fatal_error (ACE_TEXT ("Invalid Reference in internal DTD")
                             ACEXML_ENV_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
          break;
        case 0:
          this->pop_context (0 ACEXML_ENV_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
          break;
        default:
          this->fatal_error (ACE_TEXT ("Invalid content in internal subset")
                             ACEXML_ENV_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
      };
    nextch = this->skip_whitespace ();
  } while (1);

  ACE_NOTREACHED (return -1);
}

int
ACEXML_Parser::parse_external_dtd (ACEXML_ENV_SINGLE_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  this->ref_state_ = ACEXML_ParserInt::IN_EXT_DTD;
  ACEXML_Char* publicId = 0;
  ACEXML_Char* systemId = 0;
  if (this->parse_external_id (publicId, systemId
                               ACEXML_ENV_ARG_PARAMETER) != 0)
    {
      this->fatal_error (ACE_TEXT ("Error in parsing ExternalID")
                         ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }
  if (this->validate_)
    {
      ACEXML_Char* uri = this->normalize_systemid (systemId);
      ACE_Auto_Basic_Array_Ptr<ACEXML_Char> cleanup_uri (uri);
      ACEXML_InputSource* ip = 0;
      if (this->entity_resolver_)
        {
          ip = this->entity_resolver_->resolveEntity (publicId,
                                                      (uri ? uri : systemId)
                                                      ACEXML_ENV_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
        }
      if (ip)
        {
          if (this->switch_input (ip, (uri ? uri : systemId), publicId) != 0)
            return -1;
        }
      else
        {
          ACEXML_StreamFactory factory;
          ACEXML_CharStream* cstream = factory.create_stream (uri ?
                                                              uri: systemId);
          if (!cstream) {
            this->fatal_error (ACE_TEXT ("Invalid input source")
                               ACEXML_ENV_ARG_PARAMETER);
            ACEXML_CHECK_RETURN (-1);
          }
          if (this->switch_input (cstream, systemId, publicId) != 0)
            return -1;
        }
      this->parse_external_subset (ACEXML_ENV_SINGLE_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }
  return 0;
}


int
ACEXML_Parser::parse_external_subset (ACEXML_ENV_SINGLE_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  this->ref_state_ = ACEXML_ParserInt::IN_EXT_DTD;
  this->external_subset_ = 1;
  int nrelems = 0;
  ACEXML_Char nextch = this->skip_whitespace();
  do {
    switch (nextch)
      {
        case '<':
          nextch = this->get();
          switch (nextch)
            {
              case '!':
                nextch = this->peek();
                if (nextch == '[')
                  this->parse_conditional_section (ACEXML_ENV_SINGLE_ARG_PARAMETER);
                else
                  this->parse_markup_decl (ACEXML_ENV_SINGLE_ARG_PARAMETER);
                ACEXML_CHECK_RETURN (-1);
                break;
              case '?':
                nextch = this->peek();
                if (nextch == 'x')
                  this->parse_text_decl (ACEXML_ENV_SINGLE_ARG_PARAMETER);
                else
                  this->parse_processing_instruction (ACEXML_ENV_SINGLE_ARG_PARAMETER);
                ACEXML_CHECK_RETURN (-1);
                break;
              default:
                this->fatal_error (ACE_TEXT ("Invalid content in external DTD")
                                   ACEXML_ENV_ARG_PARAMETER);
                ACEXML_CHECK_RETURN (-1);
            }
          break;
        case '%':
          this->parse_PE_reference (ACEXML_ENV_SINGLE_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
          break;
        case 0:
          nrelems = this->pop_context (0 ACEXML_ENV_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
          if (nrelems == 1)
            return 0;
          break;
        default:
          this->fatal_error (ACE_TEXT ("Invalid content in external DTD")
                             ACEXML_ENV_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
      }
    nextch = this->skip_whitespace();
  } while (1);
}

int
ACEXML_Parser::parse_conditional_section (ACEXML_ENV_SINGLE_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  ACEXML_Char ch = this->get ();
  int include = 0;
  if (ch != '[')
    {
      this->fatal_error(ACE_TEXT ("Internal Parser Error")
                        ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }
  ch = this->skip_whitespace();
  if (ch == '%')
    {
      this->parse_PE_reference (ACEXML_ENV_SINGLE_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
      ch = this->skip_whitespace();
    }
  if (ch == 'I')
    {
      ch = this->get();
      switch (ch)
        {
          case 'N':
            if (this->parse_token (ACE_TEXT ("CLUDE")) < 0)
              {
                this->fatal_error (ACE_TEXT ("Expecting keyword INCLUDE in ")
                                   ACE_TEXT ("conditionalSect")
                                   ACEXML_ENV_ARG_PARAMETER);
                ACEXML_CHECK_RETURN (-1);
              }
            include = 1;
            break;
          case 'G':
            if (this->parse_token (ACE_TEXT ("GNORE")) < 0)
              {
                this->fatal_error (ACE_TEXT ("Expecting keyword IGNORE in ")
                                   ACE_TEXT ("conditionalSect")
                                   ACEXML_ENV_ARG_PARAMETER);
                ACEXML_CHECK_RETURN (-1);
              }
            include = 0;
            break;
          default:
            this->fatal_error (ACE_TEXT ("Invalid conditionalSect")
                               ACEXML_ENV_ARG_PARAMETER);
            ACEXML_CHECK_RETURN (-1);
        }
      ACEXML_Char fwd = '\xFF';
      this->skip_whitespace_count (&fwd);
      if (fwd == 0)
        {
          this->get(); // Consume the 0
          this->pop_context (0 ACEXML_ENV_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
        }
    }
  else
    {
      this->fatal_error (ACE_TEXT ("Invalid conditionalSect")
                         ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }
  if (this->skip_whitespace() != '[')
    {
      this->fatal_error (ACE_TEXT ("Expecting '[' in conditionalSect")
                         ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }
  if (include)
    this->parse_includesect (ACEXML_ENV_SINGLE_ARG_PARAMETER);
  else
    this->parse_ignoresect (ACEXML_ENV_SINGLE_ARG_PARAMETER);
  ACEXML_CHECK_RETURN (-1);
  return 0;
}

int
ACEXML_Parser::parse_ignoresect (ACEXML_ENV_SINGLE_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  ACEXML_Char nextch = this->skip_whitespace();
  int count = 0;
  int done = 0;
  do {
    switch (nextch)
      {
        case '<':
          if (this->peek() == '!')
            {
              this->get();
              if (this->peek() == '[')
                {
                  this->get();
                  count++;
                }
            }
          break;
        case ']':
          if (this->peek() == ']')
            {
              this->get();
              if (this->peek() == '>')
                {
                  this->get();
                  if (count)
                    {
                      --count;
                      break;
                    }
                  done = 1;
                }
            }
          break;
        case 0: // [VC: Proper Conditional Section/PE Nesting]
          if (count != 0)
            {
              this->fatal_error (ACE_TEXT ("Invalid Conditional Section/PE ")
                                 ACE_TEXT ("Nesting ")
                                 ACEXML_ENV_ARG_PARAMETER);
              ACEXML_CHECK_RETURN (-1);
            }
        default:
          break;
      }
    if (done)
      break;
    nextch = this->get();
  } while (1);

  return 0;
}

int
ACEXML_Parser::parse_includesect (ACEXML_ENV_SINGLE_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  ACEXML_Char nextch = this->skip_whitespace();
  do {
    switch (nextch)
      {
        case '<':
          nextch = this->get();
          switch (nextch)
            {
              case '!':
                nextch = this->peek();
                if (nextch == '[')
                  this->parse_conditional_section (ACEXML_ENV_SINGLE_ARG_PARAMETER);
                else
                  this->parse_markup_decl (ACEXML_ENV_SINGLE_ARG_PARAMETER);
                ACEXML_CHECK_RETURN (-1);
                break;
              case '?':
                nextch = this->peek();
                this->parse_processing_instruction (ACEXML_ENV_SINGLE_ARG_PARAMETER);
                ACEXML_CHECK_RETURN (-1);
                break;
              default:
                this->fatal_error (ACE_TEXT ("Invalid includeSect")
                                   ACEXML_ENV_ARG_PARAMETER);
                ACEXML_CHECK_RETURN (-1);
            }
          break;
        case '%':
          this->parse_PE_reference (ACEXML_ENV_SINGLE_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
          break;
        case 0: // [VC: Proper Conditional Section/PE Nesting]
          this->fatal_error (ACE_TEXT ("Invalid Conditional Section/PE ")
                             ACE_TEXT ("Nesting ")
                             ACEXML_ENV_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
        case ']':
          if (this->peek() == ']')
            {
              nextch = this->get();
              if (this->peek() == '>')
                {
                  nextch = this->get();
                  return 0;
                }
            }
        default:
          this->fatal_error (ACE_TEXT ("Invalid includeSect")
                             ACEXML_ENV_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
      }
    nextch = this->skip_whitespace();
  } while (1);
}

int
ACEXML_Parser::parse_markup_decl (ACEXML_ENV_SINGLE_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  ACEXML_Char nextch = this->peek ();
  switch (nextch)
    {
      case 'E':         // An ELEMENT or ENTITY decl
        this->get ();
        nextch = this->peek ();
        switch (nextch)
          {
            case 'L':
              this->parse_element_decl (ACEXML_ENV_SINGLE_ARG_PARAMETER);
              ACEXML_CHECK_RETURN (-1);
              break;
            case 'N':
              this->parse_entity_decl (ACEXML_ENV_SINGLE_ARG_PARAMETER);
              ACEXML_CHECK_RETURN (-1);
              break;
            default:
              this->fatal_error(ACE_TEXT ("Expecting keyword ELEMENT/ENTITY")
                                ACEXML_ENV_ARG_PARAMETER);
              ACEXML_CHECK_RETURN (-1);
          }
        break;

      case 'A':         // An ATTLIST decl
        this->parse_attlist_decl (ACEXML_ENV_SINGLE_ARG_PARAMETER);
        ACEXML_CHECK_RETURN (-1);
        break;

      case 'N':         // A NOTATION decl
        this->parse_notation_decl (ACEXML_ENV_SINGLE_ARG_PARAMETER);
        ACEXML_CHECK_RETURN (-1);
        break;

      case '-':         // a comment.
        if (this->parse_comment () < 0)
          {
            this->fatal_error(ACE_TEXT ("Invalid comment")
                              ACEXML_ENV_ARG_PARAMETER);
            ACEXML_CHECK_RETURN (-1);
          }
        break;
      case 0: //  [VC: Proper Declaration/PE Nesting]
        this->fatal_error (ACE_TEXT ("Unexpected end-of-file")
                           ACEXML_ENV_ARG_PARAMETER);
        ACEXML_CHECK_RETURN (-1);
      default:
        this->fatal_error (ACE_TEXT ("Invalid markupDecl")
                           ACEXML_ENV_ARG_PARAMETER);
        ACEXML_CHECK_RETURN (-1);
    }
  return 0;
}

int
ACEXML_Parser::parse_external_id (ACEXML_Char *&publicId,
                                  ACEXML_Char *&systemId
                                  ACEXML_ENV_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  publicId = systemId = 0;
  ACEXML_Char nextch = this->get ();
  ACEXML_Char fwd = 0;
  switch (nextch)
    {
      case 'S':                   // External SYSTEM id.
        if (this->parse_token (ACE_TEXT ("YSTEM")) < 0 ||
            this->skip_whitespace_count () < 1)
          {
            this->fatal_error(ACE_TEXT ("Expecting keyword SYSTEM")
                              ACEXML_ENV_ARG_PARAMETER);
            ACEXML_CHECK_RETURN (-1);
          }
        if (this->parse_system_literal (systemId) != 0)
          {
            this->fatal_error(ACE_TEXT ("Invalid systemLiteral")
                              ACEXML_ENV_ARG_PARAMETER);
            ACEXML_CHECK_RETURN (-1);
          }
        break;
      case 'P': // External PUBLIC id or previously defined PUBLIC id.
        if (this->parse_token (ACE_TEXT ("UBLIC")) < 0 ||
            this->skip_whitespace_count () < 1)
          {
            this->fatal_error(ACE_TEXT ("Expecing keyword PUBLIC")
                              ACEXML_ENV_ARG_PARAMETER);
            ACEXML_CHECK_RETURN (-1);
          }
        if (this->parse_pubid_literal (publicId) != 0)
          {
            this->fatal_error(ACE_TEXT ("Invalid PubidLiteral")
                              ACEXML_ENV_ARG_PARAMETER);
            ACEXML_CHECK_RETURN (-1);
          }
        this->skip_whitespace_count(&fwd);
        if (fwd == '\'' || fwd == '"')
          {
            if (this->parse_system_literal (systemId) != 0)
              {
                this->fatal_error(ACE_TEXT ("Invalid systemLiteral")
                                  ACEXML_ENV_ARG_PARAMETER);
                ACEXML_CHECK_RETURN (-1);
              }
          }
        else if (this->ref_state_ != ACEXML_ParserInt::IN_NOTATION)
          {
            this->fatal_error(ACE_TEXT ("Expecting systemLiteral after a ")
                              ACE_TEXT ("PUBLIC keyword")
                              ACEXML_ENV_ARG_PARAMETER);
            ACEXML_CHECK_RETURN (-1);
          }
        break;
      default:
        this->fatal_error(ACE_TEXT ("Invalid system/public Literal")
                          ACEXML_ENV_ARG_PARAMETER);
        ACEXML_CHECK_RETURN (-1);
    }
  return 0;
}

ACEXML_Char*
ACEXML_Parser::normalize_systemid (const ACEXML_Char* systemId)
{
  if (ACE_OS::strstr (systemId, ACE_TEXT("ftp://")) != 0 ||
      ACE_OS::strstr (systemId, ACE_TEXT ("http://")) != 0 ||
      ACE_OS::strstr (systemId, ACE_TEXT ("file://")) != 0)
    return 0;
  else
    {
      ACEXML_Char* normalized_uri = 0;
      const ACEXML_Char* baseURI = this->current_->getLocator()->getSystemId();
      ACE_ASSERT (baseURI);
      const ACEXML_Char* temp = 0;
      if (ACE_OS::strstr (baseURI, ACE_TEXT ("http://")) != 0)
        // baseURI is a HTTP URL and systemId is relative. Note that this
        // is not compliant with RFC2396. Caveat Emptor !
        temp = ACE_OS::strrchr (baseURI, '/');
      else
        // baseURI is a local file and systemId is relative
        // Unlike the HTTP one, this will work always.
        temp = ACE_OS::strrchr (baseURI,ACE_DIRECTORY_SEPARATOR_CHAR);
      if (temp)
        {
          size_t pos = temp - baseURI + 1;
          size_t len = pos + ACE_OS::strlen (systemId) + 1;
          ACE_NEW_RETURN (normalized_uri, ACEXML_Char[len], 0);
          ACE_OS::strncpy (normalized_uri, baseURI, pos);
          ACE_OS::strcpy (normalized_uri + pos, systemId);
          return normalized_uri;
        }
      return 0;
    }
}

void
ACEXML_Parser::parse_element (int is_root ACEXML_ENV_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  // Parse STag.
  const ACEXML_Char *startname = this->parse_name ();
  if (startname == 0)
    {
      this->fatal_error (ACE_TEXT ("Unexpected end-of-file")
                         ACEXML_ENV_ARG_PARAMETER);
      return;
    }
  if (is_root && this->doctype_ != 0
      && ACE_OS::strcmp (startname, this->doctype_) != 0)
    {
      this->fatal_error (ACE_TEXT ("Root element different from DOCTYPE")
                         ACEXML_ENV_ARG_PARAMETER);
      return ;
    }
  ACEXML_AttributesImpl attributes;
  ACEXML_Char ch;
  int ns_flag = 0;   // Push only one namespace context onto the stack
                     // if there are multiple namespaces declared.

  const ACEXML_Char* ns_uri = 0;
  const ACEXML_Char* ns_lname = 0; // namespace URI and localName
  for (int start_element_done = 0; start_element_done == 0;)
    {
      ch = this->skip_whitespace ();

      switch (ch)
        {
          case 0:
            this->fatal_error(ACE_TEXT ("Internal Parser error")
                              ACEXML_ENV_ARG_PARAMETER);
            return;
          case '/':
            if (this->get () != '>')
              {
                this->fatal_error(ACE_TEXT ("Expecting '>' at end of element ")
                                  ACE_TEXT ("definition")
                                  ACEXML_ENV_ARG_PARAMETER);
                return;
              }
            this->xml_namespace_.processName(startname, ns_uri,
                                             ns_lname, 0);
            this->prefix_mapping (this->xml_namespace_.getPrefix(ns_uri),
                                  ns_uri, ns_lname, 1
                                  ACEXML_ENV_ARG_PARAMETER);
            ACEXML_CHECK;
            this->content_handler_->startElement(ns_uri, ns_lname,
                                                 startname, &attributes
                                                 ACEXML_ENV_ARG_PARAMETER);
            ACEXML_CHECK;
            this->content_handler_->endElement (ns_uri, ns_lname, startname
                                                ACEXML_ENV_ARG_PARAMETER);
            ACEXML_CHECK;
            this->prefix_mapping (this->xml_namespace_.getPrefix(ns_uri),
                                  ns_uri, ns_lname, 0
                                  ACEXML_ENV_ARG_PARAMETER);
            ACEXML_CHECK;
            if (ns_flag)
              {
                this->xml_namespace_.popContext ();
                this->nested_namespace_--;
              }
            return;
          case '>':
            this->xml_namespace_.processName (startname, ns_uri,
                                              ns_lname, 0);
            this->prefix_mapping (this->xml_namespace_.getPrefix(ns_uri),
                                  ns_uri, ns_lname, 1
                                  ACEXML_ENV_ARG_PARAMETER);
            ACEXML_CHECK;
            this->content_handler_->startElement(ns_uri, ns_lname, startname,
                                                 &attributes
                                                 ACEXML_ENV_ARG_PARAMETER);
            ACEXML_CHECK;
            start_element_done = 1;
            break;
          default:
            ACEXML_Char *attvalue = 0;
            ACEXML_Char *attname = this->parse_name (ch);

            if (attname == 0 ||
                this->skip_equal () != 0 ||
                this->parse_attvalue (attvalue ACEXML_ENV_ARG_PARAMETER) != 0)
              {
                this->fatal_error(ACE_TEXT ("Error reading attribute value")
                                  ACEXML_ENV_ARG_PARAMETER);
                return;
              }

            // Handling new namespace if any. Notice that the order of
            // namespace declaration does matter.
            if (ACE_OS::strncmp (attname, ACE_TEXT("xmlns"), 5) == 0)
              {
                if (this->namespaces_)
                  {
                    if (!ns_flag)
                      {
                        this->xml_namespace_.pushContext ();
                        this->nested_namespace_++;
                        ns_flag = 1;
                      }

                    ACEXML_Char* name = ACE_OS::strchr (attname, ':');
                    const ACEXML_Char* ns_name = (name == 0)?
                                                 empty_string:name+1;
                    if (this->xml_namespace_.declarePrefix (ns_name,
                                                            attvalue) == -1)
                      {
                        this->fatal_error(ACE_TEXT ("Duplicate definition of ")
                                          ACE_TEXT ("prefix")
                                          ACEXML_ENV_ARG_PARAMETER);
                        return;
                      }
                  }
                if (this->namespace_prefixes_)
                  {
                    // Namespace_prefixes_feature_ is required. So add the
                    // xmlns:foo to the list of attributes.
                    if (attributes.addAttribute (ACE_TEXT (""), ACE_TEXT (""),
                                                 attname,
                                                 default_attribute_type,
                                                 attvalue) == -1)
                      {
                        this->fatal_error(ACE_TEXT ("Duplicate attribute ")
                                          ACE_TEXT ("definition. Hint: Try ")
                                          ACE_TEXT ("setting namespace_prefix")
                                          ACE_TEXT ("es feature to 0")
                                          ACEXML_ENV_ARG_PARAMETER);
                        return;
                      }
                  }
                if (!this->namespaces_ && !this->namespace_prefixes_)
                  {
                    this->fatal_error(ACE_TEXT ("One of namespaces or ")
                                      ACE_TEXT ("namespace_prefixes should be")
                                      ACE_TEXT (" declared")
                                      ACEXML_ENV_ARG_PARAMETER);
                    return;
                  }
              }
            else
              {
                const ACEXML_Char *uri, *lName;
                this->xml_namespace_.processName (attname, uri, lName, 1);
                if (attributes.addAttribute (uri, lName, attname,
                                             default_attribute_type,
                                             attvalue) == -1)
                  {
                    this->fatal_error(ACE_TEXT ("Duplicate attribute ")
                                      ACE_TEXT ("definition")
                                      ACEXML_ENV_ARG_PARAMETER);
                    return;
                  }
              }
            break;
        }
    }
  if (this->parse_content (startname, ns_uri, ns_lname
                           ACEXML_ENV_ARG_PARAMETER) != 0)
    return;
}

int
ACEXML_Parser::parse_content (const ACEXML_Char* startname,
                              const ACEXML_Char* ns_uri,
                              const ACEXML_Char* ns_lname ACEXML_ENV_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  ACEXML_Char *cdata;
  size_t cdata_length = 0;

  // Parse element contents.
  while (1)
    {
      ACEXML_Char ch = this->get ();
      switch (ch)
        {
          case 0:
            this->pop_context (1 ACEXML_ENV_ARG_PARAMETER);
            ACEXML_CHECK_RETURN (-1);
            break;
          case '<':
            // Push out old 'characters' event.
            if (cdata_length != 0)
              {
                cdata = this->obstack_.freeze ();
                this->content_handler_->characters (cdata, 0, cdata_length
                                                    ACEXML_ENV_ARG_PARAMETER);
                ACEXML_CHECK_RETURN (-1);
                this->obstack_.unwind (cdata);
                cdata_length = 0;
              }
            ch = this->peek();
            switch (ch)
              {
                case '!':             // a comment or a CDATA section.
                  this->get ();       // consume '!'
                  ch = this->peek ();
                  if (ch == '-')      // a comment
                    {
                      if (this->parse_comment () < 0)
                        {
                          this->fatal_error(ACE_TEXT ("Invalid comment in ")
                                            ACE_TEXT ("document")
                                            ACEXML_ENV_ARG_PARAMETER);
                          ACEXML_CHECK_RETURN (-1);
                        }
                    }
                  else if (ch == '[') // a CDATA section.
                    {
                      this->parse_cdata (ACEXML_ENV_SINGLE_ARG_PARAMETER);
                      ACEXML_CHECK_RETURN (-1);
                    }
                  else
                    {
                      this->fatal_error(ACE_TEXT ("Expecting a CDATA section ")
                                        ACE_TEXT ("or a comment section")
                                        ACEXML_ENV_ARG_PARAMETER);
                      ACEXML_CHECK_RETURN (-1);
                    }
                  break;
                case '?':             // a PI.
                  this->get();        // consume the '?'
                  this->parse_processing_instruction
                    (ACEXML_ENV_SINGLE_ARG_PARAMETER);
                  ACEXML_CHECK_RETURN (-1);
                  break;
                case '/':             // an ETag.
                  {
                    this->get ();       // consume '/'
                    ACEXML_Char* endname = this->parse_name ();
                    if (endname == 0 ||
                        ACE_OS::strcmp (startname, endname) != 0)
                      {
                        this->fatal_error(ACE_TEXT ("Name in ETag doesn't ")
                                          ACE_TEXT ("match name in STag")
                                          ACEXML_ENV_ARG_PARAMETER);
                        ACEXML_CHECK_RETURN (-1);
                      }
                    if (this->skip_whitespace () != '>')
                      {
                        this->fatal_error(ACE_TEXT ("Expecting '>' at end ")
                                          ACE_TEXT ("of element")
                                          ACEXML_ENV_ARG_PARAMETER);
                        return -1;
                      }
                    this->content_handler_->endElement (ns_uri, ns_lname,
                                                        endname
                                                        ACEXML_ENV_ARG_PARAMETER);
                    ACEXML_CHECK_RETURN (-1);
                    this->prefix_mapping (this->xml_namespace_.getPrefix(ns_uri),
                                          ns_uri, ns_lname, 0
                                          ACEXML_ENV_ARG_PARAMETER);
                    ACEXML_CHECK_RETURN (-1);
                    if (this->nested_namespace_ >= 1)
                      {
                        this->xml_namespace_.popContext ();
                        this->nested_namespace_--;
                      }
                    return 0;
                  }
                default:              // a new nested element?
                  this->parse_element (0 ACEXML_ENV_ARG_PARAMETER);
                  ACEXML_CHECK_RETURN (-1);
                  break;
              }
            break;
          case '&':
            if (this->peek () == '#')
              {
                ACEXML_Char buf[7];
                size_t len = 0;
                do
                  {
                    len = sizeof (buf);
                    if (this->parse_char_reference (buf, len) != 0)
                      {
                        // [WFC: Legal Character]
                        this->fatal_error (ACE_TEXT ("Invalid CharRef")
                                           ACEXML_ENV_ARG_PARAMETER);
                        ACEXML_CHECK_RETURN (-1);
                      }
                  } while (buf[0] == '&' && this->peek() == '#');
                  for (size_t j = 0; j < len; ++j)
                    this->obstack_.grow (buf[j]);
                cdata_length += len;
              }
            else
              {
                this->ref_state_ = ACEXML_ParserInt::IN_CONTENT;
                int length = this->parse_entity_reference(ACEXML_ENV_SINGLE_ARG_PARAMETER);
                ACEXML_CHECK_RETURN (-1);
                if (length == 1)
                  cdata_length++;
              }
            break;
          case '\x20': case '\x0D': case '\x0A': case '\x09':
//             if (this->validate_)
//               {
//                 // Flush out any non-whitespace characters
//                 if (cdata_length != 0)
//                   {
//                     cdata = this->obstack_.freeze ();
//                     this->content_handler_->characters(cdata, 0, cdata_length
//                                                        ACEXML_ENV_ARG_PARAMETER);
//                     ACEXML_CHECK_RETURN (-1);
//                     this->obstack_.unwind (cdata);
//                     cdata_length = 0;
//                   }
//                 ++cdata_length;
//                 this->obstack_.grow (ch);
//                 while (1)
//                   {
//                     ch = this->peek();
//                     if (ch == '\x20' || ch == '\x0D' || ch == '\x0A' ||
//                         ch == '\x09')
//                       {
//                         ch = this->get();
//                         this->obstack_.grow (ch);
//                         continue;
//                       }
//                     break;
//                   }
//                 cdata = this->obstack_.freeze ();
//                 this->content_handler_->ignorableWhitespace (cdata, 0,
//                                                              cdata_length
//                                                              ACEXML_ENV_ARG_PARAMETER);
//                 ACEXML_CHECK_RETURN (-1);
//                 this->obstack_.unwind (cdata);
//                 cdata_length = 0;
//                 break;
//               }
            // Fall thru...
          default:
            ++cdata_length;
            this->obstack_.grow (ch);
        }
    }
  ACE_NOTREACHED (return 0;)
}


int
ACEXML_Parser::parse_cdata (ACEXML_ENV_SINGLE_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  if (this->parse_token (ACE_TEXT ("[CDATA[")) < 0)
    {
      this->fatal_error(ACE_TEXT ("Expecting '[CDATA[' at beginning of CDATA ")
                        ACE_TEXT ("section")
                        ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }

  ACEXML_Char ch;
  int datalen = 0;
  ACEXML_Char *cdata = 0;
  while (1)
    {
      ch = this->get ();
      // Anything goes except the sequence "]]>".
      if (ch == ']' && this->peek() == ']')
        {
          ACEXML_Char temp = ch;
          ch = this->get();
          if (ch == ']' && this->peek() == '>')
            {
              ch = this->get();
              cdata = this->obstack_.freeze ();
              this->content_handler_->characters (cdata, 0, datalen
                                                  ACEXML_ENV_ARG_PARAMETER);
              ACEXML_CHECK_RETURN (-1);
              this->obstack_.unwind(cdata);
              return 0;
            }
          this->obstack_.grow (temp);
          ++datalen;
        }
      this->obstack_.grow (ch);
      ++datalen;
    };
  ACE_NOTREACHED (return -1);
}


int
ACEXML_Parser::parse_entity_decl (ACEXML_ENV_SINGLE_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  ACEXML_Char nextch;

  if ((this->parse_token (ACE_TEXT ("NTITY")) < 0) ||
      this->skip_whitespace_count (&nextch) == 0)
    {
      this->fatal_error (ACE_TEXT ("Expecting keyword ENTITY followed by a ")
                         ACE_TEXT ("space") ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }

  int is_GEDecl = 1;
  if (nextch == '%')            // This is a PEDecl.
    {
      is_GEDecl = 0;
      this->get ();             // consume the '%'
      if (this->skip_whitespace_count (&nextch) == 0)
        {
          this->fatal_error (ACE_TEXT ("Expecting space between % and ")
                             ACE_TEXT ("entity name")
                             ACEXML_ENV_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
        }
    }

  ACEXML_Char *entity_name = this->parse_name ();
  if (entity_name == 0)
    {
      this->fatal_error (ACE_TEXT ("Invalid entity name")
                         ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }

  if (this->skip_whitespace_count (&nextch) == 0)
    {
      this->fatal_error (ACE_TEXT ("Expecting space between entity name and ")
                         ACE_TEXT ("entityDef")
                         ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }
  int retval = 0;
  if (nextch == '\'' || nextch == '"')
    {
      ACEXML_Char *entity_value = 0;
      if (this->parse_entity_value (entity_value
                                    ACEXML_ENV_ARG_PARAMETER) != 0)
        {
          this->fatal_error(ACE_TEXT ("Invalid EntityValue")
                            ACEXML_ENV_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
        }
      if (is_GEDecl)
        retval = this->internal_GE_.add_entity (entity_name,
                                                entity_value);
      else
        retval = this->internal_PE_.add_entity (entity_name,
                                                entity_value);
      if (retval < 0)
        {
          this->fatal_error (ACE_TEXT ("Internal Parser Error in adding")
                             ACE_TEXT ("Entity to map")
                             ACEXML_ENV_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
        }
      else if (retval == 1)
        {
          this->warning (ACE_TEXT ("Duplicate entity found")
                         ACEXML_ENV_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
        }
    }
  else
    {
      ACEXML_Char *systemid, *publicid;

      this->parse_external_id (publicid, systemid
                               ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
      if (systemid == 0)
        {
          this->fatal_error(ACE_TEXT ("Invalid SystemLiteral")
                            ACEXML_ENV_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
        }
      this->skip_whitespace_count (&nextch);
      if (nextch == 'N')        // NDATA section followed
        {
          if (is_GEDecl == 0)
            {
              this->fatal_error(ACE_TEXT ("Invalid NDataDecl in PEDef")
                                ACEXML_ENV_ARG_PARAMETER);
              ACEXML_CHECK_RETURN (-1);
            }

          if ((this->parse_token (ACE_TEXT ("NDATA")) < 0) ||
              this->skip_whitespace_count (&nextch) == 0)
            {
              this->fatal_error(ACE_TEXT ("Expecting keyword NDATA followed ")
                                ACE_TEXT ("by a space") ACEXML_ENV_ARG_PARAMETER);
              ACEXML_CHECK_RETURN (-1);
            }

          ACEXML_Char *ndata = this->parse_name ();
          if (this->validate_) // [VC: Notation Declared]
            {
              if (!this->notations_.resolve_entity (ndata))
                {
                  this->fatal_error (ACE_TEXT ("Undeclared Notation name")
                                     ACEXML_ENV_ARG_PARAMETER);
                  ACEXML_CHECK_RETURN (-1);
                }
              this->dtd_handler_->unparsedEntityDecl(entity_name, publicid,
                                                     systemid, ndata
                                                     ACEXML_ENV_ARG_PARAMETER);
              ACEXML_CHECK_RETURN (-1);
            }
        }
      else
        {
          if (is_GEDecl)
            retval = this->external_GE_.add_entity (entity_name,
                                                    systemid);
          else
            retval = this->external_PE_.add_entity (entity_name,
                                                    systemid);
          if (retval < 0)
            {
              this->fatal_error(ACE_TEXT ("Internal Parser Error")
                                ACEXML_ENV_ARG_PARAMETER);
              ACEXML_CHECK_RETURN (-1);
            }
          else if (retval == 1)
            this->warning(ACE_TEXT ("Duplicate external entity")
                          ACEXML_ENV_ARG_PARAMETER);
          if (is_GEDecl)
            retval = this->external_GE_.add_entity (entity_name,
                                                    publicid);
          else
            retval = this->external_PE_.add_entity (entity_name,
                                                    publicid);
          if (retval < 0)
            {
              this->fatal_error(ACE_TEXT ("Internal Parser Error")
                                ACEXML_ENV_ARG_PARAMETER);
              ACEXML_CHECK_RETURN (-1);
            }
          else if (retval == 1)
            this->warning (ACE_TEXT ("Duplicate entity definition")
                           ACEXML_ENV_ARG_PARAMETER);
        }
    }

  // End of ENTITY definition
  if (this->skip_whitespace() != '>')
    {
      this->fatal_error(ACE_TEXT ("Expecting '>' at end of entityDef")
                        ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }
  return 0;
}

int
ACEXML_Parser::parse_attlist_decl (ACEXML_ENV_SINGLE_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  if (this->parse_token (ACE_TEXT ("ATTLIST")) < 0)
    {
      this->fatal_error(ACE_TEXT ("Expecting keyword 'ATTLIST'")
                        ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }
  int count = check_for_PE_reference (ACEXML_ENV_SINGLE_ARG_PARAMETER);
  ACEXML_CHECK_RETURN (-1);
  if (!count)
    {
      this->fatal_error(ACE_TEXT ("Expecting space between ATTLIST and ")
                        ACE_TEXT ("element name") ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }

  ACEXML_Char *element_name = this->parse_name ();
  if (element_name == 0)
    {
      this->fatal_error(ACE_TEXT ("Invalid element Name in attlistDecl")
                        ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }
  ACEXML_Char fwd = 0;
  count = this->skip_whitespace_count (&fwd);
  // Parse AttDef*
  while (fwd != '>')
    {
      if (!this->check_for_PE_reference (ACEXML_ENV_SINGLE_ARG_PARAMETER)
          && !count)
        this->fatal_error(ACE_TEXT ("Expecting space between element ")
                          ACE_TEXT ("name and AttDef")
                          ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
      this->skip_whitespace_count (&fwd);
      if (fwd == '>')
        break;

      count = this->check_for_PE_reference (ACEXML_ENV_SINGLE_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);

      this->parse_attname (ACEXML_ENV_SINGLE_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);

      count = this->check_for_PE_reference (ACEXML_ENV_SINGLE_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
      if (!count)
        {
          this->fatal_error(ACE_TEXT ("Expecting space between AttName and ")
                            ACE_TEXT ("AttType") ACEXML_ENV_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
        }
      this->parse_atttype (ACEXML_ENV_SINGLE_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);

      count = this->check_for_PE_reference (ACEXML_ENV_SINGLE_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
      if (!count)
        {
          this->fatal_error(ACE_TEXT ("Expecting space between AttType and")
                            ACE_TEXT (" DefaultDecl")
                            ACEXML_ENV_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
        }
      this->parse_defaultdecl (ACEXML_ENV_SINGLE_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);

      count = this->check_for_PE_reference (ACEXML_ENV_SINGLE_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
      this->skip_whitespace_count(&fwd);
    }
  this->get ();                 // consume closing '>'
  return 0;
}


int
ACEXML_Parser::check_for_PE_reference (ACEXML_ENV_SINGLE_ARG_DECL)
{
  ACEXML_Char fwd = '\xFF';
  // Skip any leading whitespaces and store the number of such chars skipped
  int count = this->skip_whitespace_count (&fwd);
  if (fwd == 0)
    {
      this->get(); // Consume the 0
      this->pop_context (0 ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
      fwd = this->peek();
    }
  if (fwd == '%')
    {
      this->get();  // Consume the %
      if (this->external_subset_)
        {
          this->parse_PE_reference (ACEXML_ENV_SINGLE_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
        }
      else
        {
          this->fatal_error(ACE_TEXT ("Illegal PERef within markupDecl")
                            ACEXML_ENV_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
        }
    }
  if (count)
    {
      // We have atleast one whitespace. So just skip any more whitespaces
      // and return the count
      this->skip_whitespace_count();
      return count;
    }
  return this->skip_whitespace_count();
}

ACEXML_Char*
ACEXML_Parser::parse_attname (ACEXML_ENV_SINGLE_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  // Parse attribute name
  ACEXML_Char *att_name = this->parse_name ();
  if (att_name == 0)
    {
      this->fatal_error(ACE_TEXT ("Invalid AttName")
                        ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (0);
    }
  return att_name;
}

int
ACEXML_Parser::parse_defaultdecl (ACEXML_ENV_SINGLE_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  // DefaultDecl ::=  '#REQUIRED' | '#IMPLIED' | (('#FIXED' S)? AttValue)
  ACEXML_Char nextch = this->peek ();
  ACEXML_Char *fixed_attr = 0;
  switch (nextch)
    {
      case '#':
        this->get ();         // consume the '#'
        switch (this->get ())
          {
            case 'R':
              if (this->parse_token (ACE_TEXT ("EQUIRED")) < 0)
                {
                  this->fatal_error(ACE_TEXT ("Expecting keyword REQUIRED")
                                    ACEXML_ENV_ARG_PARAMETER);
                  ACEXML_CHECK_RETURN (-1);
                }
              // We now know this attribute is required
              // @@ Set up the validator as such.
              break;
            case 'I':
              if (this->parse_token (ACE_TEXT ("MPLIED")) < 0)
                {
                  this->fatal_error(ACE_TEXT ("Expecting keyword IMPLIED")
                                    ACEXML_ENV_ARG_PARAMETER);
                  ACEXML_CHECK_RETURN (-1);
                }
              // We now know this attribute is implied.
              // @@ Set up the validator as such.
              break;
            case 'F':
              if (this->parse_token (ACE_TEXT ("IXED")) < 0 ||
                  this->skip_whitespace_count () == 0)
                {
                  this->fatal_error(ACE_TEXT ("Expecting keyword FIXED")
                                    ACEXML_ENV_ARG_PARAMETER);
                  ACEXML_CHECK_RETURN (-1);
                }
              // We now know this attribute is fixed.
              if (this->parse_attvalue (fixed_attr
                                        ACEXML_ENV_ARG_PARAMETER) != 0)
                {
                  this->fatal_error(ACE_TEXT ("Invalid Default AttValue")
                                    ACEXML_ENV_ARG_PARAMETER);
                  ACEXML_CHECK_RETURN (-1);
                }
              // @@ set up validator
              break;
            default:
              this->fatal_error (ACE_TEXT ("Invalid DefaultDecl")
                                 ACEXML_ENV_ARG_PARAMETER);
              ACEXML_CHECK_RETURN (-1);
          }
        break;
      case '\'':
      case '"':
        if (this->parse_attvalue (fixed_attr ACEXML_ENV_ARG_PARAMETER) != 0)
          {
            this->fatal_error(ACE_TEXT ("Invalid AttValue")
                              ACEXML_ENV_ARG_PARAMETER);
            ACEXML_CHECK_RETURN (-1);
          }
        // @@ set up validator
        break;
      default:
        this->fatal_error (ACE_TEXT ("Invalid DefaultDecl")
                           ACEXML_ENV_ARG_PARAMETER);
        ACEXML_CHECK_RETURN (-1);
        break;
    }
  return 0;
}

int
ACEXML_Parser::parse_tokenized_type (ACEXML_ENV_SINGLE_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  ACEXML_Char ch = this->get();
  switch (ch)
    {
      case 'I':
        if (this->get () == 'D')
          {
            if (this->peek() != 'R' && this->is_whitespace (this->peek()))
              {
                // We have successfully identified the type of the
                // attribute as ID
                // @@ Set up validator as such.
                break;
              }
            if (this->parse_token (ACE_TEXT ("REF")) == 0)
              {
                if (this->peek() != 'S' && this->is_whitespace (this->peek()))
                  {
                    // We have successfully identified the type of
                    // the attribute as IDREF
                    // @@ Set up validator as such.
                    break;
                  }
                else if (this->peek() == 'S'
                         && this->get() // consume the 'S'
                         && this->is_whitespace (this->peek()))
                  {
                    // We have successfully identified the type of
                    // the attribute as IDREFS
                    // @@ Set up validator as such.
                    break;
                  }
              }
          }
        // Admittedly, this error message is not precise enough
        this->fatal_error(ACE_TEXT ("Expecting keyword `ID', `IDREF', or")
                          ACE_TEXT ("`IDREFS'") ACEXML_ENV_ARG_PARAMETER);
        ACEXML_CHECK_RETURN (-1);
      case 'E':               // ENTITY or ENTITIES
        if (this->parse_token (ACE_TEXT ("NTIT")) == 0)
          {
            ACEXML_Char nextch = this->get ();
            if (nextch == 'Y')
              {
                // We have successfully identified the type of
                // the attribute as ENTITY
                // @@ Set up validator as such.
              }
            else if (this->parse_token (ACE_TEXT ("IES")) == 0)
              {
                // We have successfully identified the type of
                // the attribute as ENTITIES
                // @@ Set up validator as such.
              }
            if (this->is_whitespace (this->peek()))
              {
                // success
                break;
              }
          }
        // Admittedly, this error message is not precise enough
        this->fatal_error(ACE_TEXT ("Expecting keyword `ENTITY', or")
                          ACE_TEXT ("`ENTITIES'") ACEXML_ENV_ARG_PARAMETER);
        ACEXML_CHECK_RETURN (-1);
      case 'M':
        if (this->parse_token (ACE_TEXT ("TOKEN")) == 0)
          {
            if (this->is_whitespace (this->peek()))
              {
                // We have successfully identified the type of
                // the attribute as NMTOKEN
                // @@ Set up validator as such.
                break;
              }
            else if (this->peek() == 'S'
                     && this->get()
                     && this->is_whitespace (this->peek()))
              {
                // We have successfully identified the type of
                // the attribute as NMTOKENS
                // @@ Set up validator as such.
                break;
              }
          }
        this->fatal_error(ACE_TEXT ("Expecting keyword `NMTOKEN' or `NMTO")
                          ACE_TEXT ("KENS'") ACEXML_ENV_ARG_PARAMETER);
        ACEXML_CHECK_RETURN (-1);
        break;
      default:
        this->fatal_error (ACE_TEXT ("Internal Parser Error")
                           ACEXML_ENV_ARG_PARAMETER);
        ACEXML_CHECK_RETURN (-1);
        break;
    }
  return 0;
}


/**
 * AttType    ::=    StringType | TokenizedType | EnumeratedType
 * StringType ::=    'CDATA'
 * TokenizedType    ::=    'ID'       [VC: ID]
 *                                    [VC: One ID per Element Type]
 *                                    [VC: ID Attribute Default]
 *                       | 'IDREF' [VC: IDREF]
 *                       | 'IDREFS' [VC: IDREF]
 *                       | 'ENTITY' [VC: Entity Name]
 *                       | 'ENTITIES' [VC: Entity Name]
 *                       | 'NMTOKEN' [VC: Name Token]
 *                       | 'NMTOKENS'
 *
 * EnumeratedType  ::=    NotationType | Enumeration
 * NotationType    ::=   'NOTATION' S '(' S? Name (S? '|' S? Name)* S? ')'
 *                                  [VC: Notation Attributes]
 *                                  [VC: One Notation Per Element Type]
 *                                  [VC: No Notation on Empty Element]
 * Enumeration ::= '(' S? Nmtoken (S? '|' S? Nmtoken)* S? ')'
 *                                  [VC: Enumeration]
 */
int
ACEXML_Parser::parse_atttype (ACEXML_ENV_SINGLE_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  ACEXML_Char nextch = this->peek();
  switch (nextch)
    {
      case 'C':               // CDATA
        if (this->parse_token (ACE_TEXT ("CDATA")) < 0)
          {
            this->fatal_error(ACE_TEXT ("Expecting keyword 'CDATA'")
                              ACEXML_ENV_ARG_PARAMETER);
            ACEXML_CHECK_RETURN (-1);
          }
        // Else, we have successfully identified the type of the
        // attribute as CDATA
        // @@ Set up validator appropriately here.
        break;
      case 'I': case 'E':      // ID, IDREF, IDREFS, ENTITY or ENTITIES
        this->parse_tokenized_type (ACEXML_ENV_SINGLE_ARG_PARAMETER);
        ACEXML_CHECK_RETURN (-1);
        break;
      case 'N':             // NMTOKEN, NMTOKENS, or NOTATION
        this->get();
        nextch = this->peek();
        if (nextch != 'M' && nextch != 'O')
          {
            this->fatal_error (ACE_TEXT ("Expecting keyword 'NMTOKEN', ")
                               ACE_TEXT ("'NMTOKENS' or 'NOTATION'")
                               ACEXML_ENV_ARG_PARAMETER);
            ACEXML_CHECK_RETURN (-1);
          }
        if (nextch == 'M')
          {
            this->parse_tokenized_type (ACEXML_ENV_SINGLE_ARG_PARAMETER);
            ACEXML_CHECK_RETURN (-1);
            break;
          }
        else                   // NOTATION
          {
            if (this->parse_token (ACE_TEXT ("OTATION")) < 0)
              {
                this->fatal_error(ACE_TEXT ("Expecting keyword `NOTATION'")
                                  ACEXML_ENV_ARG_PARAMETER);
                ACEXML_CHECK_RETURN (-1);
              }
            int count = this->check_for_PE_reference (ACEXML_ENV_SINGLE_ARG_PARAMETER);
            ACEXML_CHECK_RETURN (-1);
            if (!count)
              {
                this->fatal_error (ACE_TEXT ("Expecting space between keyword ")
                                   ACE_TEXT ("NOTATION and '('")
                                   ACEXML_ENV_ARG_PARAMETER);
                ACEXML_CHECK_RETURN (-1);
              }
            if (this->get () != '(')
              {
                this->fatal_error(ACE_TEXT ("Expecting '(' in NotationType")
                                  ACEXML_ENV_ARG_PARAMETER);
                ACEXML_CHECK_RETURN (-1);
              }
            this->check_for_PE_reference (ACEXML_ENV_SINGLE_ARG_PARAMETER);
            ACEXML_CHECK_RETURN (-1);
            do {
              this->skip_whitespace_count();
              ACEXML_Char *notation_name = this->parse_name ();
              if (notation_name == 0)
                {
                  this->fatal_error(ACE_TEXT ("Invalid notation name")
                                    ACEXML_ENV_ARG_PARAMETER);
                  ACEXML_CHECK_RETURN (-1);
                }
              // @@ get another notation name, set up validator as such
              this->check_for_PE_reference (ACEXML_ENV_SINGLE_ARG_PARAMETER);
              ACEXML_CHECK_RETURN (-1);
              nextch = this->get();
            } while (nextch == '|');
            if (nextch != ')')
              {
                this->fatal_error (ACE_TEXT ("Expecting a ')' after a ")
                                   ACE_TEXT ("NotationType declaration")
                                   ACEXML_ENV_ARG_PARAMETER);
                ACEXML_CHECK_RETURN (-1);
              }
          }
        break;
      case '(':               // EnumeratedType - Enumeration
        this->get();
        this->check_for_PE_reference (ACEXML_ENV_SINGLE_ARG_PARAMETER);
        ACEXML_CHECK_RETURN (-1);
        do {
          this->skip_whitespace_count();
          ACEXML_Char *token_name = this->parse_nmtoken ();
          if (token_name == 0)
            {
              this->fatal_error(ACE_TEXT ("Invalid enumeration name")
                                ACEXML_ENV_ARG_PARAMETER);
              ACEXML_CHECK_RETURN (-1);
            }
          // @@ get another nmtoken, set up validator as such
          this->check_for_PE_reference (ACEXML_ENV_SINGLE_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
          nextch = this->get();
        } while (nextch == '|');
        if (nextch != ')')
          {
            this->fatal_error (ACE_TEXT ("Expecting a ')' after a ")
                               ACE_TEXT ("Enumeration declaration")
                               ACEXML_ENV_ARG_PARAMETER);
            ACEXML_CHECK_RETURN (-1);
          }
        break;
      default:
        {
          this->fatal_error(ACE_TEXT ("Invalid AttType")
                            ACEXML_ENV_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
        }
        ACE_NOTREACHED (break);
    }
  return 0;
}

int
ACEXML_Parser::parse_notation_decl (ACEXML_ENV_SINGLE_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  if (this->parse_token (ACE_TEXT ("NOTATION")) < 0)
    {
      this->fatal_error(ACE_TEXT ("Expecting Keyword 'NOTATION'")
                        ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }
  int count = this->check_for_PE_reference (ACEXML_ENV_SINGLE_ARG_PARAMETER);
  ACEXML_CHECK_RETURN (-1);
  if (!count)
    {
      this->fatal_error (ACE_TEXT ("Expecting a space between keyword NOTATION")
                         ACE_TEXT (" and notation name")
                         ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }
  ACEXML_Char *notation = this->parse_name ();
  if (notation == 0)
    {
      this->fatal_error(ACE_TEXT ("Invalid Notation name")
                        ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }
  count = this->check_for_PE_reference (ACEXML_ENV_SINGLE_ARG_PARAMETER);
  ACEXML_CHECK_RETURN (-1);
  if (!count)
    {
      this->fatal_error (ACE_TEXT ("Expecting a space between notation name ")
                         ACE_TEXT ("and ExternalID/PublicID")
                         ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }

  ACEXML_Char *systemid, *publicid;

  // Gross hack but otherwise we need to go around a lot of loops to parse,
  // When the ExternalID starts with 'PUBLIC' we cannot distinguish a
  // PublicId from a ExternalID by looking using a one character read-ahead
  ACEXML_ParserInt::ReferenceState temp = this->ref_state_;
  this->ref_state_ = ACEXML_ParserInt::IN_NOTATION;

  this->parse_external_id (publicid, systemid
                           ACEXML_ENV_ARG_PARAMETER);
  ACEXML_CHECK_RETURN (-1);
  // Restore the original value.
  this->ref_state_ = temp;

  // [VC: Unique Notation Name]
  if (systemid && this->notations_.add_entity (notation, systemid) != 0
      && this->validate_)
    {
      this->fatal_error(ACE_TEXT ("Internal Parser Error")
                        ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }
  if (publicid)
    {
      int retval = this->notations_.add_entity (notation, publicid);
      if (retval != 0 && !systemid && this->validate_)
        {
          this->fatal_error(ACE_TEXT ("Internal Parser Error")
                            ACEXML_ENV_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
        }
    }

  if (this->skip_whitespace() != '>')
    {
      this->fatal_error(ACE_TEXT ("Expecting '>' at end of NotationDecl")
                        ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }

  if (this->validate_ && this->dtd_handler_)
    {
      this->dtd_handler_->notationDecl (notation,
                                        publicid,
                                        systemid ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }
  return 0;
}

int
ACEXML_Parser::parse_element_decl (ACEXML_ENV_SINGLE_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  if (this->parse_token (ACE_TEXT ("LEMENT")) < 0)
    {
      this->fatal_error (ACE_TEXT ("Expecting keyword ELEMENT")
                         ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }
  int count = this->check_for_PE_reference (ACEXML_ENV_SINGLE_ARG_PARAMETER);
  ACEXML_CHECK_RETURN (-1);
  if (!count)
    {
      this->fatal_error (ACE_TEXT ("Expecting a space between keyword ELEMENT")
                         ACE_TEXT (" and element name")
                         ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }
  ACEXML_Char *element_name = this->parse_name ();
  if (element_name == 0)
    {
      this->fatal_error (ACE_TEXT ("Invalid element name")
                         ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }
  count = this->check_for_PE_reference (ACEXML_ENV_SINGLE_ARG_PARAMETER);
  ACEXML_CHECK_RETURN (-1);
  if (!count)
    {
      this->fatal_error (ACE_TEXT ("Expecting a space between element name ")
                         ACE_TEXT ("and element definition")
                         ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }
  ACEXML_Char nextch = this->peek();
  switch (nextch)
    {
      case 'E':                   // EMPTY
        if (this->parse_token (ACE_TEXT ("EMPTY")) < 0)
          {
            this->fatal_error (ACE_TEXT ("Expecting keyword EMPTY")
                               ACEXML_ENV_ARG_PARAMETER);
            ACEXML_CHECK_RETURN (-1);
          }
        break;
      case 'A':                   // ANY
        if (this->parse_token (ACE_TEXT ("ANY")) < 0)
          {
            this->fatal_error (ACE_TEXT ("Expecting keyword ANY")
                               ACEXML_ENV_ARG_PARAMETER);
            ACEXML_CHECK_RETURN (-1);
          }
        break;
      case '(':                   // children
        this->parse_children_definition (ACEXML_ENV_SINGLE_ARG_PARAMETER);
        ACEXML_CHECK_RETURN (-1);
        break;
      default:                    // error
        this->fatal_error (ACE_TEXT ("Invalid element definition")
                           ACEXML_ENV_ARG_PARAMETER);
        ACEXML_CHECK_RETURN (-1);
    }
  count = this->check_for_PE_reference (ACEXML_ENV_SINGLE_ARG_PARAMETER);
  ACEXML_CHECK_RETURN (-1);
  if (this->skip_whitespace () != '>')
    {
      this->fatal_error (ACE_TEXT ("Expecting '>' after element defintion")
                         ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }
  return 0;
}


int
ACEXML_Parser::parse_children_definition (ACEXML_ENV_SINGLE_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  this->get ();                 // consume the '('
  this->check_for_PE_reference (ACEXML_ENV_SINGLE_ARG_PARAMETER);
  ACEXML_CHECK_RETURN (-1);
  int subelement_number = 0;
  ACEXML_Char nextch = this->peek();
  switch (nextch)
    {
      case '#':                   // Mixed element,
        if (this->parse_token (ACE_TEXT ("#PCDATA")) < 0)
          {
            this->fatal_error(ACE_TEXT ("Expecting keyword '#PCDATA'")
                              ACEXML_ENV_ARG_PARAMETER);
            ACEXML_CHECK_RETURN (-1);
          }
        this->check_for_PE_reference (ACEXML_ENV_SINGLE_ARG_PARAMETER);
        ACEXML_CHECK_RETURN (-1);
        nextch = this->get();
        while (nextch == '|')
          {
            this->check_for_PE_reference (ACEXML_ENV_SINGLE_ARG_PARAMETER);
            ACEXML_CHECK_RETURN (-1);
            ACEXML_Char *name = this->parse_name ();
            // @@ name will be used in the Validator later.
            ACE_UNUSED_ARG (name);
            ++subelement_number;
            // @@ Install Mixed element name into the validator.
            this->check_for_PE_reference (ACEXML_ENV_SINGLE_ARG_PARAMETER);
            ACEXML_CHECK_RETURN (-1);
            nextch = this->skip_whitespace();
          }
        if (nextch != ')' ||
            (subelement_number && this->get () != '*'))
          {
            this->fatal_error(ACE_TEXT ("Expecing ')' or ')*' at end of Mixed")
                              ACE_TEXT (" element") ACEXML_ENV_ARG_PARAMETER);
            ACEXML_CHECK_RETURN (-1);
          }
        // @@ close the element definition in the validator.
        break;
      default:
        int status = this->parse_child (1 ACEXML_ENV_ARG_PARAMETER);
        ACEXML_CHECK_RETURN (-1);
        if (status != 0)
          return -1;
    }

  // Check for trailing '?', '*', '+'
  nextch = this->peek ();
  switch (nextch)
    {
      case '?':
        // @@ Consume the character and inform validator as such,
        this->get ();
        break;
      case '*':
        // @@ Consume the character and inform validator as such,
        this->get ();
        break;
      case '+':
        // @@ Consume the character and inform validator as such,
        this->get ();
        break;
      default:
        break;                    // not much to do.
    }

  return 0;
}

int
ACEXML_Parser::parse_child (int skip_open_paren ACEXML_ENV_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  // Conditionally consume the open paren.
  if (skip_open_paren == 0 && this->get () != '(')
    {
      this->fatal_error(ACE_TEXT ("Expecting '(' at beginning of children")
                        ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }

  ACEXML_Char node_type = 0;
  ACEXML_Char nextch;

  do {
    this->check_for_PE_reference (ACEXML_ENV_SINGLE_ARG_PARAMETER);
    ACEXML_CHECK_RETURN (-1);
    this->skip_whitespace_count (&nextch);
    switch (nextch)
      {
        case '(':
          this->check_for_PE_reference (ACEXML_ENV_SINGLE_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
          this->parse_child (0 ACEXML_ENV_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
          break;
        default:
          this->check_for_PE_reference (ACEXML_ENV_SINGLE_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
          // must be an element name here.
          ACEXML_Char *subelement = this->parse_name ();
          if (subelement == 0)
            {
              this->fatal_error(ACE_TEXT ("Invalid subelement name")
                                ACEXML_ENV_ARG_PARAMETER);
              ACEXML_CHECK_RETURN (-1);
            }
          // Check for trailing '?', '*', '+'
          nextch = this->peek ();
          switch (nextch)
            {
              case '?':
                // @@ Consume the character and inform validator as such,
                this->get ();
                break;
              case '*':
                // @@ Consume the character and inform validator as such,
                this->get ();
                break;
              case '+':
                // @@ Consume the character and inform validator as such,
                this->get ();
                break;
              default:
                break;                    // not much to do.
            }

          // @@ Inform validator of the new element here.
          break;
      }
    this->check_for_PE_reference (ACEXML_ENV_SINGLE_ARG_PARAMETER);
    ACEXML_CHECK_RETURN (-1);
    this->skip_whitespace_count (&nextch);
    switch (nextch)
      {
        case '|':
          switch (node_type)
            {
              case 0:
                node_type = '|';
                // @@ inform validator of this new type??
                break;
              case '|':
                break;
              default:
                this->fatal_error (ACE_TEXT ("Expecting `,', `|', or `)' ")
                                   ACE_TEXT ("while defining an element")
                                   ACEXML_ENV_ARG_PARAMETER);
                ACEXML_CHECK_RETURN (-1);
            }
          break;
        case ',':
          switch (node_type)
            {
              case 0:
                node_type = ',';
                // @@ inform validator of this new type??
                break;
              case ',':
                break;
              default:
                this->fatal_error (ACE_TEXT ("Expecting `,', `|', or `)' ")
                                   ACE_TEXT ("while defining an element")
                                   ACEXML_ENV_ARG_PARAMETER);
                ACEXML_CHECK_RETURN (-1);
            }
          break;
        case ')':
          break;
        default:
          this->fatal_error (ACE_TEXT ("Expecting `,', `|', or `)' ")
                             ACE_TEXT ("while defining an element")
                             ACEXML_ENV_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
      }
    nextch = this->get();  // Consume the `,' or `|' or `)'
    if (nextch == ')')
      break;
    this->check_for_PE_reference (ACEXML_ENV_SINGLE_ARG_PARAMETER);
    ACEXML_CHECK_RETURN (-1);
    this->skip_whitespace_count (&nextch);
  } while (nextch != ')');

  // Check for trailing '?', '*', '+'
  nextch = this->peek ();
  switch (nextch)
    {
      case '?':
        // @@ Consume the character and inform validator as such,
        this->get ();
        break;
      case '*':
        // @@ Consume the character and inform validator as such,
        this->get ();
        break;
      case '+':
        // @@ Consume the character and inform validator as such,
        this->get ();
        break;
      default:
        break;                    // not much to do.
    }


  return 0;
}

int
ACEXML_Parser::parse_char_reference (ACEXML_Char *buf, size_t& len)
{
  if (len < 7)   // Max size of a CharRef plus terminating '\0'
    return -1;
  ACEXML_Char ch = this->get();
  if (ch != '#')      // Internal error.
    return -1;
  int hex = 0;
  ch = this->peek();
  if (ch == 'x')
    {
      hex = 1;
      this->get ();
    }
  size_t i = 0;
  int more_digit = 0;
  ch = this->get ();
  for ( ; i < len &&
          (this->isNormalDigit (ch) || (hex ? this->isCharRef(ch): 0)); ++i)
    {
      buf[i] = ch;
      ch = this->get();
      ++more_digit;
    }
  if (ch != ';' || !more_digit)
    return -1;
  buf[i] = 0;
  ACEXML_UCS4 sum = (ACEXML_UCS4) ACE_OS::strtol (buf, 0, (hex ? 16 : 10));
  // [WFC: Legal Character]
  if (!this->isChar (sum))
    return -1;
  int clen;
#if defined (ACE_USES_WCHAR)
#  if (ACE_SIZEOF_WCHAR == 2)    // UTF-16
  if ((clen = ACEXML_Transcoder::ucs42utf16 (sum, buf, len)) < 0)
    return -1;
#  elif (ACE_SIZEOF_WCHAR == 4)  // UCS 4
  buf [0] = sum;
  buf [1] = 0;
  clen = 2;
#  endif /* ACE_SIZEOF_WCHAR */

#else                          // or UTF-8
  if ((clen = ACEXML_Transcoder::ucs42utf8 (sum, buf, len)) < 0)
    return -1;
#endif
  buf [clen] = 0;
  len = clen;
  return 0;
}

ACEXML_Char*
ACEXML_Parser::parse_reference_name (void)
{
  ACEXML_Char ch = this->get ();
  if (!this->isLetter (ch) && (ch != '_' || ch != ':'))
    return 0;
  while (ch) {
    this->alt_stack_.grow (ch);
    ch = this->peek ();
    if (!this->isNameChar (ch))
      break;
    ch = this->get ();
  };
  if (ch != ';')
    return 0;
  ch = this->get();
  return this->alt_stack_.freeze ();
}

int
ACEXML_Parser::parse_attvalue (ACEXML_Char *&str ACEXML_ENV_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  ACEXML_Char quote = this->get ();
  if (quote != '\'' && quote != '"')  // Not a quoted string.
    return -1;
  ACEXML_Char ch = this->get ();
  while (1)
    {
      if (ch == quote)
        {
          ACEXML_Char* temp = this->obstack_.freeze ();
          // If the attribute type is not CDATA, then the XML processor
          // must further process the normalized attribute value by
          // discarding any leading and trailing space (#x20) characters,
          // and by replacing sequences of space (#x20) characters by a
          // single space (#x20) character.

          //           if (atttype != CDATA) {
          //             ACEXML_Char* start = temp;
          //             ACEXML_Char* end = temp + ACE_OS::strlen (temp);
          //             while (*start == '\x20')
          //               start++;
          //             if (start == end) // String which is all spaces
          //               str = start;
          //             while (*start != 0)
          //               {
          //                 this->obstack_.grow (*start);
          //                 start++;
          //                 while (*start == '\x20')
          //                   start++;
          //               }
          //             str = this->obstack_.freeze();
          //           }
          str = temp;
          return 0;
        }
      switch (ch)
        {
          case '&':
            if (this->peek () == '#')
              {
                ACEXML_Char buf[7];
                size_t len = sizeof (buf);
                if (this->parse_char_reference (buf, len) != 0)
                  {
                    // [WFC: Legal Character]
                    this->fatal_error (ACE_TEXT ("Invalid CharacterRef")
                                       ACEXML_ENV_ARG_PARAMETER);
                    ACEXML_CHECK_RETURN (-1);
                  }
                for (size_t j = 0; j < len; ++j)
                  this->obstack_.grow (buf[j]);
              }
            else
              {
                this->ref_state_ = ACEXML_ParserInt::IN_ATT_VALUE;
                this->parse_entity_reference (ACEXML_ENV_SINGLE_ARG_PARAMETER);
                ACEXML_CHECK_RETURN (-1);
              }
            break;
          case '\x20': case '\x0D': case '\x0A': case '\x09':
            this->obstack_.grow ('\x20');
            break;
          case '<': // [WFC: No < in Attribute Values]
            this->fatal_error (ACE_TEXT ("Illegal '<' in AttValue")
                               ACEXML_ENV_ARG_PARAMETER);
            ACEXML_CHECK_RETURN (-1);
            break;
          case 0:
            this->pop_context (1 ACEXML_ENV_ARG_PARAMETER);
            ACEXML_CHECK_RETURN (-1);
            break;
          default:
            this->obstack_.grow (ch);
            break;
        }
      ch = this->get();
    }
}



int
ACEXML_Parser::parse_entity_reference (ACEXML_ENV_SINGLE_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  ACEXML_Char* replace = this->parse_reference_name ();
  if (replace == 0)
    {
      this->fatal_error (ACE_TEXT ("Invalid Reference name")
                         ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }

  //  [WFC: Parsed Entity]
  if (this->unparsed_entities_.resolve_entity (replace)) {
    this->fatal_error (ACE_TEXT ("EntityRef refers to unparsed entity")
                       ACEXML_ENV_ARG_PARAMETER);
    ACEXML_CHECK_RETURN (-1);
  }
  // Look in the internal general entities set first.
  const ACEXML_Char* entity = this->internal_GE_.resolve_entity(replace);

  // Look in the predefined entities.
  if (!entity)
    {
      entity = this->predef_entities_.resolve_entity (replace);
      if (entity)
        {
          // Special case to return the length in case of predefined entities
          this->obstack_.grow (*entity);
          return 1;
        }
    }

  if (!this->validate_)
    {
      if (this->standalone_)
        {
          // [WFC: Entity Declared]
          this->fatal_error (ACE_TEXT ("Undeclared Entity reference")
                             ACEXML_ENV_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
        }
      else
        {
          this->content_handler_->skippedEntity (replace
                                                 ACEXML_ENV_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
          return 0;
        }
    }

  // No match in internal subset
  if (!entity
          // or No DTDs
      && (!(this->internal_dtd_ || this->external_dtd_)
          // or Only Internal DTD and no parameter entity references
          || (this->internal_dtd_ && !this->external_dtd_
              && !this->has_pe_refs_)
          // or Standalone = 'yes'
          || this->standalone_))
    {
      // [WFC: Entity Declared]
      this->fatal_error (ACE_TEXT ("Undeclared Entity reference")
                         ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }

  ACEXML_Char* systemId = 0;
  ACEXML_Char* publicId = 0;
  if (!entity)
    {
      if (this->external_GE_.resolve_entity (replace, systemId, publicId) < 0)
        {
          this->fatal_error (ACE_TEXT ("Undeclared Entity reference")
                             ACEXML_ENV_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
        }
      if (this->ref_state_ == ACEXML_ParserInt::IN_ATT_VALUE)
        {
          this->fatal_error (ACE_TEXT ("External EntityRef in Attribute Value")
                             ACEXML_ENV_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
        }
      this->external_entity_++;
    }


  // [WFC: No Recursion]
  ACEXML_Char* ref_name = replace;
  int present = this->GE_reference_.insert (ref_name);
  if (present == 1 || present == -1)
    {
      while (this->GE_reference_.pop(ref_name) != -1)
        ;
      this->fatal_error (ACE_TEXT ("Recursion in resolving entity")
                         ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }

  if (!this->external_entity_)
    {
      ACEXML_StrCharStream* str = 0;
      ACE_NEW_RETURN (str, ACEXML_StrCharStream, -1);
      if (str->open (entity, replace) < 0
          || this->switch_input (str, replace) != 0)
        {
          this->fatal_error (ACE_TEXT ("Unable to create internal input ")
                             ACE_TEXT ("stream")
                             ACEXML_ENV_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
        }
      return 0;
    }
  else
    {
      ACEXML_Char* uri = this->normalize_systemid (systemId);
      ACE_Auto_Basic_Array_Ptr<ACEXML_Char> cleanup_uri (uri);
      ACEXML_InputSource* ip = 0;
      if (this->entity_resolver_)
        {
          ip = this->entity_resolver_->resolveEntity (publicId,
                                                      (uri ? uri : systemId)
                                                      ACEXML_ENV_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
          if (ip)
            {
              if (this->switch_input (ip, (uri ? uri : systemId),
                                      publicId) != 0)
                {
                  this->fatal_error (ACE_TEXT ("Internal Parser Error")
                                     ACEXML_ENV_ARG_PARAMETER);
                  ACEXML_CHECK_RETURN (-1);
                }
              return 0;
            }
        }
      ACEXML_StreamFactory factory;
      ACEXML_CharStream* cstream = factory.create_stream (uri ? uri: systemId);
      if (!cstream) {
        this->fatal_error (ACE_TEXT ("Invalid input source")
                           ACEXML_ENV_ARG_PARAMETER);
        ACEXML_CHECK_RETURN (-1);
      }
      if (this->switch_input (cstream, systemId, publicId) != 0)
        {
          this->fatal_error (ACE_TEXT ("Internal Parser Error")
                             ACEXML_ENV_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
        }
    }
  return 0;
}

int
ACEXML_Parser::parse_PE_reference (ACEXML_ENV_SINGLE_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  ACEXML_Char* replace = this->parse_reference_name ();
  if (replace == 0)
    {
      this->fatal_error (ACE_TEXT ("Invalid PEReference name")
                         ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }

  // Look in the internal general entities set first.
  const ACEXML_Char* entity = this->internal_PE_.resolve_entity(replace);

  if (!entity &&                     // No match in internal
      (!this->external_dtd_ ||       // or No External DTDs
       this->standalone_))           // or Standalone
    {
      // [VC: Entity Declared]
      this->fatal_error (ACE_TEXT ("Undefined Internal PEReference")
                         ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }

  ACEXML_Char* systemId = 0;
  ACEXML_Char* publicId = 0;
  if (!entity && this->validate_)
    {
      if (this->external_PE_.resolve_entity (replace, systemId, publicId) < 0)
        {
          this->fatal_error (ACE_TEXT ("Undefined PEReference")
                             ACEXML_ENV_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
        }
      this->external_entity_++;
    }

  // [WFC: No Recursion]
  ACEXML_Char* ref_name = replace;
  int present = this->PE_reference_.insert (ref_name);
  if (present == 1 || present == -1)
    {
      while (this->PE_reference_.pop(ref_name) != -1)
        ;
      this->fatal_error (ACE_TEXT ("Recursion in resolving entity")
                         ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }

  if (entity && !this->external_entity_)
    {
      ACEXML_StrCharStream* sstream = 0;
      ACEXML_String str (entity);
      if (this->ref_state_ != ACEXML_ParserInt::IN_ENTITY_VALUE)
        {
          const ACEXML_Char* ch = ACE_TEXT (" ");
          str = ch + str + ch;
        }
      //  ACE_DEBUG ((LM_DEBUG,
      //             ACE_TEXT ("Entity is %s\n Replacement Text is : %s\n"),
      //             replace, str.c_str()));
      ACE_NEW_RETURN (sstream, ACEXML_StrCharStream, -1);
      if (sstream->open (str.c_str(), replace) < 0
          || this->switch_input (sstream, replace) != 0)
        {
          this->fatal_error (ACE_TEXT ("Error in switching InputSource")
                             ACEXML_ENV_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
        }
      return 0;
    }
  else if (this->external_entity_ && this->validate_)
    {
      ACEXML_Char* uri = this->normalize_systemid (systemId);
      ACE_Auto_Basic_Array_Ptr<ACEXML_Char> cleanup_uri (uri);
      ACEXML_InputSource* ip = 0;
      if (this->entity_resolver_)
        {
          ip = this->entity_resolver_->resolveEntity (publicId,
                                                      (uri ? uri : systemId)
                                                      ACEXML_ENV_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
        }
      if (ip)
        {
          if (this->switch_input (ip, (uri ? uri : systemId), publicId) != 0)
            {
              this->fatal_error (ACE_TEXT ("Error in switching InputSource")
                                 ACEXML_ENV_ARG_PARAMETER);
              ACEXML_CHECK_RETURN (-1);
            }
          return 0;
        }
      else
        {
          ACEXML_StreamFactory factory;
          ACEXML_CharStream* cstream = factory.create_stream (uri ? uri: systemId);
          if (!cstream) {
            this->fatal_error (ACE_TEXT ("Invalid input source")
                               ACEXML_ENV_ARG_PARAMETER);
            ACEXML_CHECK_RETURN (-1);
          }
          if (this->switch_input (cstream, systemId, publicId) != 0)
            {
              this->fatal_error (ACE_TEXT ("Error in switching InputSource")
                                 ACEXML_ENV_ARG_PARAMETER);
              ACEXML_CHECK_RETURN (-1);
            }
          if (this->ref_state_ == ACEXML_ParserInt::IN_ENTITY_VALUE)
            {
              ACEXML_Char less, mark;
              if (this->peek() == '<')
                {
                  less = this->get();
                  if (this->peek() == '?')
                    {
                      mark = this->get();
                      if (this->peek() == 'x')
                        {
                          this->parse_text_decl (ACEXML_ENV_SINGLE_ARG_PARAMETER);
                          ACEXML_CHECK_RETURN (-1);
                        }
                      else
                        {
                          this->obstack_.grow (less);
                          this->obstack_.grow (mark);
                        }
                    }
                  this->obstack_.grow (less);
                }
            }
          return 0;
        }
    }
  this->fatal_error (ACE_TEXT ("Undefined PEReference")
                     ACEXML_ENV_ARG_PARAMETER);
  ACEXML_CHECK_RETURN (-1);
  return -1;
}

int
ACEXML_Parser::parse_entity_value (ACEXML_Char *&str
                                   ACEXML_ENV_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  ACEXML_ParserInt::ReferenceState temp = this->ref_state_;
  ACEXML_Char quote = this->get ();
  if (quote != '\'' && quote != '"')  // Not a quoted string.
    return -1;
  ACEXML_Char ch = this->get ();
  while (1)
    {
      if (ch == quote)
        {
          str = this->obstack_.freeze ();
          this->ref_state_ = temp;
          return 0;
        }
      switch (ch)
        {
          case '&':
            if (this->peek () == '#')
              {
                if (!this->external_entity_)
                  {
                    ACEXML_Char buf[7];
                    size_t len = sizeof (buf);
                    if (this->parse_char_reference (buf, len) != 0)
                      {
                        // [WFC: Legal Character]
                        this->fatal_error (ACE_TEXT ("Invalid character ")
                                           ACE_TEXT ("reference")
                                           ACEXML_ENV_ARG_PARAMETER);
                        return -1;
                      }
                    for (size_t j = 0; j < len; ++j)
                      this->obstack_.grow (buf[j]);
                    break;
                  }
              }
            this->obstack_.grow (ch);
            break;
          case '%':
            if (!this->external_entity_)
              {
                this->ref_state_ = ACEXML_ParserInt::IN_ENTITY_VALUE;
                this->parse_PE_reference(ACEXML_ENV_SINGLE_ARG_PARAMETER);
                ACEXML_CHECK_RETURN (-1);
                break;
              }
            this->obstack_.grow (ch);
            break;
          case 0:
            this->pop_context (0 ACEXML_ENV_ARG_PARAMETER);
            ACEXML_CHECK_RETURN (-1);
            break;
          default:
            this->obstack_.grow (ch);
            break;
        }
      ch = this->get();
    }
}

ACEXML_Char *
ACEXML_Parser::parse_name (ACEXML_Char ch)
{
  if (ch == 0)
    ch = this->get ();
  if (!this->isLetter (ch) && ch != '_' && ch != ':')
    return 0;
  while (ch) {
    this->obstack_.grow (ch);
    ch = this->peek ();
    if (!this->isNameChar (ch))
      break;
    ch = this->get ();
  };
  return this->obstack_.freeze ();
}

ACEXML_Char*
ACEXML_Parser::parse_nmtoken (ACEXML_Char ch)
{
  if (ch == 0)
    ch = this->get ();
  if (!this->isNameChar (ch))
    return 0;
  while (ch) {
    this->obstack_.grow (ch);
    ch = this->peek ();
    if (!this->isNameChar (ch))
      break;
    ch = this->get ();
  };
  return this->obstack_.freeze ();
}

int
ACEXML_Parser::parse_version_num (ACEXML_Char*& str)
{
  ACEXML_Char quote = this->get ();
  if (quote != '\'' && quote != '"')  // Not a quoted string.
    return -1;
  int numchars = 0;
  while (1)
    {
      ACEXML_Char ch = this->get ();
      if (ch == quote && !numchars)
        return -1;
      else if (ch == quote)
        {
          str = this->obstack_.freeze ();
          return 0;
        }
      // [26]    VersionNum ::= ([a-zA-Z0-9_.:] | '-')+
      if (ch == '-' || ((ch >= 'a' && ch <= 'z') ||
                        (ch >= 'A' && ch <= 'Z') ||
                        (ch >= '0' && ch <= '9') ||
                        (ch == '_' || ch == '.' || ch == ':')))
        {
          this->obstack_.grow (ch);
          numchars++;
        }
      else
        return -1;
    }
}

int
ACEXML_Parser::parse_system_literal (ACEXML_Char*& str)
{
  const ACEXML_Char quote = this->get();
  if (quote != '\'' && quote != '"')  // Not a quoted string.
    return -1;
  while (1)
    {
      ACEXML_Char ch = this->get ();
      if (ch == quote)
        {
          str = this->obstack_.freeze ();
          return 0;
        }
      switch (ch)
        {
          case '\x00': case '\x01': case '\x02': case '\x03': case '\x04':
          case '\x05': case '\x06': case '\x07': case '\x08': case '\x09':
          case '\x0A': case '\x0B': case '\x0C': case '\x0D': case '\x0E':
          case '\x0F': case '\x10': case '\x11': case '\x12': case '\x13':
          case '\x14': case '\x15': case '\x16': case '\x17': case '\x18':
          case '\x19': case '\x1A': case '\x1B': case '\x1C': case '\x1D':
          case '\x1E': case '\x1F': case '\x7F': case '\x20': case '<':
          case '>': case '#': case '%':
            ACE_ERROR ((LM_ERROR,
                        ACE_TEXT ("Invalid char %c in SystemLiteral\n"), ch));
            return -1;
          default:
            this->obstack_.grow (ch);
        }
    }
}

int
ACEXML_Parser::parse_pubid_literal (ACEXML_Char*& str)
{
  const ACEXML_Char quote = this->get();
  if (quote != '\'' && quote != '"')  // Not a quoted string.
    return -1;
  while (1)
    {
      ACEXML_Char ch = this->get ();
      if (ch == quote)
        {
          str = this->obstack_.freeze ();
          return 0;
        }
      else if (this->isPubidChar (ch))
        this->obstack_.grow (ch);
      else
        return -1;
    }
}

int
ACEXML_Parser::parse_encname (ACEXML_Char*& str)
{
  const ACEXML_Char quote = this->get ();
  if (quote != '\'' && quote != '"')  // Not a quoted string.
    return -1;
  int numchars = 0;
  while (1)
    {
      ACEXML_Char ch = this->get ();
      if (ch == quote && !numchars)
        return -1;
      else if (ch == quote)
        {
          str = this->obstack_.freeze ();
          return 0;
        }
      // [81]    EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')*
      if (!((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))
          && !numchars)
        return -1;
      if (ch == '-' || ((ch >= 'a' && ch <= 'z') ||
                        (ch >= 'A' && ch <= 'Z') ||
                        (ch >= '0' && ch <= '9') ||
                        (ch == '_' || ch == '.')))
        {
          this->obstack_.grow (ch);
          numchars++;
        }
      else
        return -1;
    }
}

int
ACEXML_Parser::parse_sddecl (ACEXML_Char*& str)
{
  ACEXML_Char quote = this->get ();
  if (quote != '\'' && quote != '"')  // Not a quoted string.
    return -1;
  int numchars = 0;
  while (1)
    {
      ACEXML_Char ch = this->get ();
      if (ch == quote && numchars < 2)
        return -1;
      else if (ch == quote)
        {
          str = this->obstack_.freeze ();
          return 0;
        }
      // [32] SDDecl ::= S 'standalone' Eq (("'" ('yes' | 'no') "'")
      //                                   | ('"' ('yes' | 'no') '"'))
      switch (ch)
        {
          case 'y': case 'e': case 's': case 'n': case 'o':
            this->obstack_.grow (ch);
            numchars++;
            break;
          default:
            return -1;
        }
    }
}

void
ACEXML_Parser::prefix_mapping (const ACEXML_Char* prefix,
                               const ACEXML_Char* uri,
                               const ACEXML_Char* name,
                               int start ACEXML_ENV_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  if (this->namespaces_)
    {
      const ACEXML_Char* temp = (name == 0) ? empty_string : prefix;
      if (start) {
        this->content_handler_->startPrefixMapping (temp, uri
                                                    ACEXML_ENV_ARG_PARAMETER);
        ACEXML_CHECK;
      }
      else
        {
          this->content_handler_->endPrefixMapping(temp
                                                   ACEXML_ENV_ARG_PARAMETER);
          ACEXML_CHECK;
        }
    }
}

int
ACEXML_Parser::switch_input (ACEXML_CharStream* cstream,
                             const ACEXML_Char* systemId,
                             const ACEXML_Char* publicId)
{
  ACEXML_InputSource* input = 0;
  ACE_NEW_RETURN (input, ACEXML_InputSource (cstream), -1);
  return this->switch_input (input, systemId, publicId);
}

int
ACEXML_Parser::switch_input (ACEXML_InputSource* input,
                             const ACEXML_Char* systemId,
                             const ACEXML_Char* publicId)
{
  ACEXML_LocatorImpl* locator = 0;
  if (!systemId)
    systemId = input->getSystemId();
  ACE_NEW_RETURN (locator, ACEXML_LocatorImpl (systemId, publicId), -1);
  ACEXML_Parser_Context* new_context = 0;
  ACE_NEW_RETURN (new_context, ACEXML_Parser_Context(input, locator), -1);
  if (this->push_context (new_context) != 0)
    {
      ACE_ERROR ((LM_ERROR, "Unable to switch input streams"));
      delete new_context;
      return -1;
    }
  this->current_ = new_context;
  this->content_handler_->setDocumentLocator (this->current_->getLocator());
  return 0;
}

int
ACEXML_Parser::push_context (ACEXML_Parser_Context* context)
{
  if (this->ctx_stack_.push (context) < 0)
    {
      ACE_ERROR ((LM_ERROR, "Unable to push input source onto the stack"));
      return -1;
    }
  return 0;
}

int
ACEXML_Parser::pop_context (int GE_ref ACEXML_ENV_ARG_DECL)
{
  int nrelems = this->ctx_stack_.size();
  if (nrelems <= 1)
    {
      this->fatal_error(ACE_TEXT ("Unexpected end-of-file")
                        ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }

  ACEXML_Parser_Context* temp = 0;
  int retval = this->ctx_stack_.pop (temp);
  if (retval != 0)
    {
      this->fatal_error (ACE_TEXT ("Unable to pop element of the input stack")
                         ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }
  delete temp;
  if (this->ctx_stack_.top (this->current_) != 0)
    {
      this->fatal_error (ACE_TEXT ("Unable to read top element of input stack")
                         ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }
  ACEXML_Char* reference = 0;
  if (GE_ref == 1 && this->GE_reference_.size() > 0)
    {
      if (this->GE_reference_.pop (reference) < 0)
        {
          this->fatal_error (ACE_TEXT ("Internal Parser Error")
                             ACEXML_ENV_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
        }
    }
  else if (GE_ref == 0 && this->PE_reference_.size() > 0)
    {
      if (this->PE_reference_.pop (reference) < 0)
        {
          this->fatal_error (ACE_TEXT ("Internal Parser Error")
                             ACEXML_ENV_ARG_PARAMETER);
          ACEXML_CHECK_RETURN (-1);
        }
    }
  nrelems = this->ctx_stack_.size();

  if (this->external_entity_ && (GE_ref == 0 || GE_ref == 1))
    this->external_entity_--;

  this->content_handler_->setDocumentLocator (this->current_->getLocator());

  return nrelems;
}

int
ACEXML_Parser::getFeature (const ACEXML_Char *name ACEXML_ENV_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXNotRecognizedException,
                   ACEXML_SAXNotSupportedException))
{
  if (ACE_OS::strcmp (name, ACEXML_Parser::simple_parsing_feature_) == 0)
    {
      return this->simple_parsing_;
    }
  else if (ACE_OS::strcmp (name, ACEXML_Parser::namespaces_feature_) == 0)
    {
      return this->namespaces_;
    }
  else if (ACE_OS::strcmp (name,
                           ACEXML_Parser::namespace_prefixes_feature_) == 0)
    {
      return this->namespace_prefixes_;
    }
  else if (ACE_OS::strcmp (name, ACEXML_Parser::validation_feature_) == 0)
    {
      return this->validate_;
    }
  ACEXML_THROW_RETURN (ACEXML_SAXNotRecognizedException (name), -1);
}



void
ACEXML_Parser::setFeature (const ACEXML_Char *name,
                           int boolean_value ACEXML_ENV_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXNotRecognizedException,
                   ACEXML_SAXNotSupportedException))
{
  if (ACE_OS::strcmp (name, ACEXML_Parser::simple_parsing_feature_) == 0)
    {
      this->simple_parsing_ = (boolean_value == 0 ? 0 : 1);
      return;
    }
  else if (ACE_OS::strcmp (name, ACEXML_Parser::namespaces_feature_) == 0)
    {
      this->namespaces_ = (boolean_value == 0 ? 0 : 1);
      return;
    }
  else if (ACE_OS::strcmp (name,
                           ACEXML_Parser::namespace_prefixes_feature_) == 0)
    {
      this->namespace_prefixes_ = (boolean_value == 0 ? 0 : 1);
      return;
    }
  else if (ACE_OS::strcmp (name, ACEXML_Parser::validation_feature_) == 0)
    {
      this->validate_ = (boolean_value == 0 ? 0 : 1);
      return;
    }

  ACEXML_THROW (ACEXML_SAXNotRecognizedException (name));
}

void *
ACEXML_Parser::getProperty (const ACEXML_Char *name ACEXML_ENV_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXNotRecognizedException,
                   ACEXML_SAXNotSupportedException))
{
  ACEXML_THROW_RETURN (ACEXML_SAXNotSupportedException (name), 0);
}

void
ACEXML_Parser::setProperty (const ACEXML_Char *name,
                            void *value ACEXML_ENV_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXNotRecognizedException,
                   ACEXML_SAXNotSupportedException))
{
  ACE_UNUSED_ARG (value);

  ACEXML_THROW (ACEXML_SAXNotSupportedException (name));
}

void
ACEXML_Parser::error (const ACEXML_Char* msg ACEXML_ENV_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  ACEXML_SAXParseException* exception = 0;
  ACE_NEW_NORETURN (exception, ACEXML_SAXParseException (msg));
  if (this->error_handler_)
      this->error_handler_->error (*exception ACEXML_ENV_ARG_PARAMETER);
  else
    ACEXML_ENV_RAISE (exception);
  return;
}

void
ACEXML_Parser::warning (const ACEXML_Char* msg ACEXML_ENV_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  ACEXML_SAXParseException* exception = 0;
  ACE_NEW_NORETURN (exception, ACEXML_SAXParseException (msg));
  if (this->error_handler_)
    this->error_handler_->warning (*exception ACEXML_ENV_ARG_PARAMETER);
  delete exception;
  return;
}

void
ACEXML_Parser::fatal_error (const ACEXML_Char* msg ACEXML_ENV_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  ACEXML_SAXParseException* exception = 0;
  ACE_NEW_NORETURN (exception, ACEXML_SAXParseException (msg));
  if (this->error_handler_)
    this->error_handler_->fatalError (*exception ACEXML_ENV_ARG_PARAMETER);
  this->reset();
  ACEXML_ENV_RAISE (exception);
  return;
}

void
ACEXML_Parser::parse_version_info (ACEXML_ENV_SINGLE_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  ACEXML_Char* astring;
  if (this->parse_token (ACE_TEXT("ersion")) < 0
      || this->skip_equal () != 0
      || this->parse_version_num (astring) != 0)
    {
      this->fatal_error (ACE_TEXT ("Invalid VersionInfo specification")
                         ACEXML_ENV_ARG_PARAMETER);
      return;
    }
  if (ACE_OS::strcmp (astring, ACE_TEXT ("1.0")) != 0)
    {
      this->fatal_error (ACE_TEXT ("ACEXML Parser supports XML version 1.0 ")
                         ACE_TEXT ("documents only") ACEXML_ENV_ARG_PARAMETER);
      return;
    }
}

void
ACEXML_Parser::parse_encoding_decl (ACEXML_ENV_SINGLE_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  ACEXML_Char* astring;
  if ((this->parse_token (ACE_TEXT("ncoding")) < 0)
      || this->skip_equal () != 0
      || this->parse_encname (astring) != 0)
    {
      this->fatal_error (ACE_TEXT ("Invalid EncodingDecl specification")
                         ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK;
    }
  const ACEXML_Char* encoding = this->current_->getInputSource()->getEncoding();
  if (encoding != 0 && ACE_OS::strcmp (astring, encoding) != 0)
    {
      ACE_ERROR ((LM_ERROR, ACE_TEXT ("Detected Encoding is %s ")
                  ACE_TEXT (": Declared Encoding is %s\n"),
                  encoding, astring));
      this->warning (ACE_TEXT ("Declared encoding differs from detected ")
                     ACE_TEXT ("encoding") ACEXML_ENV_ARG_PARAMETER);
    }
}

int
ACEXML_Parser::parse_text_decl (ACEXML_ENV_SINGLE_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  // Read xml
  if (this->parse_token (ACE_TEXT("xml")) < 0)
    {
      this->fatal_error(ACE_TEXT ("Expecting keyword 'xml' in TextDecl")
                        ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }

  ACEXML_Char fwd = this->skip_whitespace();
  // Read version
  if (fwd == 'v')
    {
      this->parse_version_info (ACEXML_ENV_SINGLE_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
      fwd = this->skip_whitespace();
    }

  if (fwd == 'e')
    {
      this->parse_encoding_decl (ACEXML_ENV_SINGLE_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
      fwd = this->skip_whitespace();
    }
  else
    {
      this->fatal_error (ACE_TEXT ("Missing encodingDecl in TextDecl")
                         ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }

  if (fwd == '?' && this->get() == '>')
    return 0;
  // All the rules fail. So return an error.
  this->fatal_error (ACE_TEXT ("Invalid TextDecl") ACEXML_ENV_ARG_PARAMETER);
  ACEXML_CHECK_RETURN (-1);
  return -1;
}

void
ACEXML_Parser::parse_xml_decl (ACEXML_ENV_SINGLE_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  // Read <?xml
  if (this->parse_token (ACE_TEXT("xml")) < 0)
    {
      this->fatal_error(ACE_TEXT ("Expecting keyword xml in XMLDecl")
                        ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK;
    }

  ACEXML_Char fwd = this->skip_whitespace();

  // Read version
  if (fwd != 'v')
    {
      this->fatal_error (ACE_TEXT ("Expecting VersionInfo declaration")
                         ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK;
    }

  this->parse_version_info (ACEXML_ENV_SINGLE_ARG_PARAMETER);
  ACEXML_CHECK;

  fwd = this->skip_whitespace();
  if (fwd != '?')
    {
      if (fwd == 'e')
        {
          this->parse_encoding_decl (ACEXML_ENV_SINGLE_ARG_PARAMETER);
          ACEXML_CHECK;
          fwd = this->skip_whitespace();
        }
      if (fwd == 's')
        {
          ACEXML_Char* astring;
          if ((this->parse_token (ACE_TEXT("tandalone")) == 0) &&
              this->skip_equal () == 0 &&
              this->parse_sddecl (astring) == 0)
            {
              if (ACE_OS::strcmp (astring, ACE_TEXT ("yes")) == 0)
                this->standalone_ = 1;
              fwd = this->skip_whitespace();
            }
        }
    }
  if (fwd == '?' && this->get() == '>')
    return;
  // All the rules fail. So return an error.
  this->fatal_error (ACE_TEXT ("Invalid XMLDecl declaration")
                     ACEXML_ENV_ARG_PARAMETER);
  ACEXML_CHECK;
}

int
ACEXML_Parser::parse_comment (void)
{
  int state = 0;

  if (this->get () != '-' ||    // Skip the opening "<!--"
      this->get () != '-' ||    // completely.
      this->get () == '-')      // and at least something not '-'.
    return -1;

  while (state < 3)
    // Waiting for the trailing three character '-->'. Notice that
    // according to the spec, '--->' is not a valid closing comment
    // sequence. But we'll let it pass anyway.
    {
      ACEXML_Char fwd = this->get ();
      if ((fwd == '-' && state < 2) ||
          (fwd == '>' && state == 2))
        state += 1;
      else
        state = 0;              // Reset parse state.
    }
  return 0;
}

int
ACEXML_Parser::parse_processing_instruction (ACEXML_ENV_SINGLE_ARG_DECL)
  ACE_THROW_SPEC ((ACEXML_SAXException))
{
  const ACEXML_Char *pitarget = this->parse_name ();
  ACEXML_Char *instruction = 0;

  if (!ACE_OS::strcasecmp (ACE_TEXT ("xml"), pitarget))
    {
      // Invalid PITarget name.
      this->fatal_error(ACE_TEXT ("PI can't have 'xml' in PITarget")
                        ACEXML_ENV_ARG_PARAMETER);
      ACEXML_CHECK_RETURN (-1);
    }

  int state = 0;

  ACEXML_Char ch = this->skip_whitespace();
  while (state < 2)
    {
      switch (ch)
        {
          case '?':
            if (state == 0)
              state = 1;
            break;
          case '>':
            if (state == 1)
              {
                instruction = this->obstack_.freeze ();
                this->content_handler_->processingInstruction (pitarget,
                                                               instruction
                                                               ACEXML_ENV_ARG_PARAMETER);
                ACEXML_CHECK_RETURN (-1);
                this->obstack_.unwind (ACE_const_cast (ACEXML_Char*, pitarget));
                return 0;
              }
            break;
          case 0x0A:
            // Fall thru...
          default:
            if (state == 1)
              this->obstack_.grow ('?');
            this->obstack_.grow (ch);
            state = 0;
        }
      ch = this->get ();
    }
  return -1;
}

void
ACEXML_Parser::reset (void)
{
  this->doctype_ = 0;
  if (this->ctx_stack_.pop (this->current_) == -1)
    ACE_ERROR ((LM_ERROR,
                ACE_TEXT ("Mismatched push/pop of Context stack")));
  if (this->current_)
    {
      this->current_->getInputSource()->getCharStream()->rewind();

      this->current_->setInputSource (0);
      delete this->current_;
      this->current_ = 0;
    }

  ACEXML_Char* temp = 0;
  while (this->GE_reference_.pop (temp) != -1)
    ;
  while (this->PE_reference_.pop (temp) != -1)
    ;
  this->obstack_.release();
  this->alt_stack_.release();
  this->xml_namespace_.reset();
  this->nested_namespace_ = 0;
  this->internal_GE_.reset();
  this->external_GE_.reset();
  this->unparsed_entities_.reset();
  this->predef_entities_.reset();
  this->internal_PE_.reset();
  this->external_PE_.reset();
  this->notations_.reset();
  this->ref_state_ = ACEXML_ParserInt::INVALID;
  this->external_subset_ = 0;
  this->external_entity_ = 0;
  this->has_pe_refs_ = 0;
  this->standalone_ = 0;
  this->external_dtd_ = 0;
  this->internal_dtd_ = 0;
}

#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION)

template class ACE_Unbounded_Stack<ACEXML_Parser_Context*>;
template class ACE_Unbounded_Set_Iterator<ACEXML_Parser_Context*>;
template class ACE_Node<ACEXML_Parser_Context*>;
template class ACE_Unbounded_Stack<ACEXML_Char *>;
template class ACE_Node<ACEXML_Char *>;
template class ACE_Unbounded_Set_Iterator<ACEXML_Char*>;

#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA)

#pragma instantiate ACE_Unbounded_Stack<ACEXML_Parser_Context*>
#pragma instantiate ACE_Unbounded_Set_Iterator<ACEXML_Parser_Context*>;
#pragma instantiate ACE_Node<ACEXML_Parser_Context*>
#pragma instantiate ACE_Unbounded_Stack<ACEXML_Char*>
#pragma instantiate ACE_Unbounded_Set_Iterator<ACEXML_Char*>;
#pragma instantiate ACE_Node<ACEXML_Char *>

#endif /*ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION*/
