2005/5/9

     
 

XMLReaderImpl.cpp

artefaktur
// -*- mode:C++; tab-width:2; c-basic-offset:2; indent-tabs-mode:nil -*- 
//
// Copyright (C) 2000-2005 by Roger Rene Kommer / artefaktur, Kassel, Germany.
// 
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public License (LGPL).
// 
// 
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the 
// License ACDK-FreeLicense document enclosed in the distribution
// for more for more details.
// This file is part of the Artefaktur Component Development Kit:
//                         ACDK
// 
// Please refer to
// - http://www.acdk.de
// - http://www.artefaktur.com
// - http://acdk.sourceforge.net
// for more information.
// 
// $Header: /cvsroot/acdk/acdk/acdk_xml/src/acdk/xml/sax/XMLReaderImpl.cpp,v 1.13 2005/02/13 03:25:57 kommer Exp $

#include "XMLReader.h"
#include "NamedBufferReader.h"
#include "AttributeListImpl.h"

#include <acdk/net/URL.h>
#include <acdk/io/MemWriter.h>
#include <org/xml/sax/helpers/LocatorImpl.h>
#include <org/xml/sax/helpers/AttributesImpl.h>

#include "LibXMLInternals.h"
#include "XmlLibLocator.h"
#include <org/xml/sax/helpers/LocatorImpl.h>
#include <org/xml/sax/helpers/NamespaceSupport.h>
#include "../libxmldom/LibXMLDocument.h"
#include "../../../libxml/HTMLparser.h"

#include <stdio.h> 
#include <stdarg.h>


