/* * 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: DOMWriterImpl.cpp 231324 2005-08-10 20:58:56Z cargilld $ */ #include "DOMWriterImpl.hpp" #include "DOMErrorImpl.hpp" #include "DOMLocatorImpl.hpp" #include "DOMImplementationImpl.hpp" #include #include #include #include #include #include #include #include #include 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; static const int XML_DECLARATION = 0x9; // 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 // // // Each feature has 2 entries in this array, // 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 gEndPI[] = { chQuestion, chCloseAngle, chNull }; // static const XMLCh gXMLDecl_endtag[] = { chQuestion, chCloseAngle, 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 gEndComment[] = { chDash, chDash, chCloseAngle, chNull }; //setUnRepFlags(XMLFormatter::UnRep_Fail); \ try \ { \ action; \ } \ catch(TranscodingException const &e) \ { \ if ( !reportError(nodeToWrite \ , DOMError::DOM_SEVERITY_FATAL_ERROR \ , e.getMessage()) || \ forceToRethrow) \ throw e; \ } DOMWriterImpl::~DOMWriterImpl() { fMemoryManager->deallocate(fEncoding);//delete [] fEncoding; fMemoryManager->deallocate(fNewLine);//delete [] fNewLine; delete fNamespaceStack; // we don't own/adopt error handler and filter } DOMWriterImpl::DOMWriterImpl(MemoryManager* const manager) :fFeatures(0) ,fEncoding(0) ,fNewLine(0) ,fErrorHandler(0) ,fFilter(0) ,fDocumentVersion(XMLUni::fgVersion1_0) ,fEncodingUsed(0) ,fNewLineUsed(0) ,fFormatter(0) ,fErrorCount(0) ,fCurrentLine(0) ,fNamespaceStack(0) ,fMemoryManager(manager) { fNamespaceStack=new (fMemoryManager) RefVectorOf< RefHashTableOf >(0,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 ); } bool DOMWriterImpl::canSetFeature(const XMLCh* const featName , bool state) const { int featureId = INVALID_FEATURE_ID; return checkFeature(featName, false, featureId) ? canSetFeature(featureId, state) : false; } void DOMWriterImpl::setFeature(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); return; } bool DOMWriterImpl::getFeature(const XMLCh* const featName) const { int featureId = INVALID_FEATURE_ID; checkFeature(featName, true, featureId); return getFeature(featureId); } // we don't check the validity of the encoding set void DOMWriterImpl::setEncoding(const XMLCh* const encoding) { fMemoryManager->deallocate(fEncoding);//delete [] fEncoding; fEncoding = XMLString::replicate(encoding, fMemoryManager); } const XMLCh* DOMWriterImpl::getEncoding() const { return fEncoding; } void DOMWriterImpl::setNewLine(const XMLCh* const newLine) { fMemoryManager->deallocate(fNewLine);//delete [] fNewLine; fNewLine = XMLString::replicate(newLine, fMemoryManager); } const XMLCh* DOMWriterImpl::getNewLine() const { return fNewLine; } void DOMWriterImpl::setErrorHandler(DOMErrorHandler *errorHandler) { fErrorHandler = errorHandler; } DOMErrorHandler* DOMWriterImpl::getErrorHandler() const { return fErrorHandler; } void DOMWriterImpl::setFilter(DOMWriterFilter *filter) { fFilter = filter; } DOMWriterFilter* DOMWriterImpl::getFilter() const { return fFilter; } // // // bool DOMWriterImpl::writeNode(XMLFormatTarget* const destination , const DOMNode &nodeToWrite) { //init session vars initSession(&nodeToWrite); try { fFormatter = new (fMemoryManager) XMLFormatter(fEncodingUsed ,fDocumentVersion ,destination ,XMLFormatter::NoEscapes ,XMLFormatter::UnRep_CharRef ,fMemoryManager); } catch (const TranscodingException& e) { reportError(&nodeToWrite, DOMError::DOM_SEVERITY_FATAL_ERROR, e.getMessage()); return false; } try { Janitor janName(fFormatter); processNode(&nodeToWrite); destination->flush(); } // // 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&) { destination->flush(); return false; } catch (const XMLDOMMsg::Codes) { destination->flush(); return false; } catch(const OutOfMemoryException&) { throw; } catch (...) { destination->flush(); 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); } // // We don't throw DOMSTRING_SIZE_ERR since we are no longer // using DOMString. // XMLCh* DOMWriterImpl::writeToString(const DOMNode &nodeToWrite) { MemBufFormatTarget destination(1023, fMemoryManager); bool retVal; // XMLCh is unicode, assume fEncoding as UTF-16 XMLCh* tempEncoding = fEncoding; fEncoding = (XMLCh*) XMLUni::fgUTF16EncodingString; try { retVal = writeNode(&destination, nodeToWrite); } catch(const OutOfMemoryException&) { throw; } catch (...) { // // there is a possibility that memeory allocation // exception thrown in XMLBuffer class // fEncoding = tempEncoding; return 0; } fEncoding = tempEncoding; return (retVal ? XMLString::replicate((XMLCh*) destination.getRawBuffer(), fMemoryManager) : 0); } void DOMWriterImpl::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 * "UTF-8" will be used. */ fEncodingUsed = gUTF8; if (fEncoding && *fEncoding) { fEncodingUsed = fEncoding; } else { const DOMDocument *docu = (nodeToWrite->getNodeType() == DOMNode::DOCUMENT_NODE)? (const DOMDocument*)nodeToWrite : nodeToWrite->getOwnerDocument(); if (docu) { const XMLCh* tmpEncoding = docu->getEncoding(); if ( tmpEncoding && *tmpEncoding) { fEncodingUsed = tmpEncoding; } else { 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 " // // 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 DOMWriterImpl::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; if (getFeature(FORMAT_PRETTY_PRINT_ID)) { lineFeedInTextNodePrinted = false; lastWhiteSpaceInTextNode = 0; if(XMLString::isAllWhiteSpace(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) { 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 ) 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 ::= '' //[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)) { 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; } 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 (!lineFeedInTextNodePrinted) { if(level == 1) printNewLine(); 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 RefHashTableOf* 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; ,true ) // Output any attributes on this element setURCharRef(); DOMNamedNodeMap *attributes = nodeToWrite->getAttributes(); int 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]) { if(prefix==0 || prefix[0]==0) prefix=XMLUni::fgZeroLenString; bool bPrefixDeclared=false; for(int i=fNamespaceStack->size()-1;i>=0;i--) { RefHashTableOf* curNamespaceMap=fNamespaceStack->elementAt(i); const XMLCh* thisUri=curNamespaceMap->get((void*)prefix); if(thisUri) { // the prefix has been declared: check if it binds to the correct namespace, otherwise, redeclare it if(XMLString::equals(thisUri,nodeToWrite->getNamespaceURI())) bPrefixDeclared=true; break; } } if(!bPrefixDeclared) { if(namespaceMap==NULL) { namespaceMap=new (fMemoryManager) RefHashTableOf(12, false, fMemoryManager); fNamespaceStack->addElement(namespaceMap); } namespaceMap->put((void*)prefix,(XMLCh*)nodeToWrite->getNamespaceURI()); *fFormatter << XMLFormatter::NoEscapes << chSpace << XMLUni::fgXMLNSString; 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); // 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(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]) { bool bPrefixDeclared=false; for(int i=fNamespaceStack->size()-1;i>=0;i--) { RefHashTableOf* curNamespaceMap=fNamespaceStack->elementAt(i); const XMLCh* thisUri=curNamespaceMap->get((void*)prefix); if(thisUri) { // the prefix has been declared: check if it binds to the correct namespace, otherwise, redeclare it if(XMLString::equals(thisUri,attribute->getNamespaceURI())) bPrefixDeclared=true; break; } } if(!bPrefixDeclared) { if(namespaceMap==NULL) { namespaceMap=new (fMemoryManager) RefHashTableOf(12, false, fMemoryManager); fNamespaceStack->addElement(namespaceMap); } namespaceMap->put((void*)prefix,(XMLCh*)attribute->getNamespaceURI()); *fFormatter << XMLFormatter::NoEscapes << chSpace << XMLUni::fgXMLNSString << chColon << prefix << chEqual << chDoubleQuote << XMLFormatter::AttrEscapes << attribute->getNamespaceURI() << XMLFormatter::NoEscapes << chDoubleQuote; } } } } *fFormatter << XMLFormatter::NoEscapes << chSpace << attribute->getNodeName() << chEqual << chDoubleQuote << XMLFormatter::AttrEscapes << attribute->getNodeValue() << 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 (!lineFeedInTextNodePrinted) { printNewLine(); } else { lineFeedInTextNodePrinted = false; } if(nodeLine != fCurrentLine && level == 0) printNewLine(); printIndent(level); } TRY_CATCH_THROW ( *fFormatter << XMLFormatter::NoEscapes << gEndElement << nodeName << chCloseAngle; ,true ) } } 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; , true ) } } // 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() << chEqual << chDoubleQuote << XMLFormatter::AttrEscapes << nodeToWrite->getNodeValue() << XMLFormatter::NoEscapes << chDoubleQuote; } else { *fFormatter << XMLFormatter::NoEscapes << chOpenCurly << nodeToWrite->getNamespaceURI() << chCloseCurly << localName << chEqual << chDoubleQuote << XMLFormatter::AttrEscapes << nodeToWrite->getNodeValue() << 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; , true ) } 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< unrep-char // =============================================================== // true split split // false fails fails // case DOMNode::CDATA_SECTION_NODE: { if (checkFilter(nodeToWrite) != DOMNodeFilter::FILTER_ACCEPT) break; if(level == 1) printNewLine(); printNewLine(); printIndent(level); if (getFeature(SPLIT_CDATA_SECTIONS_ID)) { // it is fairly complicated and we process this // in a separate function. procCdataSection(nodeValue, nodeToWrite, level); } else { // search for "]]>", the node value is not supposed to have this if (XMLString::patternMatch((XMLCh* const) 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; , true ) } break; } case DOMNode::COMMENT_NODE: { if (checkFilter(nodeToWrite) != DOMNodeFilter::FILTER_ACCEPT) break; if(level == 1) printNewLine(); printNewLine(); printIndent(level); TRY_CATCH_THROW ( *fFormatter << XMLFormatter::NoEscapes << gStartComment << nodeValue << gEndComment; , true ) 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; , true ) // 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; } } bool DOMWriterImpl::customNodeSerialize(const DOMNode* const, int) { return false; } // // DOMNodeFilter::FilterAction DOMWriterImpl::checkFilter(const DOMNode* const node) const { if (!fFilter || ((fFilter->getWhatToShow() & (1 << (node->getNodeType() - 1))) == 0)) return DOMNodeFilter::FILTER_ACCEPT; // // if and only if there is a filter, and it is interested // in the node type, then we pass the node to the filter // for examination // return (DOMNodeFilter::FilterAction) fFilter->acceptNode(node); } bool DOMWriterImpl::checkFeature(const XMLCh* const featName , bool toThrow , int& featureId) const { // check for null and/or empty feature name if (!featName || !*featName) { if (toThrow) throw DOMException(DOMException::NOT_FOUND_ERR, 0, fMemoryManager); return false; } featureId = INVALID_FEATURE_ID; if (XMLString::equals(featName, XMLUni::fgDOMWRTCanonicalForm)) featureId = CANONICAL_FORM_ID; else if (XMLString::equals(featName, XMLUni::fgDOMWRTDiscardDefaultContent)) featureId = DISCARD_DEFAULT_CONTENT_ID; else if (XMLString::equals(featName, XMLUni::fgDOMWRTEntities)) featureId = ENTITIES_ID; else if (XMLString::equals(featName, XMLUni::fgDOMWRTFormatPrettyPrint)) featureId = FORMAT_PRETTY_PRINT_ID; else if (XMLString::equals(featName, XMLUni::fgDOMWRTNormalizeCharacters)) featureId = NORMALIZE_CHARACTERS_ID; else if (XMLString::equals(featName, XMLUni::fgDOMWRTSplitCdataSections)) featureId = SPLIT_CDATA_SECTIONS_ID; else if (XMLString::equals(featName, XMLUni::fgDOMWRTValidation)) featureId = VALIDATION_ID; else if (XMLString::equals(featName, XMLUni::fgDOMWRTWhitespaceInElementContent)) featureId = WHITESPACE_IN_ELEMENT_CONTENT_ID; else if (XMLString::equals(featName, XMLUni::fgDOMWRTBOM)) featureId = BYTE_ORDER_MARK_ID; else if (XMLString::equals(featName, XMLUni::fgDOMXMLDeclaration)) featureId = XML_DECLARATION; //feature name not resolvable if (featureId == INVALID_FEATURE_ID) { if (toThrow) throw DOMException(DOMException::NOT_FOUND_ERR, featName, fMemoryManager); return false; } return true; } bool DOMWriterImpl::reportError(const DOMNode* const errorNode , DOMError::ErrorSeverity errorType , const XMLCh* const errorMsg) { bool toContinueProcess = true; // default value for no error handler if (fErrorHandler) { DOMLocatorImpl locator(0, 0, (DOMNode* const) errorNode, 0, 0); DOMErrorImpl domError(errorType , errorMsg, &locator); toContinueProcess = fErrorHandler->handleError(domError); } if (errorType != DOMError::DOM_SEVERITY_WARNING) fErrorCount++; return toContinueProcess; } bool DOMWriterImpl::reportError(const DOMNode* const errorNode , DOMError::ErrorSeverity errorType , XMLDOMMsg::Codes toEmit) { const unsigned int msgSize = 1023; XMLCh errText[msgSize + 1]; DOMImplementationImpl::getMsgLoader4DOM()->loadMsg(toEmit, errText, msgSize); bool toContinueProcess = true; // default value for no error handler if (fErrorHandler) { DOMLocatorImpl locator(0, 0, (DOMNode* const) errorNode, 0, 0); DOMErrorImpl domError(errorType , errText, &locator); toContinueProcess = fErrorHandler->handleError(domError); } if (errorType != DOMError::DOM_SEVERITY_WARNING) fErrorCount++; if (errorType == DOMError::DOM_SEVERITY_FATAL_ERROR || !toContinueProcess) throw toEmit; return toContinueProcess; } // // // void DOMWriterImpl::procCdataSection(const XMLCh* const nodeValue , const DOMNode* const nodeToWrite , int level) { /*** * Append a ']]>' at the end */ int len = XMLString::stringLen(nodeValue); XMLCh* repNodeValue = (XMLCh*) fMemoryManager->allocate ( (len + offset + 1) * sizeof(XMLCh) );//new XMLCh [len + offset + 1]; XMLString::copyString(repNodeValue, nodeValue); XMLString::catString(repNodeValue, gEndCDATA); ArrayJanitor jName(repNodeValue, fMemoryManager); XMLCh* curPtr = (XMLCh*) repNodeValue; XMLCh* nextPtr = 0; int endTagPos = -1; bool endTagFound = true; while (endTagFound) { endTagPos = XMLString::patternMatch(curPtr, gEndCDATA); if (endTagPos != -1) { nextPtr = curPtr + endTagPos + offset; // skip the ']]>' *(curPtr + endTagPos) = chNull; //nullify the first ']' if (endTagPos != len) reportError(nodeToWrite, DOMError::DOM_SEVERITY_WARNING, XMLDOMMsg::Writer_NestedCDATA); len = len - endTagPos - offset; } else { endTagFound = false; } /*** to check ]]>]]> ***/ if (endTagPos == 0) { printNewLine(); printIndent(level); TRY_CATCH_THROW ( *fFormatter << XMLFormatter::NoEscapes << gStartCDATA << gEndCDATA; , true ) } else { procUnrepCharInCdataSection(curPtr, nodeToWrite, level); } if (endTagFound) { *(nextPtr - offset) = chCloseSquare; //restore the first ']' curPtr = nextPtr; } } return; } // // // void DOMWriterImpl::procUnrepCharInCdataSection(const XMLCh* const nodeValue , const DOMNode* const nodeToWrite , int level) { // // We have to check each character and see if it could be represented. // As long as it can, we just keep up with where we started and how // many chars we've checked. When we hit an unrepresentable one, we // stop, transcode everything we've collected, then start handling // the unrepresentables via char refs. We repeat this until we get all // the chars done. // const XMLCh* srcPtr = nodeValue; const XMLCh* endPtr = nodeValue + XMLString::stringLen(nodeValue); // Set up the common part of the buffer that we build char refs into XMLCh tmpBuf[32]; tmpBuf[0] = chAmpersand; tmpBuf[1] = chPound; tmpBuf[2] = chLatin_x; while (srcPtr < endPtr) { const XMLCh* tmpPtr = srcPtr; while (tmpPtr < endPtr) { if (fFormatter->getTranscoder()->canTranscodeTo(*tmpPtr)) tmpPtr++; else break; } if (tmpPtr > srcPtr) { printNewLine(); printIndent(level); TRY_CATCH_THROW ( *fFormatter << XMLFormatter::NoEscapes << gStartCDATA; , true ) // We got at least some chars that can be done normally fFormatter->formatBuf ( srcPtr , tmpPtr - srcPtr , XMLFormatter::NoEscapes , XMLFormatter::UnRep_Fail ); TRY_CATCH_THROW ( *fFormatter << XMLFormatter::NoEscapes << gEndCDATA; , true ) // Update the source pointer to our new spot srcPtr = tmpPtr; } else { // // We hit something unrepresentable. So continue forward doing // char refs until we hit something representable again or the // end of input. // // one warning for consective unrep chars reportError(nodeToWrite, DOMError::DOM_SEVERITY_WARNING, XMLDOMMsg::Writer_NotRepresentChar); while (srcPtr < endPtr) { // Build a char ref for the current char XMLString::binToText(*srcPtr, &tmpBuf[3], 8, 16, fMemoryManager); const unsigned int bufLen = XMLString::stringLen(tmpBuf); tmpBuf[bufLen] = chSemiColon; tmpBuf[bufLen+1] = chNull; // And now call recursively back to our caller to format this fFormatter->formatBuf ( tmpBuf , bufLen + 1 , XMLFormatter::NoEscapes , XMLFormatter::UnRep_Fail ); // Move up the source pointer and break out if needed srcPtr++; if (fFormatter->getTranscoder()->canTranscodeTo(*srcPtr)) break; } } } } void DOMWriterImpl::processNode(const DOMNode* const nodeToWrite) { processNode(nodeToWrite, 0); } bool DOMWriterImpl::canSetFeature(const int featureId , bool val) const { return featuresSupported[2*featureId + (val? 0: 1)]; } void DOMWriterImpl::printNewLine() { if (getFeature(FORMAT_PRETTY_PRINT_ID)) { fCurrentLine++; *fFormatter << fNewLineUsed; } } void DOMWriterImpl::printIndent(int level) const { if (getFeature(FORMAT_PRETTY_PRINT_ID)) { if (lastWhiteSpaceInTextNode) { level -= lastWhiteSpaceInTextNode/2; // two chSpaces equals one indent level lastWhiteSpaceInTextNode = 0; // if lastWhiteSpaceInTextNode/2 is greater than level, then // it means too many spaces have been written to the // output stream and we can no longer indent properly } for(int i = 0; i < level; i++) *fFormatter << chSpace << chSpace; } } void DOMWriterImpl::release() { DOMWriterImpl* writer = (DOMWriterImpl*) this; delete writer; } void DOMWriterImpl::processBOM() { // if the feature is not set, don't output bom if (!getFeature(BYTE_ORDER_MARK_ID)) return; if ((XMLString::compareIStringASCII(fEncoding, XMLUni::fgUTF16LEncodingString) == 0) || (XMLString::compareIStringASCII(fEncoding, XMLUni::fgUTF16LEncodingString2) == 0) ) { fFormatter->writeBOM(BOM_utf16le, 2); } else if ((XMLString::compareIStringASCII(fEncoding, XMLUni::fgUTF16BEncodingString) == 0) || (XMLString::compareIStringASCII(fEncoding, XMLUni::fgUTF16BEncodingString2) == 0) ) { fFormatter->writeBOM(BOM_utf16be, 2); } else if ((XMLString::compareIStringASCII(fEncoding, XMLUni::fgUTF16EncodingString) == 0) || (XMLString::compareIStringASCII(fEncoding, XMLUni::fgUTF16EncodingString2) == 0) || (XMLString::compareIStringASCII(fEncoding, XMLUni::fgUTF16EncodingString3) == 0) || (XMLString::compareIStringASCII(fEncoding, XMLUni::fgUTF16EncodingString4) == 0) || (XMLString::compareIStringASCII(fEncoding, XMLUni::fgUTF16EncodingString5) == 0) || (XMLString::compareIStringASCII(fEncoding, XMLUni::fgUTF16EncodingString6) == 0) || (XMLString::compareIStringASCII(fEncoding, XMLUni::fgUTF16EncodingString7) == 0) ) { #if defined(ENDIANMODE_LITTLE) fFormatter->writeBOM(BOM_utf16le, 2); #elif defined(ENDIANMODE_BIG) fFormatter->writeBOM(BOM_utf16be, 2); #endif } else if ((XMLString::compareIStringASCII(fEncoding, XMLUni::fgUCS4LEncodingString) == 0) || (XMLString::compareIStringASCII(fEncoding, XMLUni::fgUCS4LEncodingString2) == 0) ) { fFormatter->writeBOM(BOM_ucs4le, 4); } else if ((XMLString::compareIStringASCII(fEncoding, XMLUni::fgUCS4BEncodingString) == 0) || (XMLString::compareIStringASCII(fEncoding, XMLUni::fgUCS4BEncodingString2) == 0) ) { fFormatter->writeBOM(BOM_ucs4be, 4); } else if ((XMLString::compareIStringASCII(fEncoding, XMLUni::fgUCS4EncodingString) == 0) || (XMLString::compareIStringASCII(fEncoding, XMLUni::fgUCS4EncodingString2) == 0) || (XMLString::compareIStringASCII(fEncoding, XMLUni::fgUCS4EncodingString3) == 0) ) { #if defined(ENDIANMODE_LITTLE) fFormatter->writeBOM(BOM_ucs4le, 4); #elif defined(ENDIANMODE_BIG) fFormatter->writeBOM(BOM_ucs4be, 4); #endif } return; } XERCES_CPP_NAMESPACE_END