Skip to content
Snippets Groups Projects
DOMLSSerializerImpl.cpp 61.6 KiB
Newer Older
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *      http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
#include "DOMLSSerializerImpl.hpp"
#include "DOMErrorImpl.hpp"
#include "DOMLocatorImpl.hpp"
Alberto Massari's avatar
Alberto Massari committed
#include "DOMStringListImpl.hpp"

#include <xercesc/framework/MemBufFormatTarget.hpp>
#include <xercesc/framework/LocalFileFormatTarget.hpp>

#include <xercesc/util/TransService.hpp>
#include <xercesc/util/TranscodingException.hpp>
#include <xercesc/util/Janitor.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/XMLUniDefs.hpp>
#include <xercesc/dom/StDOMNode.hpp>
#include <xercesc/util/OutOfMemoryException.hpp>
#include <xercesc/util/XMLChar.hpp>
Tinny Ng's avatar
Tinny Ng committed
XERCES_CPP_NAMESPACE_BEGIN


// ---------------------------------------------------------------------------
//  Local const data
//
// ---------------------------------------------------------------------------

static const int INVALID_FEATURE_ID               = -1;
static const int CANONICAL_FORM_ID                = 0x0;
static const int DISCARD_DEFAULT_CONTENT_ID       = 0x1;
static const int ENTITIES_ID                      = 0x2;
static const int FORMAT_PRETTY_PRINT_ID           = 0x3;
static const int NORMALIZE_CHARACTERS_ID          = 0x4;
static const int SPLIT_CDATA_SECTIONS_ID          = 0x5;
static const int VALIDATION_ID                    = 0x6;
static const int WHITESPACE_IN_ELEMENT_CONTENT_ID = 0x7;
static const int BYTE_ORDER_MARK_ID               = 0x8;
Alberto Massari's avatar
Alberto Massari committed
static const int XML_DECLARATION                  = 0x9;
static const int FORMAT_PRETTY_PRINT_1ST_LEVEL_ID = 0xA;

//    feature                      true                       false
// ================================================================================
//canonical-form                 [optional] Not Supported     [required] (default)
//discard-default-content        [required] (default)         [required]
//entity                         [required] (default)         [optional]
//format-pretty-print            [optional] Partially Supported [required] (default)
//normalize-characters           [optional] Not Supported     [required] (default)
//split-cdata-sections           [required] (default)         [required]
//validation                     [optional] Not Supported     [required] (default)
//whitespace-in-element-content  [requierd] (default)         [optional] Not Supported
//

//
Tinny Ng's avatar
Tinny Ng committed
// Each feature has 2 entries in this array,
// the first for "true",
// the second for "false".
//
static const bool  featuresSupported[] = {
    false, true,  // canonical-form
Tinny Ng's avatar
Tinny Ng committed
    true,  true,  // discard-default-content
    true,  true,  // entity
    true,  true,  // format-pretty-print
    false, true,  // normalize-characters
    true,  true,  // split-cdata-sections
    false, true,  // validation
    true,  false, // whitespace-in-element-content
Alberto Massari's avatar
Alberto Massari committed
    true,  true,  // http://apache.org/xml/features/dom/byte-order-mark
    true,  true,  // xml-declaration
    true,  true   // http://apache.org/xml/features/pretty-print/space-first-level-elements
};

// default end-of-line sequence
static const XMLCh  gEOLSeq[] =
{
};

//UTF-8
static const XMLCh  gUTF8[] =
{
    chLatin_U, chLatin_T, chLatin_F, chDash, chDigit_8, chNull
};

//</
static const XMLCh  gEndElement[] =
{
};

//?>
static const XMLCh  gEndPI[] =
static const XMLCh  gStartPI[] =
{
//<?xml version="
static const XMLCh  gXMLDecl_VersionInfo[] =
    chOpenAngle, chQuestion, chLatin_x,     chLatin_m,  chLatin_l,  chSpace,
    chLatin_v,   chLatin_e,  chLatin_r,     chLatin_s,  chLatin_i,  chLatin_o,
    chLatin_n,   chEqual,    chDoubleQuote, chNull
};

//encoding="
static const XMLCh  gXMLDecl_EncodingDecl[] =
{
    chLatin_e,  chLatin_n,  chLatin_c,  chLatin_o,      chLatin_d, chLatin_i,
    chLatin_n,  chLatin_g,  chEqual,    chDoubleQuote,  chNull
};