namespace acdk {
namespace xml {
namespace sax {

//#define LOCAL_DEBUG
#if defined(LOCAL_DEBUG)
#define DOUT(msg) std::cout << SBSTR(msg)->convert(CCUtf8)->c_str() << std::endl;
#else
/FONT>
#define DOUT(msg)
#endif
/FONT>

const char* feature_list[] = 
{
  "external-general-entities",
  "external-parameter-entities",
  "is-standalone",
  "lexical-handler/parameter-entities",
  "namespaces",
  "namespace-prefixes",
  "resolve-dtd-uris",
  "string-interning",
  "use-attributes2",
  "use-locator2",
  "use-entity-resolver2",
  "validation",
  0
};


bool containsInFeatureList(IN(RString) str)
{
  for (int i = 0; feature_list[i] != 0; ++i)
  {
    const char* feature = feature_list[i];
    if (str->equals(feature) == true)
      return true;
  }
  return false;
}

RString 
XMLReader::getURI(IN(RString) prefix)
{
  if (useNamespace() == true)
    return Nil;
  return _ns->getURI(prefix);
}
bool XMLReader::hasAlreadyFatalError() { return _hasAlreadyFatal; }
bool XMLReader::setFatalError() { return (_hasAlreadyFatal = true); }

bool 
XMLReader::getFeature(IN(RString) name) THROWS2(org::xml::sax::RSAXNotRecognizedException, org::xml::sax::RSAXNotSupportedException)
{
  _checkFeatureName(name);
  RString key = name->substring(strlen(FEATURES_PREFIX));
  if (key->equals("external-general-entities") == true ||
      key->equals("external-parameter-entities") == true ||
      key->equals("validation") == true
     )
  {
    return _extendedFlags & XMLRF_PARSE_DTDVALID;
  }
  else if (key->equals("is-standalone") == true)
  {
    return (_extendedFlags & XMLRF_PARSE_DTDLOAD) == false;
  }
  else if (key->equals("namespaces") == true)
  {
    return (_extendedFlags & XMLRF_PARSE_USE_NS);
  }
  else if (key->equals("namespace-prefixes") == true)
  {
    return(_extendedFlags & XMLRF_PARSE_USE_NS_PREFIX);
  }
  else if (key->equals("resolve-dtd-uris") == true)
  {
    return (_extendedFlags & XMLRF_PARSE_DTDLOAD);
  }
  return false;
}

void 
XMLReader::setFeature(IN(RString) name, bool value) THROWS2(org::xml::sax::RSAXNotRecognizedException, org::xml::sax::RSAXNotSupportedException)
{
  _checkFeatureName(name);
  RString key = name->substring(strlen(FEATURES_PREFIX));
  if (key->equals("namespaces") == true)
  {
    if (value == true)
      _extendedFlags |= XMLRF_PARSE_USE_NS;
    else
      _extendedFlags &= ~XMLRF_PARSE_USE_NS;
  }
  else if (key->equals("namespace-prefixes") == true)
  {
    if (value == true)
      _extendedFlags |= XMLRF_PARSE_USE_NS_PREFIX;
    else
      _extendedFlags &= ~XMLRF_PARSE_USE_NS_PREFIX;
  }
  else if (key->equals("validation") == true)
  {
    if (value == true)
      _extendedFlags |= XMLRF_PARSE_DTDVALID;
    else
      _extendedFlags &= ~XMLRF_PARSE_DTDVALID;
  }
}

void 
XMLReader::_checkFeatureName(IN(RString) name)
{
  if (name->startsWith(FEATURES_PREFIX) == false || 
      containsInFeatureList(name->substr(strlen(FEATURES_PREFIX))) == false)
    THROW1_FQ(org::xml::sax::, SAXNotRecognizedException, "Feature is not recognized: " + name);
}

acdk::lang::Object 
XMLReader::getProperty(IN(RString) name) THROWS2(org::xml::sax::RSAXNotRecognizedException, org::xml::sax::RSAXNotSupportedException)
{
  // ### @todo implement me
  return Nil;
}

void 
XMLReader::setProperty(IN(RString) name, IN(acdk::lang::Object) value) THROWS2(org::xml::sax::RSAXNotRecognizedException, org::xml::sax::RSAXNotSupportedException)
{
  // ### @todo implement me
}


enum SaxWarnLevel
{
  SWL_Warn,
  SWL_Error,
  SWL_FatalError
};


void invokeError(xmlParserCtxtPtr ctx, IN(RString) msg, SaxWarnLevel level)
{
  SaxParseContext* sax = SaxParseContext::sctxFromCtx(ctx->userData);
  RXMLReader target = sax->_xmlReader;
  org::xml::sax::RErrorHandler errorHandler = target->getErrorHandler();
  if (errorHandler == Nil || target->hasAlreadyFatalError() == true)
    return;
  
  xmlSAXLocatorPtr loc = (xmlSAXLocatorPtr)sax->loc;
  int lineNumber = loc->getLineNumber(ctx);
  int columnNumber = loc->getColumnNumber(ctx);
  RString publicId = XML2STR(loc->getPublicId(ctx));
  RString systemId = XML2STR(loc->getSystemId(ctx));
  org::xml::sax::RLocator locator = new org::xml::sax::helpers::LocatorImpl(lineNumber, columnNumber, publicId, systemId);
  try {
    org::xml::sax::RSAXParseException ex = new org::xml::sax::SAXParseException(msg, locator);
    if (level == SWL_FatalError)
    {
      if (target->_seenStartDocument == false)
        target->_startDocument(false);
      target->setFatalError();
    }
    if (level == SWL_Warn)
      errorHandler->warning(ex);
    else if (level == SWL_Error)
      errorHandler->error(ex);
    else
      errorHandler->fatalError(ex);
  } catch (org::xml::sax::RSAXException ex) {
    THROW_INSTANCE(ex);
  } catch (RException ex) {
    THROW1_FQ(org::xml::sax::, SAXException, ex);
  }
}

void checkValid(xmlParserCtxtPtr ctx)
{
  if (ctx->wellFormed == 0)
    invokeError(ctx, "Document is not weel-formed", SWL_FatalError);
  if (ctx->validate != 0 && ctx->valid == 0)
    invokeError(ctx, "Document is not valid", SWL_FatalError);
}
void checkValid(SaxParseContext* sctx)
{
  checkValid(sctx->ctx);
}

int	xmlInputCloseCB(void * context)
{
  return 1;
}


void	internalSubsetCB(void* vctx, const xmlChar * name, const xmlChar* externalId, const xmlChar* systemId)
{
  SaxParseContext* sax = SaxParseContext::sctxFromCtx(vctx);
  xmlSAX2InternalSubset(sax->getXmlCtx(), name, externalId, systemId);

  RXMLReader target = sax->_xmlReader;
  target->_startDTD(XML2STR(name), XML2STR(externalId), XML2STR(systemId));
}


acdk::io::RReader getReader(IN(acdk::net::RURL) sourceUrl)
{
  return new NamedBufferReader(sourceUrl->toString(), sourceUrl->openStream());
}

void 
XMLReader::_startDTD(IN(RString) name, IN(RString) externalId, IN(RString) systemId)
{
  if (_lexicalHandler == Nil)
    return;
  _lexicalHandler->startDTD(name, externalId, org::xml::sax::helpers::NamespaceSupport::getAbsoluteURI(_baseURI, systemId));
}

void 
XMLReader::_startDocument(bool isStandalone)
{
  //_isStandalone = isStandalone;
  _seenStartDocument = true;
  if (_contentHandler == Nil)
    return;

  try {
    _contentHandler->startDocument();
  } catch (org::xml::sax::RSAXException ex) {
    THROW_INSTANCE(ex);
  } catch (RException ex) {
    THROW1_FQ(org::xml::sax::, SAXException, ex);
  }
}

xmlParserInputPtr
resolveEntityCB(void* vctx, const xmlChar* publicId, const xmlChar* systemId)
{
  SaxParseContext* sax = SaxParseContext::sctxFromCtx(vctx);
  RXMLReader target = sax->_xmlReader;
  
  acdk::io::RReader is = target->_resolveEntity(XML2STR(publicId), XML2STR(systemId));
  if (is == Nil)
    return 0;
  /** #### @todo implement me
  xmlParserCtxtPtr pctpr = xmlCreateIOParserCtxt(0, 0, xmlInputReadCB, xmlInputCloseCB, &is, XML_CHAR_ENCODING_NONE);
  xmlParserInputBufferPtr inputBuffer = xmlParserInputBufferCreateIO(xmlInputReadCB, xmlInputCloseCB, pctpr, XML_CHAR_ENCODING_NONE);
  

  xmlParserInputPtr inputPtr = xmlNewIOInputStream(sax->ctx, inputBuffer, XML_CHAR_ENCODING_NONE);
  return inputPtr;
  */
  return 0;
}

acdk::io::RReader 
XMLReader::_resolveEntity(IN(RString) publicId, IN(RString) systemId)
{
  if (_entityResolver == Nil)
    return Nil;

  try {
    org::xml::sax::RInputSource source = _entityResolver->resolveEntity(publicId, org::xml::sax::helpers::NamespaceSupport::getAbsoluteURI(_baseURI, systemId));
    if (source == Nil)
      return Nil;
    return source->getByteStream();
  } catch (RException ex) {
    if (instanceof(ex, org::xml::sax::SAXException) == true)
      THROW_INSTANCE(org::xml::sax::RSAXException(ex));
    THROW1_FQ(org::xml::sax::, SAXException, ex);
  }
  return Nil;
}

void 
XMLReader::_internalEntityDecl(IN(RString) name, IN(RString) value)
{
  if (hasAlreadyFatalError() == true || _declarationHandler == Nil)
    return;
  try {
    _declarationHandler->internalEntityDecl(name, value);
  } catch (org::xml::sax::RSAXException ex) {
    THROW_INSTANCE(ex);
  } catch (RException ex) {
    THROW1_FQ(org::xml::sax::, SAXException, ex);
  }
}

void 
XMLReader::_externalEntityDecl(IN(RString) name, IN(RString) publicId, IN(RString) systemId)
{
  if (hasAlreadyFatalError() == true || _declarationHandler == Nil)
    return;
  try {
    _declarationHandler->externalEntityDecl(name, publicId, org::xml::sax::helpers::NamespaceSupport::getAbsoluteURI(_baseURI, systemId));
  } catch (org::xml::sax::RSAXException ex) {
    THROW_INSTANCE(ex);
  } catch (RException ex) {
    THROW1_FQ(org::xml::sax::, SAXException, ex);
  }
}

void
entityDeclCB(void* vctx, const xmlChar* name_, int type, const xmlChar* publicId, const xmlChar* systemId, xmlChar* content)
{
  SaxParseContext* sax = SaxParseContext::sctxFromCtx(vctx);
  xmlSAX2EntityDecl(sax->getXmlCtx(), name_, type, publicId, systemId, content);
  
  RXMLReader target = sax->_xmlReader;

  checkValid(sax);
  RString name = XML2STR(name_);
  switch (type)
  {
  case XML_INTERNAL_GENERAL_ENTITY:
  case XML_INTERNAL_PARAMETER_ENTITY:
  case XML_INTERNAL_PREDEFINED_ENTITY:
    target->_internalEntityDecl(name, XML2STR(content));
    break;
  default:
    target->_externalEntityDecl(name, XML2STR(publicId), XML2STR(systemId));
    break;
  }
}

void 
XMLReader::_notationDecl(IN(RString) name, IN(RString) publicId, IN(RString) systemId)
{
  if (hasAlreadyFatalError() == true || _dtdHandler == Nil)
    return;
  try {
    _dtdHandler->notationDecl(name, publicId, org::xml::sax::helpers::NamespaceSupport::getAbsoluteURI(_baseURI, systemId));
  } catch (org::xml::sax::RSAXException ex) {
    THROW_INSTANCE(ex);
  } catch (RException ex) {
    THROW1_FQ(org::xml::sax::, SAXException, ex);
  }
}

void
notationDeclCB(void* vctx, const xmlChar* name, const xmlChar* publicId, const xmlChar* systemId)
{
  SaxParseContext* sax = SaxParseContext::sctxFromCtx(vctx);
  xmlSAX2NotationDecl(sax->getXmlCtx(), name, publicId, systemId);
  
  RXMLReader target = sax->_xmlReader;
  checkValid(sax);
  target->_notationDecl(XML2STR(name), XML2STR(publicId), XML2STR(systemId));
}


void 
XMLReader::_attributeDecl(IN(RString) elementName, IN(RString) attributeName, IN(RString) type, IN(RString) mode, IN(RString) value)
{
  if (hasAlreadyFatalError() == true || _declarationHandler == Nil)
    return;
  try {
    _declarationHandler->attributeDecl(elementName, attributeName, type, mode, value);
  } catch (org::xml::sax::RSAXException ex) {
    THROW_INSTANCE(ex);
  } catch (RException ex) {
    THROW1_FQ(org::xml::sax::, SAXException, ex);
  }
}

RString
attributeTypeToName(int type)
{
  switch (type)
  {
  case XML_ATTRIBUTE_CDATA:
    return "CDATA";
  case XML_ATTRIBUTE_ID:
      return "ID";
  case XML_ATTRIBUTE_IDREF:
    return "IDREF";
  case XML_ATTRIBUTE_IDREFS:
    return "IDREFS";
  case XML_ATTRIBUTE_NMTOKEN:
    return "NMTOKEN";
  case XML_ATTRIBUTE_NMTOKENS:
    return "NMTOKENS";
  case XML_ATTRIBUTE_ENTITY:
   return "ID";
  case XML_ATTRIBUTE_ENTITIES:
    return "ID";
  default:
    return Nil;
  }
}

RString
attributeDefToName(int type)
{
  switch (type)
  {
  case XML_ATTRIBUTE_IMPLIED:
    return "#IMPLIED";
  case XML_ATTRIBUTE_REQUIRED:
    return "#REQUIRED";
  case XML_ATTRIBUTE_FIXED:
   return "#FIXED";
  default:
    return Nil;
  }
}


void
attributeDeclCB(void* vctx, const xmlChar* elem, const xmlChar* fullName, int type, int def, const xmlChar* defaultValue, xmlEnumerationPtr tree)
{
  SaxParseContext* sax = SaxParseContext::sctxFromCtx(vctx);
  xmlSAX2AttributeDecl(sax->getXmlCtx(), elem, fullName, type, def, defaultValue, tree);
    
  RXMLReader target = sax->_xmlReader;
  checkValid(sax);

  target->_attributeDecl(XML2STR(elem), XML2STR(fullName), attributeTypeToName(type), attributeDefToName(def), XML2STR(defaultValue));
}


void 
XMLReader::_elementDecl(IN(RString) name, IN(RString) model)
{
  if (hasAlreadyFatalError() == true || _declarationHandler == Nil)
    return;
  try {
    _declarationHandler->elementDecl(name, model);
  } catch (org::xml::sax::RSAXException ex) {
    THROW_INSTANCE(ex);
  } catch (RException ex) {
    THROW1_FQ(org::xml::sax::, SAXException, ex);
  }
}

void
elementDeclCB(void* vctx, const xmlChar* name, int type, xmlElementContentPtr content)
{
  DOUT("elementDeclCB: " << name);

  SaxParseContext* sax = SaxParseContext::sctxFromCtx(vctx);
  xmlSAX2ElementDecl(sax->getXmlCtx(), name, type, content);
  
  RXMLReader target = sax->_xmlReader;
  checkValid(sax);
  target->_elementDecl(XML2STR(name), Nil); // ### @todo transform type to model name;
}

void 
XMLReader::_unparsedEntityDecl(IN(RString) name, IN(RString) publicId, IN(RString) systemId, IN(RString) notationName)
{
  if (hasAlreadyFatalError() == true || _dtdHandler == Nil)
    return;
  try {
    _dtdHandler->unparsedEntityDecl(name, publicId, org::xml::sax::helpers::NamespaceSupport::getAbsoluteURI(_baseURI, systemId), notationName);
  } catch (org::xml::sax::RSAXException ex) {
    THROW_INSTANCE(ex);
  } catch (RException ex) {
    THROW1_FQ(org::xml::sax::, SAXException, ex);
  }
}

void
unparsedEntityDeclCB(void* vctx, const xmlChar* name, const xmlChar* publicId, const xmlChar* systemId, const xmlChar* notationName)
{
  DOUT("unparsedEntityDeclCB: " << name);
  SaxParseContext* sax = SaxParseContext::sctxFromCtx(vctx);
  xmlSAX2UnparsedEntityDecl(sax->getXmlCtx(), name, publicId, systemId, notationName);
    
  RXMLReader target = sax->_xmlReader;
  checkValid(sax);

  target->_unparsedEntityDecl(XML2STR(name), XML2STR(publicId), XML2STR(systemId), XML2STR(notationName));
}

void
startDocumentCB(void* vctx)
{
  DOUT("startDocumentCB");
  SaxParseContext* sax = SaxParseContext::sctxFromCtx(vctx);
  xmlSAX2StartDocument(sax->getXmlCtx());
  RXMLReader target = sax->_xmlReader;
  checkValid(sax);
  target->_startDocument(sax->ctx->standalone);
}

void 
XMLReader::_endDocument()
{
  if (_contentHandler == Nil)
    return;
  try {
    _contentHandler->endDocument();
  } catch (org::xml::sax::RSAXException ex) {
    THROW_INSTANCE(ex);
  } catch (RException ex) {
    THROW1_FQ(org::xml::sax::, SAXException, ex);
  }
}

void
endDocumentCB(void* vctx)
{
  DOUT("endDocumentCB");
  SaxParseContext* sax = SaxParseContext::sctxFromCtx(vctx);
  xmlSAX2EndDocument(sax->getXmlCtx());
  
  RXMLReader target = sax->_xmlReader;
  checkValid(sax);
  target->_endDocument();
}

void 
XMLReader::_splitName(IN(RString) fqName, OUT(RString) localName, OUT(RString) prefix, OUT(RString) uri)
{
  int ci = fqName->lastIndexOf (':');
  if (ci < 1)
  {
    localName = fqName;
    prefix = Nil;
    uri = "";
    return;
  }
  localName = fqName->substr(ci + 1);
  prefix = fqName->substr(0, ci);
  if (prefix->equals("xml") == true)
  {
    if (localName->equals("lang") == true || 
        localName->equals("space") == true
       )
      uri = "http://www.w3.org/XML/1998/namespace";
    else
      uri = getURI(prefix);
  }
  else
  {
    uri = getURI(prefix);
  }
}

void 
XMLReader::_startPrefixMapping(IN(RString) prefix, IN(RString) uri)
{
  if (hasAlreadyFatalError() == true || _contentHandler == Nil)
    return;
  _ns->declarePrefix(prefix, uri);
  _contentHandler->startPrefixMapping(prefix, uri);
}

void 
XMLReader::_startElement(IN(RString) name, IN(RStringArray) attrs)
{
  if (hasAlreadyFatalError() == true || _contentHandler == Nil)
    return;
  try {
    RString localName;
    RString prefix;
    RString uri;
    _splitName(name, localName, prefix, uri);
    RStringArray newAttrs = attrs;
    if (useNamespace() == true)
    {
      _ns->pushContext();
      
      int len = (attrs == Nil) ? 0 : attrs->length();
      if (len > 0)
      {
        
        RStringArray filtered = new StringArray(0);

        for (int i = 0; i < len; i += 2)
        {
          RString attName = attrs[i];
          RString attValue = attrs[i + 1];
          if (attName->equals("xmlns") == true)
          {
            _startPrefixMapping("", attValue);
          }
          else if (attName->startsWith("xmlns:") == true)
          {
            _startPrefixMapping(attName->substr(6), attValue);
          }
          else
          {
            filtered->append(attName);
            filtered->append(attValue);
          }
        }
        newAttrs = filtered;
      }
    }
    org::xml::sax::helpers::RAttributesImpl atts  = new org::xml::sax::helpers::AttributesImpl();
    for (int i = 0; i < newAttrs->length(); i += 2)
    {
      RString attName = newAttrs[i];
      RString attValue = newAttrs[i + 1];
      RString localName;
      RString prefix;
      RString uri;
      _splitName(attName, localName, prefix, uri);
      atts->addAttribute(uri, localName, attName, "", attValue);
          
    }
    _contentHandler->startElement (uri, localName, name, &atts);
  } catch (org::xml::sax::RSAXException ex) {
    THROW_INSTANCE(ex);
  } catch (RException ex) {
    THROW1_FQ(org::xml::sax::, SAXException, ex);
  }
}

void 
XMLReader::_startElement(IN(RString) name, IN(org::xml::sax::RAttributes) attrs)
{
  if (hasAlreadyFatalError() == true || _contentHandler == Nil)
    return;
  try {
    RString localName;
    RString prefix;
    RString uri;
    _splitName(name, localName, prefix, uri);
    _contentHandler->startElement(uri, localName, name, attrs);
  } catch (org::xml::sax::RSAXException ex) {
    THROW_INSTANCE(ex);
  } catch (RException ex) {
    THROW1_FQ(org::xml::sax::, SAXException, ex);
  }
   
}

void startElementCB(void* vctx, const xmlChar* name, const xmlChar** attrs)
{
  DOUT("startElementCB");
  SaxParseContext* sax = SaxParseContext::sctxFromCtx(vctx);
  xmlSAX2StartElement(sax->getXmlCtx(), name, attrs);

  RXMLReader target = sax->_xmlReader;
  checkValid(sax);

  //RAttributesImpl ali = new AttributesImpl();
  RStringArray sa = new StringArray(0);
  if (attrs && *attrs != 0) 
  {
    while (*attrs != 0) 
    {
      RString name = XML2STR(*attrs++);
      RString value = XML2STR(*attrs++);
      sa->append(name);
      sa->append(value);
      /*
      RString localName;
      RString prefix;
      RString uri;
      target->_splitName(name, localName, prefix, uri);
      ali->addAttribute(uri, localName, name, "", value);
      //ali->addAttribute(name, "", value);
      */
    } 
  }
  target->_startElement(XML2STR(name), sa);
}

void 
XMLReader::_endElement(IN(RString) name)
{
}

void
endElementCB (void* vctx, const xmlChar* name)
{
  DOUT("endElementCB");
  SaxParseContext* sax = SaxParseContext::sctxFromCtx(vctx);
  xmlSAX2EndElement(sax->getXmlCtx(), name);
  
  RXMLReader target = sax->_xmlReader;
  checkValid(sax);
  target->_endElement(XML2STR(name));
}



void 
XMLReader::_setDocumentLocator(IN(org::xml::sax::RLocator) loc)
{
  if (hasAlreadyFatalError() == true || _contentHandler == Nil)
    return;
  try {
    _locator = loc;
    _contentHandler->setDocumentLocator(_locator);
  } catch (org::xml::sax::RSAXException ex) {
    THROW_INSTANCE(ex);
  } catch (RException ex) {
    THROW1_FQ(org::xml::sax::, SAXException, ex);
  }
  
}

void
setDocumentLocatorCB(void* vctx, xmlSAXLocatorPtr loc)
{
  DOUT("setDocumentLocatorCB");
  SaxParseContext* sax = SaxParseContext::sctxFromCtx(vctx);
  xmlSAX2SetDocumentLocator(sax->getXmlCtx(), loc);
  
  RXMLReader target = sax->_xmlReader;
  sax->loc = loc;
  target->_setDocumentLocator(new XmlLibLocator(sax->ctx, loc));
}

void 
XMLReader::_characters(IN(RString) text)
{
  if (hasAlreadyFatalError() == true || _contentHandler == Nil || text == Nil)
    return;
  try {
    _contentHandler->characters(text);
  } catch (org::xml::sax::RSAXException ex) {
    THROW_INSTANCE(ex);
  } catch (RException ex) {
    THROW1_FQ(org::xml::sax::, SAXException, ex);
  }
}

void
charactersCB(void* vctx, const xmlChar* ch, int len)
{
  DOUT("charactersCB: '" << XML2STR_LEN(ch, len) << "'");
  SaxParseContext* sax = SaxParseContext::sctxFromCtx(vctx);
  
  xmlSAX2Characters(sax->getXmlCtx(), ch, len);
  RXMLReader target = sax->_xmlReader;
  
  checkValid(sax);

  xmlChar* dup = xmlStrndup(ch, len);
  StackSavedStr _stsafe(dup);

  target->_characters(XML2STR(dup));
}


void 
XMLReader::_ignorableWhitespace(IN(RString) text)
{
  if (hasAlreadyFatalError() == true || _contentHandler == Nil || text == Nil)
    return;
  try {
    _contentHandler->ignorableWhitespace(text);
  } catch (org::xml::sax::RSAXException ex) {
    THROW_INSTANCE(ex);
  } catch (RException ex) {
    THROW1_FQ(org::xml::sax::, SAXException, ex);
  }
}

void ignorableWhitespaceCB(void* vctx, const xmlChar* ch, int len)
{
  DOUT("ignorableWhitespaceCB: \'" << XML2STR_LEN(ch, len) << "\'");
  SaxParseContext* sax = SaxParseContext::sctxFromCtx(vctx);
  xmlSAX2IgnorableWhitespace(sax->getXmlCtx(), ch, len);

  RXMLReader target = sax->_xmlReader;
  checkValid(sax);

  xmlChar* dup = xmlStrndup(ch, len);
  StackSavedStr _stsafe(dup);
  target->_ignorableWhitespace(XML2STR(dup));
}

void 
XMLReader::_processingInstruction(IN(RString) target, IN(RString) data)
{
  if (hasAlreadyFatalError() == true || _contentHandler == Nil)
    return;
  try {
    RString tdata = data;
    if (tdata == Nil)
      tdata = "";
    _contentHandler->processingInstruction(target, tdata);
  } catch (org::xml::sax::RSAXException ex) {
    THROW_INSTANCE(ex);
  } catch (RException ex) {
    THROW1_FQ(org::xml::sax::, SAXException, ex);
  }
}


void
processingInstructionCB(void* vctx, const xmlChar* targ, const xmlChar* data)
{
  DOUT("processingInstructionCB: \'" << (const char*)targ << "\', '" << (const char*)data << "\'");
  SaxParseContext* sax = SaxParseContext::sctxFromCtx(vctx);
  xmlSAX2ProcessingInstruction(sax->getXmlCtx(), targ, data);
  
  RXMLReader target = sax->_xmlReader;
  
  checkValid(sax);
  target->_processingInstruction(XML2STR(targ), XML2STR(data));
}

xmlEntityPtr
getEntityCB(void* vctx, const xmlChar* name)
{
  DOUT("getEntityCB: \'" << (const char*)name << "\'");
  SaxParseContext* sax = SaxParseContext::sctxFromCtx(vctx);
  // ### @todo implement me
  return 0;
}
void referenceCB(void* vctx, const xmlChar* name)
{
  DOUT("referenceCB: \'" << (const char*)name << "\'");
  SaxParseContext* sax = SaxParseContext::sctxFromCtx(vctx);
  xmlSAX2Reference(sax->getXmlCtx(), name);
  // ### @todo implement me
}

void 
XMLReader::_comment(IN(RString) text)
{
   if (hasAlreadyFatalError() == true || _lexicalHandler == Nil || text == Nil)
    return;
  try {
    _lexicalHandler->comment(text);
  } catch (org::xml::sax::RSAXException ex) {
    THROW_INSTANCE(ex);
  } catch (RException ex) {
    THROW1_FQ(org::xml::sax::, SAXException, ex);
  }
}

void
commentCB(void* vctx, const xmlChar* value)
{
  DOUT("commentCB: \'" << (const char*)value << "\'");
  SaxParseContext* sax = SaxParseContext::sctxFromCtx(vctx);

  xmlSAX2Comment(sax->getXmlCtx(), value);
  
  RXMLReader target = sax->_xmlReader;
  
  checkValid(sax);
  target->_comment(XML2STR(value));
}

void 
XMLReader::_cdataBlock(IN(RString) text)
{
  if (hasAlreadyFatalError() == true || text == Nil)
    return;
  try {
    if (_lexicalHandler == Nil)
      _characters(text);
    else
    {
      _lexicalHandler->startCDATA();
      _characters(text);
      _lexicalHandler->endCDATA();
    }
  } catch (org::xml::sax::RSAXException ex) {
    THROW_INSTANCE(ex);
  } catch (RException ex) {
    THROW1_FQ(org::xml::sax::, SAXException, ex);
  }
}

void
cdataBlockCB(void* vctx, const xmlChar* ch, int len)
{
  DOUT("cdataBlockCB: \'" << XML2STR_LEN(ch, len)  << "\'");
  SaxParseContext* sax = SaxParseContext::sctxFromCtx(vctx);
  xmlSAX2CDataBlock(sax->getXmlCtx(), ch, len);
  
  RXMLReader target = sax->_xmlReader;
  checkValid(sax);

  xmlChar* dup = xmlStrndup(ch, len);
  StackSavedStr _stsafe(dup);
  target->_cdataBlock(XML2STR(dup));
}

void 
XMLReader::_warning(IN(RString) message, int lineNumber, int columnNumber, IN(RString) publicId, IN(RString) systemId)
{
  if (hasAlreadyFatalError() == true || _errorHandler == Nil)
    return;
   try {
     org::xml::sax::RLocator l = new org::xml::sax::helpers::LocatorImpl(lineNumber, columnNumber, publicId, systemId);
     _errorHandler->warning (new org::xml::sax::SAXParseException(message, l));
  } catch (org::xml::sax::RSAXException ex) {
     ex->throwException();
    THROW_INSTANCE(ex);
  } catch (RException ex) {
    THROW1_FQ(org::xml::sax::, SAXException, ex);
  }
}

void 
XMLReader::_error(IN(RString) message, int lineNumber, int columnNumber, IN(RString) publicId, IN(RString) systemId)
{
  if (hasAlreadyFatalError() == true || _errorHandler == Nil)
    return;
   try {
     org::xml::sax::RLocator l = new org::xml::sax::helpers::LocatorImpl(lineNumber, columnNumber, publicId, systemId);
     _errorHandler->error(new org::xml::sax::SAXParseException(message, l));
  } catch (org::xml::sax::RSAXException ex) {
    THROW_INSTANCE(ex);
  } catch (RException ex) {
    THROW1_FQ(org::xml::sax::, SAXException, ex);
  }
}

void 
XMLReader::_fatalError(IN(RString) message, int lineNumber, int columnNumber, IN(RString) publicId, IN(RString) systemId)
{
  if (hasAlreadyFatalError() == true)
    return;
  _hasAlreadyFatal = true;
  if (_errorHandler == Nil)
    return;
   try {
     if (_seenStartDocument == false)
       _startDocument(false);
     org::xml::sax::RLocator l = new org::xml::sax::helpers::LocatorImpl(lineNumber, columnNumber, publicId, systemId);
     _errorHandler->fatalError(new org::xml::sax::SAXParseException(message, l));
  } catch (org::xml::sax::RSAXException ex) {
    THROW_INSTANCE(ex);
  } catch (RException ex) {
    THROW1_FQ(org::xml::sax::, SAXException, ex);
  }
}



#if defined(_MSC_VER)
#define vsnprintf _vsnprintf
#endif
/FONT>


void
warningCB(void* vctx, const char *msg, ...)
{
  
  va_list args;

  SaxParseContext* sax = SaxParseContext::sctxFromCtx(vctx);
  xmlSAXLocatorPtr loc = (xmlSAXLocatorPtr)sax->loc;
  RXMLReader target = sax->_xmlReader;
  
  // xmlParserWarning (vctx, msg, args); 
  int lineNumber = loc->getLineNumber(sax->ctx);
  int columnNumber = loc->getColumnNumber(sax->ctx);
  
  va_start(args, msg);
  
  char buffer[2048] = "";
  if (msg != 0)
  {
    vsnprintf(buffer, sizeof(buffer), msg, args);
  }
  DOUT("warningCB: \'" << (const char*)buffer << "\'");
  if (target != Nil)
    target->_warning(XML2STR(buffer), lineNumber, columnNumber, XML2STR(loc->getPublicId(sax->ctx)), XML2STR(loc->getSystemId(sax->ctx)));
  va_end(args);
}

void
errorCB(void* vctx, const char *msg, ...)
{
  va_list args;

  SaxParseContext* sax = SaxParseContext::sctxFromCtx(vctx);
  xmlSAXLocatorPtr loc = (xmlSAXLocatorPtr)sax->loc;
  RXMLReader target = sax->_xmlReader;
  
  // xmlParserWarning (vctx, msg, args); 
  int lineNumber = loc->getLineNumber(sax->ctx);
  int columnNumber = loc->getColumnNumber(sax->ctx);
  
  va_start(args, msg);
  
  char buffer[2048] = "";
  if (msg != 0)
  {
    vsnprintf(buffer, sizeof(buffer), msg, args);
  }
  DOUT("errorCB: \'" << (const char*)buffer << "\'");
  if (target != Nil)
    target->_error(XML2STR(buffer), lineNumber, columnNumber, XML2STR(loc->getPublicId(sax->ctx)), XML2STR(loc->getSystemId(sax->ctx)));
  va_end(args);
}


void
fatalErrorCB(void* vctx, const char *msg, ...)
{
  va_list args;

  SaxParseContext* sax = SaxParseContext::sctxFromCtx(vctx);
  xmlSAXLocatorPtr loc = (xmlSAXLocatorPtr)sax->loc;
  RXMLReader target = sax->_xmlReader;
  
  // xmlParserWarning (vctx, msg, args); 
  int lineNumber = loc->getLineNumber(sax->ctx);
  int columnNumber = loc->getColumnNumber(sax->ctx);
  
  va_start(args, msg);
  
  char buffer[2048] = "";
  if (msg != 0)
  {
    vsnprintf(buffer, sizeof(buffer), msg, args);
  }
  DOUT("fatalErrorCB: \'" << (const char*)buffer << "\'");
  if (target != Nil)
    target->_fatalError(XML2STR(buffer), lineNumber, columnNumber, XML2STR(loc->getPublicId(sax->ctx)), XML2STR(loc->getSystemId(sax->ctx)));
  va_end(args);
}

void structuredErrorCB(void *userData, xmlErrorPtr error)
{

}

void endElementNsSAX2CB(void *vctx, const xmlChar *localname, const xmlChar *prefix, const xmlChar* uri)
{
  SaxParseContext* sax = SaxParseContext::sctxFromCtx(vctx);
  //xmlSAX2CDataBlock(sax->getXmlCtx(), ch, len);
  xmlSAX2EndElementNs(sax->getXmlCtx(), localname, prefix, uri);
}

void startElementNsSAX2CB(void *vctx, const xmlChar *localname, const xmlChar *prefix, const xmlChar *uri, int nb_namespaces, const xmlChar **namespaces,
					                 int nb_attributes, int nb_defaulted, const xmlChar **attributes)
{
  SaxParseContext* sax = SaxParseContext::sctxFromCtx(vctx);
  xmlSAX2StartElementNs(sax->getXmlCtx(), localname, prefix, uri, nb_namespaces, namespaces, nb_attributes, nb_defaulted, attributes);
}

void 
XMLReader::parse(IN(org::xml::sax::RInputSource) input) THROWS2(acdk::io::RIOException, org::xml::sax::RSAXException)
{
  parseInternal(input);
}

void 
XMLReader::_evaluteExtendedFlags()
{
  //_validate = _extendedFlags & XMLRF_PARSE_DTDVALID;
  //setFeature("http://xml.org/sax/features/validation", _validate);
  //_useNamespaces = _extendedFlags & XMLRF_PARSE_NONS;
  //setFeature("http://xml.org/sax/features/namespaces", _useNamespaces);
  //_isStandalone = _extendedFlags & XML_PARSE_NONET;
  //_useNamespacePrefixes = _useNamespaces;
}


org::w3c::dom::RDocument 
XMLReader::parseInternalHtml(IN(org::xml::sax::RInputSource) input) THROWS2(acdk::io::RIOException, org::xml::sax::RSAXException)
{
  RString publicId = input->getPublicId();
  RString systemId = input->getSystemId();
  _baseURI = org::xml::sax::helpers::NamespaceSupport::getBaseURI(systemId);
  _evaluteExtendedFlags();
  
  const char* fileName = 0;
  
  if (systemId != Nil && systemId->length() > 0)
    fileName = (const char*)STR2XMLDUP(systemId);

  htmlDocPtr htmldoc = 0;
  if (fileName != 0 && false)
  {
    htmldoc = htmlReadFile(fileName, 0, (short)_extendedFlags);
  }
  else
  {
    bool useInMemory = true;
    if (useInMemory == true)
    {
      acdk::io::MemWriter mout;
      input->getByteStream()->trans(&mout);
      //mout.write(0);
      // ctxt->sax->startElement is not set on htmlReadDoc and htmlReadIO!!!!!
      // is set in htmlReadMemory htmlReadFile
      // not working htmldoc = htmlReadDoc(mout.getBuffer()->data(), fileName, 0, (short)_extendedFlags);
      htmldoc = htmlReadMemory((const char*)mout.getBuffer()->data(), mout.getBuffer()->length(), 0, 0, (short)_extendedFlags);
    }
    else
    {
      // code is buggy in libxml2
      SaxParseContext parseContext(this, publicId, systemId, input->getByteStream());
    
      htmldoc = htmlReadIO(SaxParseContext::readCB, SaxParseContext::closeCB, &parseContext,  fileName, 0, (short)_extendedFlags);
    }
  }
  return new libxmldom::LibXMLDocument((xmlNodePtr)htmldoc);
}

org::w3c::dom::RDocument 
XMLReader::parseInternal(IN(org::xml::sax::RInputSource) input) THROWS2(acdk::io::RIOException, org::xml::sax::RSAXException)
{
  if (_extendedFlags & XMLRF_PARSE_HTML)
  {
    return parseInternalHtml(input);
  }
  RString publicId = input->getPublicId();
  RString systemId = input->getSystemId();
  _baseURI = org::xml::sax::helpers::NamespaceSupport::getBaseURI(systemId);
  _evaluteExtendedFlags();
  
  int xmlFlags = _extendedFlags;

  //xmlParserCtxtPtr	xmlCreateIOParserCtxt	(xmlSAXHandlerPtr sax, 						 void * user_data, 						 xmlInputReadCallback ioread, 						 xmlInputCloseCallback ioclose, 						 void * ioctx, 						 xmlCharEncoding enc)
  
  xmlSAXHandlerPtr sax =  (xmlSAXHandlerPtr)xmlMalloc(sizeof(xmlSAXHandler));
  memset (sax, 0, sizeof (xmlSAXHandler));
  SaxParseContext parseContext(this, publicId, systemId, input->getByteStream());
  xmlParserCtxtPtr ctx = xmlCreateIOParserCtxt(sax, &parseContext, SaxParseContext::readCB, SaxParseContext::closeCB, &parseContext, XML_CHAR_ENCODING_NONE);
  parseContext.setCtx(ctx);
  //ctx->parseMode = XML_PARSE_DOM;
  if (_extendedFlags & XMLRF_PARSE_SAX1)
    ctx->sax2 = 0;
  else
    ctx->sax2 = 1;
  
  

  ctx->sax = sax;
  ctx->_private = &parseContext;
  ctx->userData = ctx;

  //wrong ctx->replaceEntities = (_extendedFlags & XMLRF_PARSE_NOENT) == 0;
  ctx->validate = isValidating();
  ctx->keepBlanks = (_extendedFlags & XMLRF_PARSE_NOBLANKS) == 0;
  ctx->pedantic = _extendedFlags & XMLRF_PARSE_PEDANTIC;
  ctx->options = _extendedFlags;
  //loadsubset
  if (systemId != Nil && systemId->length() > 0)
    ctx->input->filename = (const char*)STR2XMLDUP(systemId);
  if (_baseURI != Nil && _baseURI->length() > 0)
    ctx->input->directory = (const char*)STR2XMLDUP(_baseURI);
  
  xmlSAXVersion (sax, 2);

  if (_dtdHandler != Nil)
  {
    sax->internalSubset = &internalSubsetCB;
  }
  /* ###
  if (defaultLoader == 0)
  
    defaultLoader = xmlGetExternalEntityLoader ();
    xmlSetExternalEntityLoader (xmljExternalEntityLoader);
  }
  */
  if (_entityResolver != Nil)
  {
    sax->resolveEntity = &resolveEntityCB;
  }

  if (_declarationHandler != Nil)
  {
    sax->entityDecl = &entityDeclCB;
    sax->notationDecl = &notationDeclCB;
    sax->attributeDecl = &attributeDeclCB;
    sax->elementDecl = &elementDeclCB;
    sax->unparsedEntityDecl = &unparsedEntityDeclCB;
  }

  sax->setDocumentLocator = &setDocumentLocatorCB;
  
  if (_contentHandler != Nil)
  {
    sax->startDocument = &startDocumentCB;
    sax->endDocument = &endDocumentCB;
    sax->startElement = &startElementCB;
    sax->endElement = &endElementCB;
    sax->characters = &charactersCB;
    sax->ignorableWhitespace = &ignorableWhitespaceCB;
    sax->processingInstruction = &processingInstructionCB;
    sax->startElementNs = startElementNsSAX2CB;
    sax->endElementNs = endElementNsSAX2CB;
  
  }

  /* We always intercept getEntity */
  sax->getEntity = &getEntityCB;
  if (_lexicalHandler != Nil)
  {
    sax->reference = &referenceCB;
    sax->comment = &commentCB;
    sax->cdataBlock = &cdataBlockCB;
  }
  else if (_contentHandler != Nil)
  {
    sax->cdataBlock = &charactersCB;
  }

  if (_errorHandler != Nil)
  {
    sax->warning = &warningCB;
    sax->error = &errorCB;
    sax->fatalError = &fatalErrorCB;
    if (isValidating() == true)
    {
      ctx->vctxt.error = &errorCB;
      ctx->vctxt.warning = &warningCB;
    }
  }
  int parseRet = 0;
  parseRet = xmlParseDocument(ctx);
  if ((xmlNodePtr)ctx->myDoc == 0)
    return Nil;
  return new libxmldom::LibXMLDocument((xmlNodePtr)ctx->myDoc);
}

void 
XMLReader::parse(IN(RString) systemId) THROWS2(acdk::io::RIOException, org::xml::sax::RSAXException)
{
  parseInternal(systemId);
}

org::w3c::dom::RDocument 
XMLReader::parseInternal(IN(RString) systemId) THROWS2(acdk::io::RIOException, org::xml::sax::RSAXException)
{
  return Nil;
}



} // namespace sax
} // namespace xml
} // namespace acdk