in src/xercesc/dom/impl/DOMLSSerializerImpl.cpp [631:1315]
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())
{
case DOMNode::TEXT_NODE:
{
if (checkFilter(nodeToWrite) != DOMNodeFilter::FILTER_ACCEPT)
break;
ensureValidString(nodeToWrite, nodeValue);
if (getFeature(FORMAT_PRETTY_PRINT_ID))
{
fLineFeedInTextNodePrinted = false;
fLastWhiteSpaceInTextNode = 0;
if(XMLChar1_0::isAllSpaces(nodeValue, XMLString::stringLen(nodeValue)))
{
// 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)
{
fLineFeedInTextNodePrinted = true;
fLastWhiteSpaceInTextNode = (unsigned int)(lent - pos);
}
else
{
// for those platforms using chCR alone as
// a newline character
pos = XMLString::lastIndexOf(nodeValue, chCR);
if (-1 != pos)
{
fLineFeedInTextNodePrinted = true;
fLastWhiteSpaceInTextNode = (unsigned int)(lent - pos);
}
}
}
}
}
setURCharRef(); // character data
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);
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;
)
break;
}
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') '"'))
//
if (getFeature(XML_DECLARATION)) {
// use the version and encoding resolved
*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();
while( child != 0)
{
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();
}
printNewLine();
break;
}
case DOMNode::ELEMENT_NODE:
{
DOMNodeFilter::FilterAction filterAction = checkFilter(nodeToWrite);
if ( filterAction == DOMNodeFilter::FILTER_REJECT)
break;
if (!fLineFeedInTextNodePrinted)
{
if(level == 1 && getFeature(FORMAT_PRETTY_PRINT_1ST_LEVEL_ID))
printNewLine();
printNewLine();
}
else
{
fLineFeedInTextNodePrinted = false;
}
printIndent(level);
//track the line number the current node begins on
int nodeLine = fCurrentLine;
// add an entry in the namespace stack
RefHashTableOf<XMLCh>* namespaceMap=NULL;
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();
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);
*fFormatter << XMLFormatter::NoEscapes
<< chSpace << XMLUni::fgXMLNSString;
if(!XMLString::equals(prefix,XMLUni::fgZeroLenString))
*fFormatter << chColon << prefix;
*fFormatter << chEqual << chDoubleQuote
<< XMLFormatter::AttrEscapes
<< uri
<< XMLFormatter::NoEscapes
<< chDoubleQuote;
}
}
bool discard = getFeature(DISCARD_DEFAULT_CONTENT_ID);
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);
*fFormatter << XMLFormatter::NoEscapes
<< chSpace << XMLUni::fgXMLNSString << chColon << prefix
<< chEqual << chDoubleQuote
<< XMLFormatter::AttrEscapes
<< uri
<< XMLFormatter::NoEscapes
<< chDoubleQuote;
}
}
}
}
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());
*fFormatter << 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();
}
*fFormatter << XMLFormatter::NoEscapes
<< chDoubleQuote;
}
} // end of for
} // end of FILTER_ACCEPT
level++;
// FILTER_SKIP may start from here
//
// 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;
while( child != 0)
{
processNode(child, level);
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)
{
if (!fLineFeedInTextNodePrinted)
{
printNewLine();
}
else
{
fLineFeedInTextNodePrinted = false;
}
if(nodeLine != fCurrentLine && level == 0 && getFeature(FORMAT_PRETTY_PRINT_1ST_LEVEL_ID))
printNewLine();
printIndent(level);
}
TRY_CATCH_THROW
(
*fFormatter << XMLFormatter::NoEscapes << gEndElement
<< nodeName << chCloseAngle;
)
}
}
else
{
level--;
//
// There were no children. Output the short form close of
// the element start tag, making it an empty-element tag.
//
if (filterAction == DOMNodeFilter::FILTER_ACCEPT)
{
TRY_CATCH_THROW
(
*fFormatter << XMLFormatter::NoEscapes << chForwardSlash << chCloseAngle;
)
}
}
// remove the namespace map at this level
if(namespaceMap!=NULL)
fNamespaceStack->removeLastElement();
break;
}
case DOMNode::ATTRIBUTE_NODE:
{
if (checkFilter(nodeToWrite) != DOMNodeFilter::FILTER_ACCEPT)
break;
const XMLCh* localName = nodeToWrite->getLocalName();
// check if this is a DOM Level 1 Node
if(localName == 0)
*fFormatter << XMLFormatter::NoEscapes
<< nodeToWrite->getNodeName();
else
*fFormatter << XMLFormatter::NoEscapes
<< chOpenCurly << nodeToWrite->getNamespaceURI()
<< chCloseCurly << localName;
*fFormatter << chEqual << chDoubleQuote
<< XMLFormatter::AttrEscapes;
if (getFeature(ENTITIES_ID))
{
DOMNodeSPtr child = nodeToWrite->getFirstChild();
while( child != 0)
{
if(child->getNodeType()==DOMNode::TEXT_NODE)
{
ensureValidString(nodeToWrite, child->getNodeValue());
*fFormatter << child->getNodeValue();
}
else if(child->getNodeType()==DOMNode::ENTITY_REFERENCE_NODE)
*fFormatter << XMLFormatter::NoEscapes
<< chAmpersand << child->getNodeName() << chSemiColon
<< XMLFormatter::AttrEscapes;
child = child->getNextSibling();
}
}
else
{
ensureValidString(nodeToWrite, nodeValue);
*fFormatter << nodeValue;
}
*fFormatter << XMLFormatter::NoEscapes
<< chDoubleQuote;
break;
}
case DOMNode::ENTITY_REFERENCE_NODE:
{
//"entities"
//true
//[required] (default)
//Keep EntityReference and Entity nodes in the document.
//false
//[optional]
//Remove all EntityReference and Entity nodes from the document,
// putting the entity expansions directly in their place.
// Text nodes are into "normal" form.
//Only EntityReference nodes to non-defined entities are kept in the document.
if (checkFilter(nodeToWrite) != DOMNodeFilter::FILTER_ACCEPT)
break;
if (getFeature(ENTITIES_ID))
{
TRY_CATCH_THROW
(
*fFormatter << XMLFormatter::NoEscapes << chAmpersand
<< nodeName << chSemiColon;
)
}
else
{
// check if the referenced entity is defined or not
if (nodeToWrite->getOwnerDocument()->getDoctype()->getEntities()->getNamedItem(nodeName))
{
DOMNodeSPtr child;
for (child = nodeToWrite->getFirstChild();
child != 0;
child = child->getNextSibling())
{
processNode(child, level);
}
}
else
{
TRY_CATCH_THROW
(
*fFormatter<<XMLFormatter::NoEscapes<<chAmpersand<<nodeName<<chSemiColon;
)
}
}
break;
}
//
// feature:split_cdata_sections occurence of ]]> unrep-char
// ===============================================================
// true split split
// false fails fails
//
case DOMNode::CDATA_SECTION_NODE:
{
if (checkFilter(nodeToWrite) != DOMNodeFilter::FILTER_ACCEPT)
break;
if (getFeature(SPLIT_CDATA_SECTIONS_ID))
{
// it is fairly complicated and we process this
// in a separate function.
procCdataSection(nodeValue, nodeToWrite);
}
else
{
ensureValidString(nodeToWrite, nodeValue);
// search for "]]>", the node value is not supposed to have this
if (XMLString::patternMatch(nodeValue, gEndCDATA) != -1)
{
reportError(nodeToWrite, DOMError::DOM_SEVERITY_FATAL_ERROR, XMLDOMMsg::Writer_NestedCDATA);
}
TRY_CATCH_THROW
(
// transcoder throws exception for unrep chars
*fFormatter << XMLFormatter::NoEscapes << gStartCDATA << nodeValue << gEndCDATA;
)
}
break;
}
case DOMNode::COMMENT_NODE:
{
if (checkFilter(nodeToWrite) != DOMNodeFilter::FILTER_ACCEPT)
break;
ensureValidString(nodeToWrite, nodeValue);
// Figure out if we want pretty-printing for this comment.
// If this comment node does not have any element siblings
// (i.e., it is a text node) then we don't want to add any
// whitespaces since that might be significant to the
// application. Otherwise we want pretty-printing.
//
bool pretty = (level == 0); // Document-level comments.
if (!pretty)
{
// See if we have any element siblings.
//
const DOMNode* s = nodeToWrite->getNextSibling ();
while (s != 0 && s->getNodeType () != DOMNode::ELEMENT_NODE)
s = s->getNextSibling ();
if (s != 0)
pretty = true;
else
{
s = nodeToWrite->getPreviousSibling ();
while (s != 0 && s->getNodeType () != DOMNode::ELEMENT_NODE)
s = s->getPreviousSibling ();
if (s != 0)
pretty = true;
}
}
if (pretty)
{
if(level == 1 && getFeature(FORMAT_PRETTY_PRINT_1ST_LEVEL_ID))
printNewLine();
printNewLine();
printIndent(level);
}
TRY_CATCH_THROW
(
*fFormatter << XMLFormatter::NoEscapes << gStartComment
<< nodeValue << gEndComment;
)
break;
}
case DOMNode::DOCUMENT_TYPE_NODE: // Not to be shown to Filter
{
const DOMDocumentType *doctype = (const DOMDocumentType *)nodeToWrite;
fFormatter->setEscapeFlags(XMLFormatter::NoEscapes);
printNewLine();
printIndent(level);
TRY_CATCH_THROW
(
*fFormatter << gStartDoctype << nodeName;
const XMLCh *id = doctype->getPublicId();
if (id && *id)
{
*fFormatter << chSpace << gPublic << id << chDoubleQuote;
id = doctype->getSystemId();
if (id && *id)
{
*fFormatter << chSpace << chDoubleQuote << id << chDoubleQuote;
}
else
{
//
// 4.2.2 External Entities
// [Definition: If the entity is not internal,
// it is an external entity, declared as follows:]
// External Entity Declaration
// [75] ExternalID ::= 'SYSTEM' S SystemLiteral
// | 'PUBLIC' S PubidLiteral S SystemLiteral
//
reportError(nodeToWrite, DOMError::DOM_SEVERITY_FATAL_ERROR, XMLDOMMsg::Writer_NotRecognizedType);
// systemLiteral not found
}
}
else
{
id = doctype->getSystemId();
if (id && *id)
{
*fFormatter << chSpace << gSystem << id << chDoubleQuote;
}
}
id = doctype->getInternalSubset();
if (id && *id)
{
*fFormatter << chSpace << chOpenSquare << id << chCloseSquare;
}
*fFormatter << chCloseAngle;
) // end of TRY_CATCH_THROW
break;
}
case DOMNode::ENTITY_NODE: // Not to be shown to Filter
{
//
// REVISIT: how does the feature "entities" impact
// entity node?
//
printNewLine();
printIndent(level);
fFormatter->setEscapeFlags(XMLFormatter::NoEscapes);
*fFormatter << gStartEntity << nodeName;
const XMLCh * id = ((const DOMEntity*)nodeToWrite)->getPublicId();
if (id)
*fFormatter << gPublic << id << chDoubleQuote;
id = ((const DOMEntity*)nodeToWrite)->getSystemId();
if (id)
*fFormatter << gSystem << id << chDoubleQuote;
id = ((const DOMEntity*)nodeToWrite)->getNotationName();
if (id)
*fFormatter << gNotation << id << chDoubleQuote;
*fFormatter << chCloseAngle;
break;
}
default:
/***
This is an implementation specific behaviour, we abort if a user derived class has not dealt with
this node type.
***/
{
if(!customNodeSerialize(nodeToWrite, level)) {
reportError(nodeToWrite, DOMError::DOM_SEVERITY_FATAL_ERROR, XMLDOMMsg::Writer_NotRecognizedType);
// UnreognizedNodeType;
}
}
break;
}
}