Newer
Older
 * Copyright 2002-2004 The Apache Software Foundation.
 * 
 * Licensed 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.
 */
/*
 * $Id$
 */
#include "DOMLSSerializerImpl.hpp"
Khaled Noaman
committed
#include "DOMErrorImpl.hpp"
#include "DOMLocatorImpl.hpp"
Tinny Ng
committed
#include "DOMImplementationImpl.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>
Tinny Ng
committed
#include <xercesc/util/XMLMsgLoader.hpp>
#include <xercesc/dom/StDOMNode.hpp>
Neil Graham
committed
#include <xercesc/util/OutOfMemoryException.hpp>
// ---------------------------------------------------------------------------
//  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;
//    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
//
//
// the first for "true",
// the second for "false".
//
static const bool  featuresSupported[] = {
    false, true,  // canonical-form
    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
    true,  true,   // byte-order-mark
    true,  true   // xml-declaration
};
// default end-of-line sequence
static const XMLCh  gEOLSeq[] =
{
    chLF, chNull
};
//UTF-8
static const XMLCh  gUTF8[] =
{
    chLatin_U, chLatin_T, chLatin_F, chDash, chDigit_8, chNull
};
//</
static const XMLCh  gEndElement[] =
{
    chOpenAngle, chForwardSlash, chNull
};
//?>
static const XMLCh  gEndPI[] =
    chQuestion, chCloseAngle, chNull
