Logo Search packages:      
Sourcecode: poco version File versions  Download package

XMLWriter.cpp

//
// XMLWriter.cpp
//
// $Id: //poco/1.2/XML/src/XMLWriter.cpp#2 $
//
// Library: XML
// Package: XML
// Module:  XMLWriter
//
// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
// 
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//


#include "Poco/XML/XMLWriter.h"
#include "Poco/XML/XMLString.h"
#include "Poco/XML/XMLException.h"
#include "Poco/SAX/AttributesImpl.h"
#include "Poco/UTF8Encoding.h"
#include "Poco/UTF16Encoding.h"
#include <sstream>


namespace Poco {
namespace XML {


const std::string XMLWriter::NEWLINE_DEFAULT;
const std::string XMLWriter::NEWLINE_CR         = "\r";
const std::string XMLWriter::NEWLINE_CRLF       = "\r\n";
const std::string XMLWriter::NEWLINE_LF         = "\n";
const std::string XMLWriter::MARKUP_QUOTENC     = "&quot;";
const std::string XMLWriter::MARKUP_APOSENC     = "&apos;";
const std::string XMLWriter::MARKUP_AMPENC      = "&amp;";
const std::string XMLWriter::MARKUP_LTENC       = "&lt;";
const std::string XMLWriter::MARKUP_GTENC       = "&gt;";
const std::string XMLWriter::MARKUP_LT          = "<";
const std::string XMLWriter::MARKUP_GT          = ">";
const std::string XMLWriter::MARKUP_SLASHGT     = "/>";
const std::string XMLWriter::MARKUP_LTSLASH     = "</";
const std::string XMLWriter::MARKUP_COLON       = ":";
const std::string XMLWriter::MARKUP_EQQUOT      = "=\"";
const std::string XMLWriter::MARKUP_QUOT        = "\"";
const std::string XMLWriter::MARKUP_SPACE       = " ";
const std::string XMLWriter::MARKUP_TAB         = "\t";
const std::string XMLWriter::MARKUP_BEGIN_CDATA = "<![CDATA[";
const std::string XMLWriter::MARKUP_END_CDATA   = "]]>";


#if defined(XML_UNICODE_WCHAR_T)
      #define NATIVE_ENCODING Poco::UTF16Encoding
#else
      #define NATIVE_ENCODING Poco::UTF8Encoding
#endif


XMLWriter::XMLWriter(XMLByteOutputStream& str, int options):
      _pTextConverter(0),
      _pInEncoding(new NATIVE_ENCODING),
      _pOutEncoding(new Poco::UTF8Encoding),
      _options(options),
      _encoding("UTF-8"),
      _depth(-1),
      _elementCount(0),
      _inFragment(false),
      _inCDATA(false),
      _inDTD(false),
      _inInternalDTD(false),
      _contentWritten(false),
      _unclosedStartTag(false),
      _prefix(0)
{
      _pTextConverter = new Poco::OutputStreamConverter(str, *_pInEncoding, *_pOutEncoding);
      setNewLine(NEWLINE_DEFAULT);
}


00100 XMLWriter::XMLWriter(XMLByteOutputStream& str, int options, const std::string& encodingName, Poco::TextEncoding& textEncoding):
      _pTextConverter(0),
      _pInEncoding(new NATIVE_ENCODING),
      _pOutEncoding(0),
      _options(options),
      _encoding(encodingName),
      _depth(-1),
      _elementCount(0),
      _inFragment(false),
      _inCDATA(false),
      _inDTD(false),
      _inInternalDTD(false),
      _contentWritten(false),
      _unclosedStartTag(false),
      _prefix(0)
{
      _pTextConverter = new Poco::OutputStreamConverter(str, *_pInEncoding, textEncoding);
      setNewLine(NEWLINE_DEFAULT);
}


00121 XMLWriter::XMLWriter(XMLByteOutputStream& str, int options, const std::string& encodingName, Poco::TextEncoding* pTextEncoding):
      _pTextConverter(0),
      _pInEncoding(new NATIVE_ENCODING),
      _pOutEncoding(0),
      _options(options),
      _encoding(encodingName),
      _depth(-1),
      _elementCount(0),
      _inFragment(false),
      _inCDATA(false),
      _inDTD(false),
      _inInternalDTD(false),
      _contentWritten(false),
      _unclosedStartTag(false),
      _prefix(0)
{
      if (pTextEncoding)
      {
            _pTextConverter = new Poco::OutputStreamConverter(str, *_pInEncoding, *pTextEncoding);
      }
      else
      {
            _encoding = "UTF-8";
            _pOutEncoding = new Poco::UTF8Encoding;
            _pTextConverter = new Poco::OutputStreamConverter(str, *_pInEncoding, *_pOutEncoding);
      }
      setNewLine(NEWLINE_DEFAULT);
}


00151 XMLWriter::~XMLWriter()
{
      delete _pTextConverter;
      delete _pInEncoding;
      delete _pOutEncoding;
}


00159 void XMLWriter::setDocumentLocator(const Locator* loc)
{
}


00164 void XMLWriter::setNewLine(const std::string& newLineCharacters)
{
      if (newLineCharacters.empty())
      {
#if defined(_WIN32)
            _newLine = NEWLINE_CRLF;
#else
            _newLine = NEWLINE_LF;
#endif
      }
      else _newLine = newLineCharacters;
}


00178 const std::string& XMLWriter::getNewLine() const
{
      return _newLine;
}


00184 void XMLWriter::startDocument()
{
      if (_depth != -1)
            throw XMLException("Cannot start a document in another document");

      _inFragment    = false;
      _depth         = 0;
      _elementCount  = 0;
      _inDTD         = false;
      _inInternalDTD = false;
      _prefix        = 0;

      if (_options & WRITE_XML_DECLARATION)
            writeXMLDeclaration();
      
      _contentWritten = true;
      _namespaces.reset();
      _namespaces.pushContext();
}


00205 void XMLWriter::endDocument()
{
      if (_depth > 0)
            throw XMLException("Not well-formed (at least one tag has no matching end tag)");
      if (_elementCount == 0)
            throw XMLException("No document element");

      _elementCount = 0;
      _depth        = -1;
}


00217 void XMLWriter::startFragment()
{
      if (_depth != -1)
            throw XMLException("Cannot start a fragment in another fragment or document");

      _inFragment   = true;
      _depth        = 0;
      _elementCount = 0;
      _prefix       = 0;

      _contentWritten = true;
      _namespaces.reset();
      _namespaces.pushContext();
}


00233 void XMLWriter::endFragment()
{
      if (_depth > 1)
            throw XMLException("Not well-formed (at least one tag has no matching end tag)");
      
      _inFragment   = false;
      _elementCount = 0;
      _depth        = -1;
}


00244 void XMLWriter::startElement(const XMLString& namespaceURI, const XMLString& localName, const XMLString& qname)
{
      AttributesImpl attributes;
      startElement(namespaceURI, localName, qname, attributes);
}


00251 void XMLWriter::startElement(const XMLString& namespaceURI, const XMLString& localName, const XMLString& qname, const Attributes& attributes)
{
      if (_depth == 0 && !_inFragment && _elementCount > 1) 
            throw XMLException("Not well-formed. Second root element found", nameToString(localName, qname));
      
      if (_unclosedStartTag) closeStartTag();
      prettyPrint();
      writeStartElement(namespaceURI, localName, qname, attributes);
      _elementStack.push_back(Name(qname, namespaceURI, localName));
      _contentWritten = false;
      ++_depth;
}


00265 void XMLWriter::endElement(const XMLString& namespaceURI, const XMLString& localName, const XMLString& qname)
{
      if (_depth < 1)
            throw XMLException("No unclosed tag");

      if (!_elementStack.back().equalsWeakly(qname, namespaceURI, localName))
            throw XMLException("End tag does not match start tag", nameToString(localName, qname));

      _elementStack.pop_back();
      --_depth;
      if (!_unclosedStartTag) prettyPrint();
      writeEndElement(namespaceURI, localName, qname);
      _contentWritten = false;
      if (_depth == 0)
            writeNewLine();
}


00283 void XMLWriter::emptyElement(const XMLString& namespaceURI, const XMLString& localName, const XMLString& qname)
{
      AttributesImpl attributes;
      emptyElement(namespaceURI, localName, qname, attributes);
}


00290 void XMLWriter::emptyElement(const XMLString& namespaceURI, const XMLString& localName, const XMLString& qname, const Attributes& attributes)
{
      if (_depth == 0 && _elementCount > 1)
            throw XMLException("Not well-formed. Second root element found.");

      if (_unclosedStartTag) closeStartTag();
      prettyPrint();
      writeStartElement(namespaceURI, localName, qname, attributes);
      _contentWritten = false;
}


00302 void XMLWriter::characters(const XMLChar ch[], int start, int length)
{
      if (_unclosedStartTag) closeStartTag();
      _contentWritten = _contentWritten || length > 0;
      if (_inCDATA)
      {
            while (length-- > 0) writeXML(ch[start++]);
      }
      else
      {
            while (length-- > 0)
            {
                  XMLChar c = ch[start++];
                  switch (c)
                  {
                  case '"':  writeMarkup(MARKUP_QUOTENC); break;
                  case '\'': writeMarkup(MARKUP_APOSENC); break;
                  case '&':  writeMarkup(MARKUP_AMPENC); break;
                  case '<':  writeMarkup(MARKUP_LTENC); break;
                  case '>':  writeMarkup(MARKUP_GTENC); break;
                  default:
                        if (c >= 0 && c < 32)
                        {
                              if (c == '\t' || c == '\r' || c == '\n')
                                    writeXML(c);
                              else
                                    throw XMLException("Invalid character token.");
                        }
                        else writeXML(c);
                  }
            }
      }
}


00337 void XMLWriter::characters(const XMLString& str)
{
      characters(str.data(), 0, (int) str.length());
}


00343 void XMLWriter::rawCharacters(const XMLString& str)
{
      if (_unclosedStartTag) closeStartTag();
      _contentWritten = _contentWritten || !str.empty();
      writeXML(str);
}


00351 void XMLWriter::ignorableWhitespace(const XMLChar ch[], int start, int length)
{
      characters(ch, start, length);
}


00357 void XMLWriter::processingInstruction(const XMLString& target, const XMLString& data)
{
      if (_unclosedStartTag) closeStartTag();
      prettyPrint();
      writeMarkup("<?");
      writeXML(target);
      if (!data.empty())
      {
            writeMarkup(MARKUP_SPACE);
            writeXML(data);
      }
      writeMarkup("?>");
      if (_depth == 0)
            writeNewLine();
}


00374 void XMLWriter::dataElement(const XMLString& namespaceURI, const XMLString& localName, const XMLString& qname,
                             const XMLString& data,
                               const XMLString& attr1, const XMLString& value1,
                                           const XMLString& attr2, const XMLString& value2,
                                           const XMLString& attr3, const XMLString& value3)
{
      static const XMLString CDATA = toXMLString("CDATA");

      AttributesImpl attributes;
      if (!attr1.empty()) attributes.addAttribute(XMLString(), XMLString(), attr1, CDATA, value1);
      if (!attr2.empty()) attributes.addAttribute(XMLString(), XMLString(), attr2, CDATA, value2);
      if (!attr3.empty()) attributes.addAttribute(XMLString(), XMLString(), attr3, CDATA, value3);
      if (data.empty())
      {
            emptyElement(namespaceURI, localName, qname, attributes);
      }
      else
      {
            startElement(namespaceURI, localName, qname, attributes);
            characters(data);
            endElement(namespaceURI, localName, qname);
      }
}


00399 void XMLWriter::startPrefixMapping(const XMLString& prefix, const XMLString& namespaceURI)
{
      if (prefix != NamespaceSupport::XML_NAMESPACE_PREFIX)
            _namespaces.declarePrefix(prefix, namespaceURI);
}


00406 void XMLWriter::endPrefixMapping(const XMLString& prefix)
{
      if (prefix != NamespaceSupport::XML_NAMESPACE_PREFIX)
            _namespaces.undeclarePrefix(prefix);
}


00413 void XMLWriter::skippedEntity(const XMLString& name)
{
}


00418 void XMLWriter::startCDATA()
{
      if (_inCDATA) throw XMLException("Cannot nest CDATA sections");
      if (_unclosedStartTag) closeStartTag();
      _inCDATA = true;
      writeMarkup(MARKUP_BEGIN_CDATA);
}


00427 void XMLWriter::endCDATA()
{
      poco_assert (_inCDATA);
      _inCDATA = false;
      writeMarkup(MARKUP_END_CDATA);
}


00435 void XMLWriter::comment(const XMLChar ch[], int start, int length)
{
      if (_unclosedStartTag) closeStartTag();
      prettyPrint();
      writeMarkup("<!--");
      while (length-- > 0) writeXML(ch[start++]);
      writeMarkup("-->");
      _contentWritten = false;
}


00446 void XMLWriter::startDTD(const XMLString& name, const XMLString& publicId, const XMLString& systemId)
{
      writeMarkup("<!DOCTYPE ");
      writeXML(name);
      if (!publicId.empty())
      {
            writeMarkup(" PUBLIC \"");
            writeXML(publicId);
            writeMarkup("\"");
      }
      if (!systemId.empty())
      {
            writeMarkup(" SYSTEM \"");
            writeXML(systemId);
            writeMarkup("\"");
      }
      _inDTD = true;
}


00466 void XMLWriter::endDTD()
{
      poco_assert (_inDTD);
      if (_inInternalDTD)
      {
            writeNewLine();
            writeMarkup("]");
            _inInternalDTD = false;
      }
      writeMarkup(">");
      writeNewLine();
      _inDTD = false;
}


00481 void XMLWriter::startEntity(const XMLString& name)
{
}


00486 void XMLWriter::endEntity(const XMLString& name)
{
}


00491 void XMLWriter::notationDecl(const XMLString& name, const XMLString* publicId, const XMLString* systemId)
{
      if (!_inDTD) throw XMLException("Notation declaration not within DTD");
      if (!_inInternalDTD)
      {
            writeMarkup(" [");
            _inInternalDTD = true;
      }
      if (_options & PRETTY_PRINT)
      {
            writeNewLine();
            writeMarkup(MARKUP_TAB);
      }
      writeMarkup("<!NOTATION ");
      writeXML(name);
      if (systemId && !systemId->empty())
      {
            writeMarkup(" SYSTEM \"");
            writeXML(*systemId);
            writeMarkup("\"");
      }
      if (publicId && !publicId->empty())
      {
            writeMarkup(" PUBLIC \"");
            writeXML(*publicId);
            writeMarkup("\"");
      }
      writeMarkup(">");
}


00522 void XMLWriter::unparsedEntityDecl(const XMLString& name, const XMLString* publicId, const XMLString& systemId, const XMLString& notationName)
{
      if (!_inDTD) throw XMLException("Entity declaration not within DTD");
      if (!_inInternalDTD)
      {
            writeMarkup(" [");
            _inInternalDTD = true;
      }
      if (_options & PRETTY_PRINT)
      {
            writeNewLine();
            writeMarkup(MARKUP_TAB);
      }
      writeMarkup("<!ENTITY ");
      writeXML(name);
      if (!systemId.empty())
      {
            writeMarkup(" SYSTEM \"");
            writeXML(systemId);
            writeMarkup("\"");
      }
      if (publicId && !publicId->empty())
      {
            writeMarkup(" PUBLIC \"");
            writeXML(*publicId);
            writeMarkup("\"");
      }
      if (!notationName.empty())
      {
            writeMarkup(" NDATA ");
            writeXML(notationName);
      }
      writeMarkup(">");
}


void XMLWriter::prettyPrint() const
{
      if ((_options & PRETTY_PRINT) && !_contentWritten)
      {
            writeNewLine();
            writeIndent();
      }
}


void XMLWriter::writeStartElement(const XMLString& namespaceURI, const XMLString& localName, const XMLString& qname, const Attributes& attributes)
{
      ++_elementCount;
      writeMarkup(MARKUP_LT);
      if (!localName.empty() && (qname.empty() || localName == qname))
      {
            XMLString prefix;
            if (!namespaceURI.empty() && !_namespaces.isMapped(namespaceURI))
            {
                  prefix = newPrefix();
                  _namespaces.declarePrefix(prefix, namespaceURI);
            }
            else prefix = _namespaces.getPrefix(namespaceURI);
            writeName(prefix, localName);
      }
      else if (namespaceURI.empty() && localName.empty() && !qname.empty())
      {
            writeXML(qname);
      }
      else if (!localName.empty() && !qname.empty())
      {
            XMLString local;
            XMLString prefix;
            Name::split(qname, prefix, local);
            if (prefix.empty()) prefix = _namespaces.getPrefix(namespaceURI);
            const XMLString& uri = _namespaces.getURI(prefix);
            if ((uri.empty() || uri != namespaceURI) && !namespaceURI.empty())
            {
                  _namespaces.declarePrefix(prefix, namespaceURI);
            }
            writeName(prefix, localName);
      }
      else throw XMLException("Tag mismatch", nameToString(localName, qname));

      declareAttributeNamespaces(attributes);
      AttributeMap attributeMap;
      addNamespaceAttributes(attributeMap);
      addAttributes(attributeMap, attributes, namespaceURI);
      writeAttributes(attributeMap);
      _unclosedStartTag = true;
      _namespaces.pushContext();
}


void XMLWriter::writeEndElement(const XMLString& namespaceURI, const XMLString& localName, const XMLString& qname)
{
      if (_unclosedStartTag)
      {
            writeMarkup(MARKUP_SLASHGT);
            _unclosedStartTag = false;
      }
      else
      {
            writeMarkup(MARKUP_LTSLASH);
            if (!localName.empty())
            {
                  XMLString prefix = _namespaces.getPrefix(namespaceURI);
                  writeName(prefix, localName);
            }
            else
            {
                  writeXML(qname);
            }
            writeMarkup(MARKUP_GT);
      }
      _namespaces.popContext();
}


void XMLWriter::closeStartTag()
{
      _unclosedStartTag = false;
      writeMarkup(MARKUP_GT);
}


void XMLWriter::declareAttributeNamespaces(const Attributes& attributes)
{
      for (int i = 0; i < attributes.getLength(); i++)
      {
            XMLString namespaceURI = attributes.getURI(i);
            XMLString localName    = attributes.getLocalName(i);
            XMLString qname        = attributes.getQName(i);
            if (!localName.empty())
            {
                  XMLString prefix;
                  XMLString splitLocalName;
                  Name::split(qname, prefix, splitLocalName);
                  if (prefix.empty()) prefix = _namespaces.getPrefix(namespaceURI);
                  if (prefix.empty() && !namespaceURI.empty() && !_namespaces.isMapped(namespaceURI))
                  {
                        prefix = newPrefix();
                        _namespaces.declarePrefix(prefix, namespaceURI);
                  }


                  const XMLString& uri = _namespaces.getURI(prefix);
                  if ((uri.empty() || uri != namespaceURI) && !namespaceURI.empty())
                  {
                        _namespaces.declarePrefix(prefix, namespaceURI);
                  }
            }
      }
}


void XMLWriter::addNamespaceAttributes(AttributeMap& attributeMap)
{
      NamespaceSupport::PrefixSet prefixes;
      _namespaces.getDeclaredPrefixes(prefixes);
      for (NamespaceSupport::PrefixSet::const_iterator it = prefixes.begin(); it != prefixes.end(); ++it)
      {
            XMLString prefix = *it;
            XMLString uri    = _namespaces.getURI(prefix);
            XMLString qname  = NamespaceSupport::XMLNS_NAMESPACE_PREFIX;
            
            if (!prefix.empty())
            {
                  qname.append(toXMLString(MARKUP_COLON));
                  qname.append(prefix);
            }
            attributeMap[qname] = uri;
      }
}


void XMLWriter::addAttributes(AttributeMap& attributeMap, const Attributes& attributes, const XMLString& elementNamespaceURI)
{
      for (int i = 0; i < attributes.getLength(); i++)
      {
            XMLString namespaceURI = attributes.getURI(i);
            XMLString localName    = attributes.getLocalName(i);
            XMLString qname        = attributes.getQName(i);
            if (!localName.empty())
            {
                  XMLString prefix;
                  if (namespaceURI != elementNamespaceURI)
                        prefix = _namespaces.getPrefix(namespaceURI);
                  if (!prefix.empty())
                  {
                        qname = prefix;
                        qname.append(toXMLString(MARKUP_COLON));
                  }
                  else qname.clear();
                  qname.append(localName);
            }
            attributeMap[qname] = attributes.getValue(i);
      }
}


void XMLWriter::writeAttributes(const AttributeMap& attributeMap)
{
      for (AttributeMap::const_iterator it = attributeMap.begin(); it != attributeMap.end(); ++it)
      {
            writeMarkup(MARKUP_SPACE);
            writeXML(it->first);
            writeMarkup(MARKUP_EQQUOT);
            characters(it->second);
            writeMarkup(MARKUP_QUOT);
      }
}


void XMLWriter::writeMarkup(const std::string& str) const
{
      _pTextConverter->write(str.data(), (int) str.size());
}


void XMLWriter::writeXML(const XMLString& str) const
{
      _pTextConverter->write((const char*) str.data(), (int) str.size()*sizeof(XMLChar));
}


void XMLWriter::writeXML(XMLChar ch) const
{
      _pTextConverter->write((const char*) &ch, sizeof(ch));
}


void XMLWriter::writeName(const XMLString& prefix, const XMLString& localName)
{
      if (prefix.empty())
      {
            writeXML(localName);
      }
      else
      {
            writeXML(prefix); 
            writeMarkup(MARKUP_COLON); 
            writeXML(localName);
      }
}


void XMLWriter::writeNewLine() const
{
      if (_options & PRETTY_PRINT)
            writeMarkup(_newLine);
}


void XMLWriter::writeIndent() const
{
      for (int i = 0; i < _depth; ++i)
            writeMarkup(MARKUP_TAB);
}


void XMLWriter::writeXMLDeclaration()
{
      writeMarkup("<?xml version=\"1.0\"");
      if (!_encoding.empty())
      {
            writeMarkup(" encoding=\"");
            writeMarkup(_encoding);
            writeMarkup("\"");
      }
      writeMarkup("?>");
      writeNewLine();
}


std::string XMLWriter::nameToString(const XMLString& localName, const XMLString& qname)
{
      if (qname.empty())
            return fromXMLString(localName);
      else
            return fromXMLString(qname);
}


XMLString XMLWriter::newPrefix()
{
      std::ostringstream str;
      str << "ns" << ++_prefix;
      return toXMLString(str.str());
}


} } // namespace Poco::XML

Generated by  Doxygen 1.6.0   Back to index