//" standalone="
static const XMLCh  gXMLDecl_SDDecl[] =
{
    chLatin_s, chLatin_t, chLatin_a,   chLatin_n,    chLatin_d,   chLatin_a,
Tinny Ng's avatar
Tinny Ng committed
    chLatin_l, chLatin_o, chLatin_n,   chLatin_e,    chEqual,     chDoubleQuote,
Tinny Ng's avatar
Tinny Ng committed
//"
static const XMLCh  gXMLDecl_separator[] =
//?>
static const XMLCh  gXMLDecl_endtag[] =
};

//<![CDATA[
static const XMLCh  gStartCDATA[] =
{
    chOpenAngle, chBang,    chOpenSquare, chLatin_C, chLatin_D,
    chLatin_A,   chLatin_T, chLatin_A,    chOpenSquare, chNull
};

//]]>
static const XMLCh  gEndCDATA[] =
{
//    chCloseSquare, chCloseAngle, chCloseAngle, chNull  // test only: ]>>
      chCloseSquare, chCloseSquare, chCloseAngle, chNull
};

//<!--
static const XMLCh  gStartComment[] =
{
    chOpenAngle, chBang, chDash, chDash, chNull
};

//-->
static const XMLCh  gEndComment[] =
{
    chDash, chDash, chCloseAngle, chNull
};

static const XMLCh  gStartDoctype[] =
{
    chOpenAngle, chBang,    chLatin_D, chLatin_O, chLatin_C, chLatin_T,
    chLatin_Y,   chLatin_P, chLatin_E, chSpace,   chNull
};

//PUBLIC "
static const XMLCh  gPublic[] =
{
    chLatin_P, chLatin_U, chLatin_B,     chLatin_L, chLatin_I,
    chLatin_C, chSpace,   chDoubleQuote, chNull
};

//SYSTEM "
static const XMLCh  gSystem[] =
{
    chLatin_S, chLatin_Y, chLatin_S,     chLatin_T, chLatin_E,
    chLatin_M, chSpace,   chDoubleQuote, chNull
};

static const XMLCh  gStartEntity[] =
{
    chOpenAngle, chBang,    chLatin_E, chLatin_N, chLatin_T, chLatin_I,
    chLatin_T,   chLatin_Y, chSpace,   chNull
};

//NDATA "
static const XMLCh  gNotation[] =
{
    chLatin_N, chLatin_D,     chLatin_A, chLatin_T, chLatin_A,
    chSpace,   chDoubleQuote, chNull
};

static const XMLByte  BOM_utf8[]    = {(XMLByte)0xEF, (XMLByte)0xBB, (XMLByte)0xBF, (XMLByte) 0};
static const XMLByte  BOM_utf16be[] = {(XMLByte)0xFE, (XMLByte)0xFF, (XMLByte) 0};
static const XMLByte  BOM_utf16le[] = {(XMLByte)0xFF, (XMLByte)0xFE, (XMLByte) 0};
static const XMLByte  BOM_ucs4be[]  = {(XMLByte)0x00, (XMLByte)0x00, (XMLByte)0xFE, (XMLByte)0xFF, (XMLByte) 0};
static const XMLByte  BOM_ucs4le[]  = {(XMLByte)0xFF, (XMLByte)0xFE, (XMLByte)0x00, (XMLByte)0x00, (XMLByte) 0};