static const XMLCh  gStartPI[] =
{
    chOpenAngle, chQuestion, chNull
//<?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
};
static const XMLCh gXMLDecl_ver10[] =
{
    chDigit_1, chPeriod, chDigit_0, 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,
    chLatin_l, chLatin_o, chLatin_n,   chLatin_e,    chEqual,     chDoubleQuote,
static const XMLCh  gXMLDecl_separator[] =
    chDoubleQuote, chSpace, chNull
//?>
static const XMLCh  gXMLDecl_endtag[] =
    chQuestion, chCloseAngle,  chNull
};
//<![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 int offset = XMLString::stringLen(gEndCDATA);
//<!--
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 XMLCh  gFeature[] =
{
    chLatin_F, chLatin_e, chLatin_a, chLatin_t, chLatin_u, chLatin_r,
    chLatin_e, chSpace,   chNull
};
static const XMLCh  gCantSet[] =
{
    chSpace,   chLatin_C, chLatin_a, chLatin_n, chSpace, chLatin_n, chLatin_o,
    chLatin_t, chSpace,   chLatin_b, chLatin_e, chSpace, chLatin_s,
    chLatin_e, chLatin_t, chSpace,   chLatin_t, chLatin_o, chSpace, chNull
};
static const XMLCh  gTrue[] =
{
    chSingleQuote, chLatin_t, chLatin_r, chLatin_u, chLatin_e,
    chSingleQuote, chLF,      chNull
};
static const XMLCh  gFalse[] =
{
    chSingleQuote, chLatin_f, chLatin_a, chLatin_l, chLatin_s,
    chLatin_e,     chSingleQuote, chLF, chNull
};
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};
static bool lineFeedInTextNodePrinted = false;
static int  lastWhiteSpaceInTextNode = 0;
Gareth Reakes
committed
//
// 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, forceToRethrow)                     \
fFormatter->setUnRepFlags(XMLFormatter::UnRep_Fail);                 \
try                                                                  \
{                                                                    \
    action;                                                          \
}                                                                    \
catch(TranscodingException const &e)                                 \
{                                                                    \
    if ( !reportError(nodeToWrite                                    \
                    , DOMError::DOM_SEVERITY_FATAL_ERROR             \
                    , e.getMessage())                       ||       \
          forceToRethrow)                                            \
        throw e;                                                       \
DOMLSSerializerImpl::~DOMLSSerializerImpl()
    fMemoryManager->deallocate(fEncoding);//delete [] fEncoding;
    fMemoryManager->deallocate(fNewLine);//delete [] fNewLine;
Gareth Reakes
committed
    delete fNamespaceStack;
    // we don't own/adopt error handler and filter
DOMLSSerializerImpl::DOMLSSerializerImpl(MemoryManager* const manager)
:fFeatures(0)
,fEncoding(0)
,fNewLine(0)
,fErrorHandler(0)
,fFilter(0)
,fDocumentVersion(XMLUni::fgVersion1_0)
,fSupportedParameters(0)
,fEncodingUsed(0)
,fNewLineUsed(0)
,fFormatter(0)
,fErrorCount(0)
,fCurrentLine(0)
Gareth Reakes
committed
,fNamespaceStack(0)
,fMemoryManager(manager)
    fNamespaceStack=new (fMemoryManager) RefVectorOf< RefHashTableOf<XMLCh> >(0,true, fMemoryManager);
Gareth Reakes
committed
    //
    // 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 );
    fSupportedParameters=new RefVectorOf<XMLCh>(11, false, manager);
    fSupportedParameters->addElement((XMLCh*)XMLUni::fgDOMErrorHandler);
    fSupportedParameters->addElement((XMLCh*)XMLUni::fgDOMWRTCanonicalForm);
    fSupportedParameters->addElement((XMLCh*)XMLUni::fgDOMWRTDiscardDefaultContent);
    fSupportedParameters->addElement((XMLCh*)XMLUni::fgDOMWRTEntities);
    fSupportedParameters->addElement((XMLCh*)XMLUni::fgDOMWRTFormatPrettyPrint);
    fSupportedParameters->addElement((XMLCh*)XMLUni::fgDOMWRTNormalizeCharacters);
    fSupportedParameters->addElement((XMLCh*)XMLUni::fgDOMWRTSplitCdataSections);
    fSupportedParameters->addElement((XMLCh*)XMLUni::fgDOMWRTValidation);
    fSupportedParameters->addElement((XMLCh*)XMLUni::fgDOMWRTWhitespaceInElementContent);
    fSupportedParameters->addElement((XMLCh*)XMLUni::fgDOMWRTBOM);
    fSupportedParameters->addElement((XMLCh*)XMLUni::fgDOMXMLDeclaration);
bool DOMLSSerializerImpl::canSetParameter(const XMLCh* const featName
                                        , const void*        value) const
{
    if(XMLString::compareIStringASCII(featName, XMLUni::fgDOMErrorHandler)==0)
        return true;
    return false;
}
bool DOMLSSerializerImpl::canSetParameter(const XMLCh* const featName
                                        , bool               state) const
    int featureId = INVALID_FEATURE_ID;
    return checkFeature(featName, false, featureId) ? canSetFeature(featureId, state) : false;
void DOMLSSerializerImpl::setParameter(const XMLCh* const 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* const featName
                                     , bool               state)
    int featureId = INVALID_FEATURE_ID;
    checkFeature(featName, true, featureId);
    if (!canSetFeature(featureId, state))
    {
        XMLCh  tmpbuf[256];
        unsigned int strLen = XMLString::stringLen(gFeature) +
                              XMLString::stringLen(featName) +
                              XMLString::stringLen(gCantSet) +
                              XMLString::stringLen(gFalse);
        XMLString::copyString(tmpbuf, gFeature);
        if (strLen < 256)
        {
            XMLString::catString(tmpbuf, featName);
        }
        else
        {
            // truncate the featurename to fit into the 256 buffer
            XMLString::copyNString(tmpbuf+XMLString::stringLen(gFeature),
                                   featName, 200);
        }
        XMLString::catString(tmpbuf, gCantSet);
        XMLString::catString(tmpbuf, state? gTrue : gFalse);
        throw DOMException(DOMException::NOT_SUPPORTED_ERR, tmpbuf, fMemoryManager);
    else
        setFeature(featureId, state);
    //
    // canonical-form and format-pretty-print can not be both set to true
    // meaning set canonical-form true will automatically set
    // format-pretty-print to false and vise versa.
    if ((featureId == CANONICAL_FORM_ID) && state)
        setFeature(FORMAT_PRETTY_PRINT_ID, false);
    if ((featureId == FORMAT_PRETTY_PRINT_ID) && state)
        setFeature(CANONICAL_FORM_ID, false);
const void* DOMLSSerializerImpl::getParameter(const XMLCh* const 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);
    }
const RefVectorOf<XMLCh>* DOMLSSerializerImpl::getParameterNames() const
    return fSupportedParameters;
}
// we don't check the validity of the encoding set
void DOMLSSerializerImpl::setEncoding(const XMLCh* const encoding)
    fMemoryManager->deallocate(fEncoding);//delete [] fEncoding;
    fEncoding = XMLString::replicate(encoding, fMemoryManager);
const XMLCh* DOMLSSerializerImpl::getEncoding() const
    return fEncoding;
void DOMLSSerializerImpl::setNewLine(const XMLCh* const newLine)
    fMemoryManager->deallocate(fNewLine);//delete [] fNewLine;
    fNewLine = XMLString::replicate(newLine, fMemoryManager);
const XMLCh* DOMLSSerializerImpl::getNewLine() const
    return fNewLine;
void DOMLSSerializerImpl::setFilter(DOMLSSerializerFilter *filter)
    fFilter = filter;
DOMLSSerializerFilter* DOMLSSerializerImpl::getFilter() const
    return fFilter;
}
//
//
//
bool DOMLSSerializerImpl::write(const DOMNode*         nodeToWrite,
                                XMLFormatTarget* const destination)
    //init session vars
    initSession(nodeToWrite);
        fFormatter = new (fMemoryManager) XMLFormatter(fEncodingUsed
                                     ,fDocumentVersion
                                     ,destination
                                     ,XMLFormatter::NoEscapes
Alberto Massari
committed
                                     ,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 renters the output ill-formed, or
    //   . Instructed by the application's error handler
    //
    catch (const TranscodingException&)
    {
        return false;
    }
Tinny Ng
committed
    catch (const XMLDOMMsg::Codes)
    {
        return false;
    }
Neil Graham
committed
    catch(const OutOfMemoryException&)
    {
        throw;
    }
    //
    // DOMSystemException
    // This exception will be raised in response to any sort of IO or system
    // error that occurs while writing to the destination. It may wrap an
    // underlying system exception.
    //
    //catch (RuntimeException const &)
    catch (...)
    {
        // REVISIT generate a DOMSystemException wrapping the underlying
        //         exception.
        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)
{
    LocalFileFormatTarget target(uri);
    return write(nodeToWrite, &target);
}
//
// We don't throw DOMSTRING_SIZE_ERR since we are no longer
// using DOMString.
//
XMLCh* DOMLSSerializerImpl::writeToString(const DOMNode* nodeToWrite)
Khaled Noaman
committed
    MemBufFormatTarget  destination(1023, fMemoryManager);
    bool retVal;
    // XMLCh is unicode, assume fEncoding as UTF-16
    XMLCh* tempEncoding = fEncoding;
    fEncoding = (XMLCh*) XMLUni::fgUTF16EncodingString;
    try
    {
        retVal = write(nodeToWrite, &destination);
    }
Neil Graham
committed
    catch(const OutOfMemoryException&)
    {
        throw;
    }
    catch (...)
    {
        //
        // there is a possibility that memory allocation
        // exception thrown in XMLBuffer class
        //
        fEncoding = tempEncoding;
        return 0;
    }
    fEncoding = tempEncoding;
    return (retVal ? XMLString::replicate((XMLCh*) destination.getRawBuffer(), fMemoryManager) : 0);
void DOMLSSerializerImpl::initSession(const DOMNode* const nodeToWrite)
 * The encoding to use when writing is determined as follows:
 *     If the encoding attribute has been set, that value will be used.
 *     If the encoding attribute is null or empty,
 *            but the item to be written, or
 *            the owner document specified encoding (ie. the "actualEncoding"
 *            from the document) that value will be used.
 *     If neither of the above provides an encoding name, a default encoding of
 */
    fEncodingUsed = gUTF8;
    if (fEncoding && *fEncoding)
    {
        fEncodingUsed = fEncoding;
    }
    else
    {
        const DOMDocument *docu = (nodeToWrite->getNodeType() == DOMNode::DOCUMENT_NODE)?
                            (const DOMDocument*)nodeToWrite : nodeToWrite->getOwnerDocument();
            const XMLCh* tmpEncoding = docu->getEncoding();
            if ( tmpEncoding && *tmpEncoding)
                fEncodingUsed = tmpEncoding;
                tmpEncoding = docu->getActualEncoding();
                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
     */
    const DOMDocument *docu = (nodeToWrite->getNodeType() == DOMNode::DOCUMENT_NODE)?
                              (const DOMDocument*)nodeToWrite : nodeToWrite->getOwnerDocument();
    if (docu)
    {
        fDocumentVersion = docu->getVersion();
    }
    fErrorCount = 0;
}
//
// Characters not representable in output encoding,
//
// 1. CHARACTER DATA (outside of markup)                --- no error
//    ordinary character  -> numeric character reference
//    '<' and '&'         -> < and &
// 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 "
//
//    "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();
    unsigned long   lent = XMLString::stringLen(nodeValue);
    switch (nodeToWrite->getNodeType())
    {
    case DOMNode::TEXT_NODE:
            if (checkFilter(nodeToWrite) != DOMNodeFilter::FILTER_ACCEPT)
                break;
            {
                lineFeedInTextNodePrinted = false;
                lastWhiteSpaceInTextNode = 0;
                if(XMLString::isAllWhiteSpace(nodeValue))
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
                {
                    // skips whitespace-only text nodes unless whitespace-in-element is set.
                    if (!getFeature(WHITESPACE_IN_ELEMENT_CONTENT_ID))
                    {
                        break;
                    }
                    else        
                    {
                        //
                        // 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)
                        {
                            lineFeedInTextNodePrinted = true;
                            lastWhiteSpaceInTextNode = lent - pos;
                        }
                        else
                        {
                            // for those platforms using chCR alone as
                            // a newline character
                            pos = XMLString::lastIndexOf(nodeValue, chCR);
                            if (-1 != pos)
                            {
                                lineFeedInTextNodePrinted = true;
                                lastWhiteSpaceInTextNode = lent - pos;
                            }
                        }
                    }
                }
            }
            setURCharRef();      // character data
            fFormatter->formatBuf(nodeValue, lent, XMLFormatter::CharEscapes);
            break;
        }
    case DOMNode::PROCESSING_INSTRUCTION_NODE:
            if (checkFilter(nodeToWrite) != DOMNodeFilter::FILTER_ACCEPT)
                break;
            if(level == 1)
                printNewLine();
            printNewLine();
            printIndent(level);
            TRY_CATCH_THROW
            (
                *fFormatter << XMLFormatter::NoEscapes << gStartPI << nodeName;
                if (lent > 0)
                {
                    *fFormatter << chSpace << nodeValue;
                }
                *fFormatter << gEndPI;
                ,true
            )
    case DOMNode::DOCUMENT_NODE: // Not to be shown to Filter
            // output BOM if needed
            processBOM();          
            
            setURCharRef();
            const DOMDocument *docu = (const DOMDocument*)nodeToWrite;
            //[23] XMLDecl      ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>'
            //[24] VersionInfo  ::= S 'version' Eq ("'" VersionNum "'" | '"' VersionNum '"')
            //[80] EncodingDecl ::= S 'encoding' Eq ('"' EncName '"' | "'" EncName
            //[32] SDDecl       ::= S 'standalone' Eq (("'" ('yes' | 'no') "'") | ('"' ('yes' | 'no') '"'))
                const XMLCh* versionNo = (docu->getVersion()) ? docu->getVersion() : gXMLDecl_ver10;
                *fFormatter << gXMLDecl_VersionInfo << versionNo << gXMLDecl_separator;
                // use the encoding resolved in initSession()
                *fFormatter << gXMLDecl_EncodingDecl << fEncodingUsed << gXMLDecl_separator;
                const XMLCh* st = (docu->getStandalone())? XMLUni::fgYesString : XMLUni::fgNoString;
                *fFormatter << gXMLDecl_SDDecl << st << gXMLDecl_separator;
                
                *fFormatter << gXMLDecl_endtag;
            }