//
// Notification of the error though error handler
//
// The application may instruct the engine to abort serialization
// by returning "false".
//
// REVISIT: update the locator ctor once the line#, col#, uri and offset
// are available from DOM3 core
//
// REVISIT: use throwing exception to abort serialization is an interesting
// thing here, since the serializer is a recusive function, we
// can't use return, obviously. However we may have multiple try/catch
// along its way going back to write(). So far we don't come up with a
// "short-cut" to go "directly" back.
//
#define  TRY_CATCH_THROW(action)                                     \
fFormatter->setUnRepFlags(XMLFormatter::UnRep_Fail);                 \
try                                                                  \
{                                                                    \
catch(TranscodingException const &e)                                 \
{                                                                    \
    reportError(nodeToWrite, DOMError::DOM_SEVERITY_FATAL_ERROR, e.getMessage());  \
    throw e;                                                         \
DOMLSSerializerImpl::~DOMLSSerializerImpl()
    fMemoryManager->deallocate(fNewLine);//delete [] fNewLine;
Alberto Massari's avatar
Alberto Massari committed
    delete fSupportedParameters;
    // we don't own/adopt error handler and filter
DOMLSSerializerImpl::DOMLSSerializerImpl(MemoryManager* const manager)
:fFeatures(0)
,fNewLine(0)
,fErrorHandler(0)
,fFilter(0)
,fDocumentVersion(XMLUni::fgVersion1_0)
,fEncodingUsed(0)
,fNewLineUsed(0)
,fFormatter(0)
,fErrorCount(0)
Alberto Massari's avatar
Alberto Massari committed
,fLineFeedInTextNodePrinted(false)
,fLastWhiteSpaceInTextNode(0)
    fNamespaceStack=new (fMemoryManager) RefVectorOf< RefHashTableOf<XMLCh> >(1,true, fMemoryManager);
    //
    // set features to default setting
    //
    setFeature(CANONICAL_FORM_ID,                false);
    setFeature(DISCARD_DEFAULT_CONTENT_ID,       true );
    setFeature(ENTITIES_ID,                      true );
    setFeature(FORMAT_PRETTY_PRINT_ID,           false);
    setFeature(NORMALIZE_CHARACTERS_ID,          false);
    setFeature(SPLIT_CDATA_SECTIONS_ID,          true );
    setFeature(VALIDATION_ID,                    false);
    setFeature(WHITESPACE_IN_ELEMENT_CONTENT_ID, true );
    setFeature(BYTE_ORDER_MARK_ID,               false);
    setFeature(XML_DECLARATION,                  true );
Alberto Massari's avatar
Alberto Massari committed
    setFeature(FORMAT_PRETTY_PRINT_1ST_LEVEL_ID, true );
Alberto Massari's avatar
Alberto Massari committed
    fSupportedParameters=new (fMemoryManager) DOMStringListImpl(12, fMemoryManager);
Alberto Massari's avatar
Alberto Massari committed
    fSupportedParameters->add(XMLUni::fgDOMErrorHandler);
    fSupportedParameters->add(XMLUni::fgDOMWRTCanonicalForm);
    fSupportedParameters->add(XMLUni::fgDOMWRTDiscardDefaultContent);
    fSupportedParameters->add(XMLUni::fgDOMWRTEntities);
    fSupportedParameters->add(XMLUni::fgDOMWRTFormatPrettyPrint);
    fSupportedParameters->add(XMLUni::fgDOMWRTNormalizeCharacters);
    fSupportedParameters->add(XMLUni::fgDOMWRTSplitCdataSections);
    fSupportedParameters->add(XMLUni::fgDOMWRTValidation);
    fSupportedParameters->add(XMLUni::fgDOMWRTWhitespaceInElementContent);
    fSupportedParameters->add(XMLUni::fgDOMWRTBOM);
    fSupportedParameters->add(XMLUni::fgDOMXMLDeclaration);
Alberto Massari's avatar
Alberto Massari committed
    fSupportedParameters->add(XMLUni::fgDOMWRTXercesPrettyPrint);
bool DOMLSSerializerImpl::canSetParameter(const XMLCh* featName
                                        , const void*  /*value*/) const
{
    if(XMLString::compareIStringASCII(featName, XMLUni::fgDOMErrorHandler)==0)
        return true;
    return false;
}

bool DOMLSSerializerImpl::canSetParameter(const XMLCh* featName
                                        , bool         state) const
    int featureId = INVALID_FEATURE_ID;
    return checkFeature(featName, false, featureId) ? canSetFeature(featureId, state) : false;
void DOMLSSerializerImpl::setParameter(const XMLCh* featName
                                     , const void*  value)
{
    if(XMLString::compareIStringASCII(featName, XMLUni::fgDOMErrorHandler)==0)
        fErrorHandler = (DOMErrorHandler*)value;
    else
        throw DOMException(DOMException::NOT_SUPPORTED_ERR, 0, fMemoryManager);
}

void DOMLSSerializerImpl::setParameter(const XMLCh* featName
                                     , bool         state)
    int featureId = INVALID_FEATURE_ID;
    checkFeature(featName, true, featureId);
    if (!canSetFeature(featureId, state))
        throw DOMException(DOMException::NOT_SUPPORTED_ERR, 0, fMemoryManager);

    setFeature(featureId, state);
    // setting "canonical-form" to true will set the parameters "format-pretty-print",
Alberto Massari's avatar
Alberto Massari committed
    // "discard-default-content", and "xml-declaration", to false
    if ((featureId == CANONICAL_FORM_ID) && state)
        setFeature(FORMAT_PRETTY_PRINT_ID, false);
Alberto Massari's avatar
Alberto Massari committed
        setFeature(FORMAT_PRETTY_PRINT_1ST_LEVEL_ID, false);
Alberto Massari's avatar
Alberto Massari committed
        setFeature(DISCARD_DEFAULT_CONTENT_ID, false);
        setFeature(XML_DECLARATION, false);
    }
    // Setting one of those parameters to true will set "canonical-form" to false.
    if ((featureId == FORMAT_PRETTY_PRINT_ID || featureId == DISCARD_DEFAULT_CONTENT_ID || featureId == XML_DECLARATION) && state)
        setFeature(CANONICAL_FORM_ID, false);
const void* DOMLSSerializerImpl::getParameter(const XMLCh* featName) const
{
    if(XMLString::compareIStringASCII(featName, XMLUni::fgDOMErrorHandler)==0)
    {
        return (void*)fErrorHandler;
    }
    else
    {
        int featureId = INVALID_FEATURE_ID;
        checkFeature(featName, true, featureId);
        return (void*)getFeature(featureId);
    }
Alberto Massari's avatar
Alberto Massari committed
const DOMStringList* DOMLSSerializerImpl::getParameterNames() const
void DOMLSSerializerImpl::setNewLine(const XMLCh* const newLine)
    fMemoryManager->deallocate(fNewLine);//delete [] fNewLine;
    fNewLine = XMLString::replicate(newLine, fMemoryManager);
const XMLCh* DOMLSSerializerImpl::getNewLine() const
void DOMLSSerializerImpl::setFilter(DOMLSSerializerFilter *filter)
DOMLSSerializerFilter* DOMLSSerializerImpl::getFilter() const
bool DOMLSSerializerImpl::write(const DOMNode* nodeToWrite,
                                DOMLSOutput* const destination)
    XMLFormatTarget* pTarget=destination->getByteStream();
    Janitor<XMLFormatTarget> janTarget(0);
    if(!pTarget)
    {
        const XMLCh* szSystemId=destination->getSystemId();
        if(!szSystemId)
        {
            //TODO: report error "missing target"
            return false;
        }
        pTarget=new LocalFileFormatTarget(szSystemId, fMemoryManager);
        janTarget.reset(pTarget);
    }
    /**
     * When writing to a LSOutput, the encoding is found by looking at the encoding information
     * that is reachable through the LSOutput and the item to be written (or its owner document) in this order:
     *
     *  1. LSOutput.encoding,
     *  2. Document.inputEncoding,
     *  3. Document.xmlEncoding.
     *
     * If no encoding is reachable through the above properties, a default encoding of "UTF-8" will be used.
     * If the specified encoding is not supported an "unsupported-encoding" fatal error is raised.
     */
    fEncodingUsed = gUTF8;

    const DOMDocument *docu = (nodeToWrite->getNodeType() == DOMNode::DOCUMENT_NODE)?
                              (const DOMDocument*)nodeToWrite : nodeToWrite->getOwnerDocument();

    const XMLCh* lsEncoding=destination->getEncoding();
    if (lsEncoding && *lsEncoding)
    {
        fEncodingUsed = lsEncoding;
    }
    else if (docu)
    {
        const XMLCh* tmpEncoding = docu->getInputEncoding();

        if ( tmpEncoding && *tmpEncoding)
        {
            fEncodingUsed = tmpEncoding;
        }
        else
        {
            tmpEncoding = docu->getXmlEncoding();

            if ( tmpEncoding && *tmpEncoding)
            {
                fEncodingUsed = tmpEncoding;
            }
        }
    }


    /**
     *  The end-of-line sequence of characters to be used in the XML being
     *  written out. The only permitted values are these:
     *     . null
     *
     *  Use a default end-of-line sequence. DOM implementations should choose
     * the default to match the usual convention for text files in the
     * environment being used. Implementations must choose a default
     * sequence that matches one of those allowed by  2.11 "End-of-Line
     * Handling".
     *
     *    CR    The carriage-return character (#xD)
     *    CR-LF The carriage-return and line-feed characters (#xD #xA)
     *    LF    The line-feed character (#xA)
     *
     *  The default value for this attribute is null
     */
    fNewLineUsed = (fNewLine && *fNewLine)? fNewLine : gEOLSeq;

    /**
     *  get Document Version
     */
    fDocumentVersion = (docu && docu->getXmlVersion() && *(docu->getXmlVersion()))?docu->getXmlVersion():XMLUni::fgVersion1_0;
    fIsXml11 = XMLString::equals(fDocumentVersion, XMLUni::fgVersion1_1);
Alberto Massari's avatar
Alberto Massari committed
    fLineFeedInTextNodePrinted = false;
    fLastWhiteSpaceInTextNode = 0;

        fFormatter = new (fMemoryManager) XMLFormatter( fEncodingUsed
                                                       ,fDocumentVersion
                                                       ,pTarget
                                                       ,XMLFormatter::NoEscapes
                                                       ,XMLFormatter::UnRep_CharRef
                                                       ,fMemoryManager);
    }
    catch (const TranscodingException& e)
    {
        reportError(nodeToWrite, DOMError::DOM_SEVERITY_FATAL_ERROR, e.getMessage());
        return false;
    }

    try
    {
        Janitor<XMLFormatter> janName(fFormatter);
        processNode(nodeToWrite);
    }

    //
    // The serialize engine (processNode) throws an exception to abort
    // serialization if
    //
    //   . A fatal error occurs which renders the output ill-formed, or
    //   . Instructed by the application's error handler
    //
    catch (const TranscodingException&)
    {
        throw;
    }

    //
    // true if node was successfully serialized and
    // false in case a failure occured and the
    // failure wasn't canceled by the error handler.
    //
    return ((fErrorCount == 0)? true : false);
bool DOMLSSerializerImpl::writeToURI(const DOMNode* nodeToWrite, const XMLCh* uri)
{
    DOMLSOutputImpl output(fMemoryManager);
    output.setSystemId(uri);
    return write(nodeToWrite, &output);
//
// We don't throw DOMSTRING_SIZE_ERR since we are no longer
// using DOMString.
//
XMLCh* DOMLSSerializerImpl::writeToString(const DOMNode* nodeToWrite, MemoryManager* manager /*= NULL*/)
    if(manager==NULL)
        manager = fMemoryManager;
    MemBufFormatTarget  destination(1023, manager);
Alberto Massari's avatar
Alberto Massari committed
    bool bBOMFlag=getFeature(BYTE_ORDER_MARK_ID);
    setFeature(BYTE_ORDER_MARK_ID, false);
        output.setByteStream(&destination);
        output.setEncoding(XMLUni::fgUTF16EncodingString);
        retVal = write(nodeToWrite, &output);
        // there is a possibility that memory allocation
        // exception thrown in XMLBuffer class
        //
Alberto Massari's avatar
Alberto Massari committed
        setFeature(BYTE_ORDER_MARK_ID, bBOMFlag);
Alberto Massari's avatar
Alberto Massari committed
    setFeature(BYTE_ORDER_MARK_ID, bBOMFlag);
    return (retVal ? XMLString::replicate(reinterpret_cast<const XMLCh*>(destination.getRawBuffer()), manager) : 0);
}

//
// Characters not representable in output encoding,
//
// 1. CHARACTER DATA (outside of markup)                --- no error
//    ordinary character  -> numeric character reference
//    '<' and '&'         -> &lt; and &amp;
// 2. Within MARKUP, but outside of attributes
//    reported as an error                                 --- ERROR
//    markup:
//           start tag                                  done
//           end tag                                    done
//           empty element tag                          done
//           entity references                          done
//           character references    // REVISIT
//           comments                                   done
//           CDATA section delimiters                   done, done
//           document type declarartions                done
//           processing instructions (PI)               done
//
// 3. With in ATTRIBUTE
//    -> numeric character reference
//    no quotes                        -> in quotes
//    with quotes, no apostrophe       -> in apostrophe
//    with quotes and apostrophe       -> in quotes and &quot;
//
// 4. CDATA sections
//    "split_cdata_section"  true                      --- char ref
//                           false                     ---      ERROR
//
// ---------------------------------------------------------------------------
//  Stream out a DOM node, and, recursively, all of its children. This
//  function is the heart of writing a DOM tree out as XML source. Give it
//  a document node and it will do the whole thing.
// ---------------------------------------------------------------------------
void DOMLSSerializerImpl::processNode(const DOMNode* const nodeToWrite, int level)
    // Get the name and value out for convenience
    const XMLCh*    nodeName = nodeToWrite->getNodeName();
    const XMLCh*    nodeValue = nodeToWrite->getNodeValue();
    XMLSize_t       lent = XMLString::stringLen(nodeValue);

    switch (nodeToWrite->getNodeType())
    {
            if (checkFilter(nodeToWrite) != DOMNodeFilter::FILTER_ACCEPT)
                break;
            ensureValidString(nodeToWrite, nodeValue);
PeiYong Zhang's avatar
PeiYong Zhang committed
            if (getFeature(FORMAT_PRETTY_PRINT_ID))
Alberto Massari's avatar
Alberto Massari committed
                fLineFeedInTextNodePrinted = false;
                fLastWhiteSpaceInTextNode = 0;
PeiYong Zhang's avatar
PeiYong Zhang committed

                if(XMLChar1_0::isAllSpaces(nodeValue, XMLString::stringLen(nodeValue)))
PeiYong Zhang's avatar
PeiYong Zhang committed
                {
                    // skips whitespace-only text nodes unless whitespace-in-element is set.
                    if (!getFeature(WHITESPACE_IN_ELEMENT_CONTENT_ID))
                    {
                        break;
                    }
PeiYong Zhang's avatar
PeiYong Zhang committed
                    {
                        //
                        // we need to trace if newline(s) have been printed out
                        // to avoid generate extra newline for pretty printing,
                        // as well as the number of whitespaces after the last
                        // newline character to do indentation properly.
                        //
                        int pos = XMLString::lastIndexOf(nodeValue, chLF);
                        if (-1 != pos)
                        {
Alberto Massari's avatar
Alberto Massari committed
                            fLineFeedInTextNodePrinted = true;
                            fLastWhiteSpaceInTextNode = (unsigned int)(lent - pos);
PeiYong Zhang's avatar
PeiYong Zhang committed
                        }
                        else
                        {
                            // for those platforms using chCR alone as
                            // a newline character
                            pos = XMLString::lastIndexOf(nodeValue, chCR);
                            if (-1 != pos)
                            {
Alberto Massari's avatar
Alberto Massari committed
                                fLineFeedInTextNodePrinted = true;
                                fLastWhiteSpaceInTextNode = (unsigned int)(lent - pos);
            fFormatter->formatBuf(nodeValue, lent, XMLFormatter::CharEscapes);
            break;
        }

    case DOMNode::PROCESSING_INSTRUCTION_NODE:
            if (checkFilter(nodeToWrite) != DOMNodeFilter::FILTER_ACCEPT)
                break;

            ensureValidString(nodeToWrite, nodeName);
            ensureValidString(nodeToWrite, nodeValue);

Alberto Massari's avatar
Alberto Massari committed
            if(level == 1 && getFeature(FORMAT_PRETTY_PRINT_1ST_LEVEL_ID))
                printNewLine();

            printNewLine();
            printIndent(level);

            TRY_CATCH_THROW
            (
                *fFormatter << XMLFormatter::NoEscapes << gStartPI << nodeName;
                if (lent > 0)
                {
                    *fFormatter << chSpace << nodeValue;
                }
                *fFormatter << gEndPI;
            )
    case DOMNode::DOCUMENT_NODE: // Not to be shown to Filter
            const DOMDocument *docu = (const DOMDocument*)nodeToWrite;
Tinny Ng's avatar
Tinny Ng committed
            //[23] XMLDecl      ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>'
            //[24] VersionInfo  ::= S 'version' Eq ("'" VersionNum "'" | '"' VersionNum '"')
Tinny Ng's avatar
Tinny Ng committed
            //[80] EncodingDecl ::= S 'encoding' Eq ('"' EncName '"' | "'" EncName
            //[32] SDDecl       ::= S 'standalone' Eq (("'" ('yes' | 'no') "'") | ('"' ('yes' | 'no') '"'))
Neil Graham's avatar
Neil Graham committed
            if (getFeature(XML_DECLARATION)) {
                *fFormatter << gXMLDecl_VersionInfo << fDocumentVersion << gXMLDecl_separator;
                *fFormatter << gXMLDecl_EncodingDecl << fEncodingUsed << gXMLDecl_separator;
                const XMLCh* st = (docu->getXmlStandalone())? XMLUni::fgYesString : XMLUni::fgNoString;
                *fFormatter << gXMLDecl_SDDecl << st << gXMLDecl_separator;
                *fFormatter << gXMLDecl_endtag;
            }
            DOMNodeSPtr child = nodeToWrite->getFirstChild();
                child = child->getNextSibling();
            }
            printNewLine();
            break;
        }

    case DOMNode::DOCUMENT_FRAGMENT_NODE:
        {
            setURCharRef();

            DOMNode *child = nodeToWrite->getFirstChild();
            while( child != 0)
            {
                processNode(child, level);
                child = child->getNextSibling();
            }
            printNewLine();
            DOMNodeFilter::FilterAction filterAction = checkFilter(nodeToWrite);
            if ( filterAction == DOMNodeFilter::FILTER_REJECT)
                break;
Alberto Massari's avatar
Alberto Massari committed
            if (!fLineFeedInTextNodePrinted)
PeiYong Zhang's avatar
PeiYong Zhang committed
            {
Alberto Massari's avatar
Alberto Massari committed
                if(level == 1 && getFeature(FORMAT_PRETTY_PRINT_1ST_LEVEL_ID))
PeiYong Zhang's avatar
PeiYong Zhang committed
                    printNewLine();

                printNewLine();
PeiYong Zhang's avatar
PeiYong Zhang committed
            }
            else
            {
Alberto Massari's avatar
Alberto Massari committed
                fLineFeedInTextNodePrinted = false;
PeiYong Zhang's avatar
PeiYong Zhang committed
            }

            //track the line number the current node begins on
            int nodeLine = fCurrentLine;

            // add an entry in the namespace stack
            if ( filterAction == DOMNodeFilter::FILTER_ACCEPT)
                //           this element    attributes   child elements
                // accept        yes             yes           yes
                // skip          no              no            yes
                //
                TRY_CATCH_THROW
                (
                // The name has to be representable without any escapes
                    *fFormatter  << XMLFormatter::NoEscapes
                                 << chOpenAngle << nodeName;
                )

                // Output any attributes on this element
                setURCharRef();
                DOMNamedNodeMap *attributes = nodeToWrite->getAttributes();
Alberto Massari's avatar
Alberto Massari committed
                XMLSize_t attrCount = attributes->getLength();
                // check if the namespace for the current node is already defined
                const XMLCh* prefix = nodeToWrite->getPrefix();
                const XMLCh* uri = nodeToWrite->getNamespaceURI();
                if((uri && uri[0]) || ((prefix==0 || prefix[0]==0) && isDefaultNamespacePrefixDeclared()))
                {
                    if(prefix==0 || prefix[0]==0)
                        prefix=XMLUni::fgZeroLenString;
                    if(!isNamespaceBindingActive(prefix, uri))
                        if(namespaceMap==NULL)
                        {
                            namespaceMap=new (fMemoryManager) RefHashTableOf<XMLCh>(12, false, fMemoryManager);
                            fNamespaceStack->addElement(namespaceMap);
                        }
                        namespaceMap->put((void*)prefix,(XMLCh*)uri);
                                     << chSpace << XMLUni::fgXMLNSString;
                        if(!XMLString::equals(prefix,XMLUni::fgZeroLenString))
                            *fFormatter  << chColon << prefix;
                        *fFormatter  << chEqual << chDoubleQuote
                                     << XMLFormatter::AttrEscapes
                bool discard = getFeature(DISCARD_DEFAULT_CONTENT_ID);
Alberto Massari's avatar
Alberto Massari committed
                for (XMLSize_t i = 0; i < attrCount; i++)
                    DOMAttrSPtr  attribute = (DOMAttr*)attributes->item(i);

                    // Not to be shown to Filter

                    //
                    //"discard-default-content"
                    //    true
                    //    [required] (default)
                    //    Use whatever information available to the implementation
                    //  (i.e. XML schema, DTD, the specified flag on Attr nodes,
                    //  and so on) to decide what attributes and content should be
                    //  discarded or not.
                    //  Note that the specified flag on Attr nodes in itself is
                    //  not always reliable, it is only reliable when it is set
                    //  to false since the only case where it can be set to false
                    //  is if the attribute was created by the implementation.
                    //  The default content won't be removed if an implementation
                    //  does not have any information available.
                    //    false
                    //    [required]
                    //    Keep all attributes and all content.
                    //
                    if (discard && !((DOMAttr*)attribute )->getSpecified())
                        continue;
                    //
                    //  Again the name has to be completely representable. But the
                    //  attribute can have refs and requires the attribute style
                    //  escaping.
                    //

                    // if this attribute is a namespace declaration, add it to the namespace map for the current level
                    const XMLCh* ns = attribute->getNamespaceURI();
                    if (ns != 0 )
                    {
                        if(XMLString::equals(ns, XMLUni::fgXMLNSURIName))
                            if(namespaceMap==NULL)
                            {
                                namespaceMap=new (fMemoryManager) RefHashTableOf<XMLCh>(12, false, fMemoryManager);
                                fNamespaceStack->addElement(namespaceMap);
                            }
                            const XMLCh* nsPrefix = attribute->getLocalName();
                            if(XMLString::equals(attribute->getNodeName(),XMLUni::fgXMLNSString))
                                nsPrefix = XMLUni::fgZeroLenString;
                            if(namespaceMap->containsKey((void*)nsPrefix))
                                continue;
                            namespaceMap->put((void*)attribute->getLocalName(),(XMLCh*)attribute->getNodeValue());
                        }
                        else if(!XMLString::equals(ns, XMLUni::fgXMLURIName))
                        {
                            // check if the namespace for the current node is already defined
                            const XMLCh* prefix = attribute->getPrefix();
                            if(prefix && prefix[0])
                            {
                                const XMLCh* uri = attribute->getNamespaceURI();
                                if(!isNamespaceBindingActive(prefix, uri))
                                    if(namespaceMap==NULL)
                                    {
                                        namespaceMap=new (fMemoryManager) RefHashTableOf<XMLCh>(12, false, fMemoryManager);
                                        fNamespaceStack->addElement(namespaceMap);
                                    }
                                    namespaceMap->put((void*)prefix,(XMLCh*)uri);
                                                 << chSpace << XMLUni::fgXMLNSString << chColon << prefix
                                                 << chEqual << chDoubleQuote
                                                 << XMLFormatter::AttrEscapes
Alberto Massari's avatar
Alberto Massari committed
                    if (XMLString::equals(ns, XMLUni::fgXMLNSURIName) || checkFilter(attribute) == DOMNodeFilter::FILTER_ACCEPT)
                    {
                        *fFormatter  << XMLFormatter::NoEscapes
                                     << chSpace << attribute->getNodeName()
                                     << chEqual << chDoubleQuote
                                     << XMLFormatter::AttrEscapes;
                        if (getFeature(ENTITIES_ID))
                        {
                            DOMNodeSPtr child = attribute->getFirstChild();
                            while( child != 0)
                            {
                                if(child->getNodeType()==DOMNode::TEXT_NODE)
                                {
                                    ensureValidString(attribute, child->getNodeValue());
                                else if(child->getNodeType()==DOMNode::ENTITY_REFERENCE_NODE)
                                    *fFormatter << XMLFormatter::NoEscapes
                                                << chAmpersand << child->getNodeName() << chSemiColon
                                                << XMLFormatter::AttrEscapes;
                                child = child->getNextSibling();
                            }
                        }
                        else
                        {
                            ensureValidString(attribute, attribute->getNodeValue());
                            *fFormatter  << attribute->getNodeValue();
Alberto Massari's avatar
Alberto Massari committed
                                     << chDoubleQuote;
                    }
                } // end of for
            } // end of FILTER_ACCEPT

            //
            //  Test for the presence of children, which includes both
            //  text content and nested elements.
            //
            DOMNodeSPtr child = nodeToWrite->getFirstChild();
            if (child != 0)
            {
                // There are children. Close start-tag, and output children.
                // No escapes are legal here
                if (filterAction == DOMNodeFilter::FILTER_ACCEPT)
                    *fFormatter << XMLFormatter::NoEscapes << chCloseAngle;

                    child = child->getNextSibling();
                }

                level--;

                if (filterAction == DOMNodeFilter::FILTER_ACCEPT)
                {
                    //if we are not on the same line as when we started
                    //this node then print a new line and indent
                    if(nodeLine != fCurrentLine)
                    {
Alberto Massari's avatar
Alberto Massari committed
                        if (!fLineFeedInTextNodePrinted)
PeiYong Zhang's avatar
PeiYong Zhang committed
                        {
                            printNewLine();
                        }
                        else
                        {
Alberto Massari's avatar
Alberto Massari committed
                            fLineFeedInTextNodePrinted = false;
PeiYong Zhang's avatar
PeiYong Zhang committed
                        }
Alberto Massari's avatar
Alberto Massari committed
                        if(nodeLine != fCurrentLine && level == 0 && getFeature(FORMAT_PRETTY_PRINT_1ST_LEVEL_ID))
                            printNewLine();

                         *fFormatter << XMLFormatter::NoEscapes << gEndElement
                                     << nodeName << chCloseAngle;