Neil Graham
committed
            DOMNodeSPtr child = nodeToWrite->getFirstChild();
                processNode(child, level);
                child = child->getNextSibling();
            }
            printNewLine();
            break;
        }
    case DOMNode::DOCUMENT_FRAGMENT_NODE:
        {
           
            setURCharRef();
            DOMNode *child = nodeToWrite->getFirstChild();
            while( child != 0)
            {
                processNode(child, level);
                child = child->getNextSibling();
            }
    case DOMNode::ELEMENT_NODE:
            DOMNodeFilter::FilterAction filterAction = checkFilter(nodeToWrite);
            if ( filterAction == DOMNodeFilter::FILTER_REJECT)
                break;
            if (!lineFeedInTextNodePrinted)
            {
                if(level == 1)
                    printNewLine();
            }
            else
            {
                lineFeedInTextNodePrinted = false;
            }
            printIndent(level);
            //track the line number the current node begins on
            int nodeLine = fCurrentLine;
            // add an entry in the namespace stack
Alberto Massari
committed
            RefHashTableOf<XMLCh>* namespaceMap=NULL;
Gareth Reakes
committed
            if ( filterAction == DOMNodeFilter::FILTER_ACCEPT)
            {
                //           this element    attributes   child elements
                // skip          no              no            yes
                //
                TRY_CATCH_THROW
                (
                // The name has to be representable without any escapes
                    *fFormatter  << XMLFormatter::NoEscapes
                                 << chOpenAngle << nodeName;
                    ,true
                )
                // Output any attributes on this element
                setURCharRef();
                DOMNamedNodeMap *attributes = nodeToWrite->getAttributes();
                int attrCount = attributes->getLength();
Gareth Reakes
committed
                // 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])
                {
                    if(prefix==0 || prefix[0]==0)
                        prefix=XMLUni::fgZeroLenString;
                    bool bPrefixDeclared=false;
                    for(int i=fNamespaceStack->size()-1;i>=0;i--)
                    {
                        RefHashTableOf<XMLCh>* curNamespaceMap=fNamespaceStack->elementAt(i);
                        const XMLCh* thisUri=curNamespaceMap->get((void*)prefix);
Alberto Massari
committed
                        if(thisUri)
Gareth Reakes
committed
                        {
Alberto Massari
committed
                            // the prefix has been declared: check if it binds to the correct namespace, otherwise, redeclare it
                            if(XMLString::equals(thisUri,nodeToWrite->getNamespaceURI()))
                                bPrefixDeclared=true;
Gareth Reakes
committed
                            break;
                        }
                    }
                    if(!bPrefixDeclared)
                    {
Alberto Massari
committed
                        if(namespaceMap==NULL)
                        {
                            namespaceMap=new (fMemoryManager) RefHashTableOf<XMLCh>(12, false, fMemoryManager);
                            fNamespaceStack->addElement(namespaceMap);
                        }
Gareth Reakes
committed
                        namespaceMap->put((void*)prefix,(XMLCh*)nodeToWrite->getNamespaceURI());
                        *fFormatter  << XMLFormatter::NoEscapes
                                     << chSpace << XMLUni::fgXMLNSString;
Gareth Reakes
committed
                        if(!XMLString::equals(prefix,XMLUni::fgZeroLenString))
                            *fFormatter  << chColon << prefix;
                        *fFormatter  << chEqual << chDoubleQuote
                                     << XMLFormatter::AttrEscapes
                                     << nodeToWrite->getNamespaceURI()
                                     << XMLFormatter::NoEscapes
                                     << chDoubleQuote;
                    }
                }
                bool discard = getFeature(DISCARD_DEFAULT_CONTENT_ID);
                for (int i = 0; i < attrCount; i++)
                {
                    DOMAttrSPtr  attribute = (DOMAttr*)attributes->item(i);
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
                    // 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.
                    //
Gareth Reakes
committed
                    // 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)) 
Gareth Reakes
committed
                        {
Alberto Massari
committed
                            if(namespaceMap==NULL)
                            {
                                namespaceMap=new (fMemoryManager) RefHashTableOf<XMLCh>(12, false, fMemoryManager);
                                fNamespaceStack->addElement(namespaceMap);
                            }
Gareth Reakes
committed
			                const XMLCh* nsPrefix = attribute->getLocalName();
                            if(XMLString::equals(attribute->getNodeName(),XMLUni::fgXMLNSString))
Gareth Reakes
committed
								nsPrefix = XMLUni::fgZeroLenString;
							if(namespaceMap->containsKey((void*)nsPrefix))
								continue;
Gareth Reakes
committed
                            namespaceMap->put((void*)attribute->getLocalName(),(XMLCh*)attribute->getNodeValue());
                        }
                        else if(!XMLString::equals(ns, XMLUni::fgXMLURIName)) 
Gareth Reakes
committed
                        {
                            // check if the namespace for the current node is already defined
                            const XMLCh* prefix = attribute->getPrefix();
                            if(prefix && prefix[0])
                            {
                                bool bPrefixDeclared=false;
                                for(int i=fNamespaceStack->size()-1;i>=0;i--)
                                {
                                    RefHashTableOf<XMLCh>* curNamespaceMap=fNamespaceStack->elementAt(i);
                                    const XMLCh* thisUri=curNamespaceMap->get((void*)prefix);
Alberto Massari
committed
                                    if(thisUri)
Gareth Reakes
committed
                                    {
Alberto Massari
committed
                                        // the prefix has been declared: check if it binds to the correct namespace, otherwise, redeclare it
                                        if(XMLString::equals(thisUri,attribute->getNamespaceURI()))
                                            bPrefixDeclared=true;
Gareth Reakes
committed
                                        break;
                                    }
                                }
                                if(!bPrefixDeclared)
                                {
Alberto Massari
committed
                                    if(namespaceMap==NULL)
                                    {
                                        namespaceMap=new (fMemoryManager) RefHashTableOf<XMLCh>(12, false, fMemoryManager);
                                        fNamespaceStack->addElement(namespaceMap);
                                    }
Gareth Reakes
committed
                                    namespaceMap->put((void*)prefix,(XMLCh*)attribute->getNamespaceURI());
                                    *fFormatter  << XMLFormatter::NoEscapes
                                                 << chSpace << XMLUni::fgXMLNSString << chColon << prefix