From f0afcfec405b303bc2703ecca4f627c6bd992258 Mon Sep 17 00:00:00 2001 From: cantor Date: Tue, 28 Feb 2006 04:39:39 +0000 Subject: [PATCH] DOM fixup changes git-svn-id: https://svn.middleware.georgetown.edu/cpp-xmltooling/trunk@33 de75baf8-a10c-0410-a50a-987c0e22f00f --- xmltooling/impl/UnknownElement.cpp | 59 +++++++++++++- xmltooling/impl/UnknownElement.h | 16 +++- xmltooling/io/AbstractXMLObjectMarshaller.cpp | 108 ++++++++++++++++++++------ xmltooling/io/AbstractXMLObjectMarshaller.h | 32 +++++++- xmltooling/io/Marshaller.h | 28 +++++-- xmltooling/io/Unmarshaller.h | 2 +- 6 files changed, 209 insertions(+), 36 deletions(-) diff --git a/xmltooling/impl/UnknownElement.cpp b/xmltooling/impl/UnknownElement.cpp index 0dfef21..9af4b26 100644 --- a/xmltooling/impl/UnknownElement.cpp +++ b/xmltooling/impl/UnknownElement.cpp @@ -103,7 +103,9 @@ DOMElement* UnknownElementMarshaller::marshall(XMLObject* xmlObject, DOMDocument DOMElement* cachedDOM=unk->getDOM(); if (cachedDOM) { if (!document || document==cachedDOM->getOwnerDocument()) { - log.debug("XMLObject has a usable cached DOM, using it"); + log.debug("XMLObject has a usable cached DOM, reusing it"); + setDocumentElement(cachedDOM->getOwnerDocument(),cachedDOM); + unk->releaseParentDOM(true); return cachedDOM; } @@ -121,7 +123,7 @@ DOMElement* UnknownElementMarshaller::marshall(XMLObject* xmlObject, DOMDocument log.debug("parsing XML back into DOM tree"); DOMDocument* internalDoc=XMLToolingInternalConfig::getInternalConfig().m_parserPool->parse(dsrc); if (document) { - // The caller insists on using his own document, so we now have to import the damn thing + // The caller insists on using his own document, so we now have to import the thing // into it. Then we're just dumping the one we built. log.debug("reimporting new DOM into caller-supplied document"); cachedDOM=static_cast(document->importNode(internalDoc->getDocumentElement(), true)); @@ -129,13 +131,64 @@ DOMElement* UnknownElementMarshaller::marshall(XMLObject* xmlObject, DOMDocument } else { // We just bind the document we built to the object as the result. + cachedDOM=static_cast(internalDoc->getDocumentElement()); document=internalDoc; bindDocument=true; } // Recache the DOM and clear the serialized copy. - log.debug("caching DOM for XMLObject"); + setDocumentElement(document, cachedDOM); + log.debug("caching DOM for XMLObject (document is %sbound)", bindDocument ? "" : "not "); unk->setDOM(cachedDOM, bindDocument); + unk->releaseParentDOM(true); + unk->m_xml.erase(); + return cachedDOM; +} + +DOMElement* UnknownElementMarshaller::marshall(XMLObject* xmlObject, DOMElement* parentElement) const +{ +#ifdef _DEBUG + xmltooling::NDC ndc("marshall"); +#endif + + Category& log=Category::getInstance(XMLTOOLING_LOGCAT".Marshaller"); + log.debug("marshalling unknown content"); + + UnknownElementImpl* unk=dynamic_cast(xmlObject); + if (!unk) + throw MarshallingException("Only objects of class UnknownElementImpl can be marshalled."); + + DOMElement* cachedDOM=unk->getDOM(); + if (cachedDOM) { + if (parentElement->getOwnerDocument()==cachedDOM->getOwnerDocument()) { + log.debug("XMLObject has a usable cached DOM, reusing it"); + parentElement->appendChild(cachedDOM); + unk->releaseParentDOM(true); + return cachedDOM; + } + + // We have a DOM but it doesn't match the document we were given. This both sucks and blows. + // Without an adoptNode option to maintain the child pointers, we rely on our custom + // implementation class to preserve the XML when we release the existing DOM. + unk->releaseDOM(); + } + + // If we get here, we didn't have a usable DOM (and/or we flushed the one we had). + // We need to reparse the XML we saved off into a new DOM. + MemBufInputSource src(reinterpret_cast(unk->m_xml.c_str()),unk->m_xml.length(),"UnknownElementImpl"); + Wrapper4InputSource dsrc(&src,false); + log.debug("parsing XML back into DOM tree"); + DOMDocument* internalDoc=XMLToolingInternalConfig::getInternalConfig().m_parserPool->parse(dsrc); + + log.debug("reimporting new DOM into caller-supplied document"); + cachedDOM=static_cast(parentElement->getOwnerDocument()->importNode(internalDoc->getDocumentElement(), true)); + internalDoc->release(); + + // Recache the DOM and clear the serialized copy. + parentElement->appendChild(cachedDOM); + log.debug("caching DOM for XMLObject"); + unk->setDOM(cachedDOM, false); + unk->releaseParentDOM(true); unk->m_xml.erase(); return cachedDOM; } diff --git a/xmltooling/impl/UnknownElement.h b/xmltooling/impl/UnknownElement.h index acab528..764e17d 100644 --- a/xmltooling/impl/UnknownElement.h +++ b/xmltooling/impl/UnknownElement.h @@ -112,9 +112,23 @@ namespace xmltooling { virtual ~UnknownElementMarshaller() {} /** - * @see Marshaller::marshall() + * @see Marshaller::marshall(XMLObject*,DOMDocument*) */ DOMElement* marshall(XMLObject* xmlObject, DOMDocument* document=NULL) const; + + /** + * @see Marshaller::marshall(XMLObject*,DOMElement*) + */ + DOMElement* marshall(XMLObject* xmlObject, DOMElement* parentElement) const; + + protected: + void setDocumentElement(DOMDocument* document, DOMElement* element) const { + DOMElement* documentRoot = document->getDocumentElement(); + if (documentRoot) + document->replaceChild(documentRoot, element); + else + document->appendChild(element); + } }; /** diff --git a/xmltooling/io/AbstractXMLObjectMarshaller.cpp b/xmltooling/io/AbstractXMLObjectMarshaller.cpp index 1457d4b..c66b770 100644 --- a/xmltooling/io/AbstractXMLObjectMarshaller.cpp +++ b/xmltooling/io/AbstractXMLObjectMarshaller.cpp @@ -45,7 +45,7 @@ AbstractXMLObjectMarshaller::AbstractXMLObjectMarshaller(const XMLCh* targetName if (!targetLocalName || !*targetLocalName) throw MarshallingException("targetLocalName cannot be null or empty"); } - + DOMElement* AbstractXMLObjectMarshaller::marshall(XMLObject* xmlObject, DOMDocument* document) const { #ifdef _DEBUG @@ -53,7 +53,7 @@ DOMElement* AbstractXMLObjectMarshaller::marshall(XMLObject* xmlObject, DOMDocum #endif if (XT_log.isDebugEnabled()) { - XT_log.debug("marshalling %s", xmlObject->getElementQName().toString().c_str()); + XT_log.debug("starting to marshalling %s", xmlObject->getElementQName().toString().c_str()); } DOMCachingXMLObject* dc=dynamic_cast(xmlObject); @@ -61,7 +61,9 @@ DOMElement* AbstractXMLObjectMarshaller::marshall(XMLObject* xmlObject, DOMDocum DOMElement* cachedDOM=dc->getDOM(); if (cachedDOM) { if (!document || document==cachedDOM->getOwnerDocument()) { - XT_log.debug("XMLObject has a usable cached DOM, using it."); + XT_log.debug("XMLObject has a usable cached DOM, reusing it"); + setDocumentElement(cachedDOM->getOwnerDocument(),cachedDOM); + dc->releaseParentDOM(true); return cachedDOM; } @@ -75,7 +77,7 @@ DOMElement* AbstractXMLObjectMarshaller::marshall(XMLObject* xmlObject, DOMDocum } } - // If we get here, we didn't have a usable DOM (and/or we flushed the one we had). + // If we get here, we didn't have a usable DOM (and/or we released the one we had). // We may need to create our own document. bool bindDocument=false; if (!document) { @@ -83,43 +85,103 @@ DOMElement* AbstractXMLObjectMarshaller::marshall(XMLObject* xmlObject, DOMDocum bindDocument=true; } - XT_log.debug("creating root element to marshall"); - DOMElement* domElement = document->createElementNS( - xmlObject->getElementQName().getNamespaceURI(), xmlObject->getElementQName().getLocalPart() - ); - domElement->setPrefix(xmlObject->getElementQName().getPrefix()); - try { - marshallNamespaces(xmlObject, domElement); - marshallAttributes(xmlObject, domElement); - marshallChildElements(xmlObject, domElement); - marshallElementContent(xmlObject, domElement); - marshallElementType(xmlObject, domElement); - - /* TODO: signing - if (xmlObject instanceof SignableXMLObject) { - signElement(domElement, xmlObject); + XT_log.debug("creating root element to marshall"); + DOMElement* domElement = document->createElementNS( + xmlObject->getElementQName().getNamespaceURI(), xmlObject->getElementQName().getLocalPart() + ); + setDocumentElement(document, domElement); + marshallInto(xmlObject, domElement); + + //Recache the DOM. + if (dc) { + XT_log.debug("caching DOM for XMLObject (document is %sbound)", bindDocument ? "" : "not "); + dc->setDOM(domElement, bindDocument); + dc->releaseParentDOM(true); } - */ + + return domElement; } catch (...) { // Delete the document if need be, and rethrow. if (bindDocument) { document->release(); - document=NULL; } throw; } +} + +DOMElement* AbstractXMLObjectMarshaller::marshall(XMLObject* xmlObject, DOMElement* parentElement) const +{ +#ifdef _DEBUG + xmltooling::NDC ndc("marshall"); +#endif + + if (XT_log.isDebugEnabled()) { + XT_log.debug("starting to marshalling %s", xmlObject->getElementQName().toString().c_str()); + } + + DOMCachingXMLObject* dc=dynamic_cast(xmlObject); + if (dc) { + DOMElement* cachedDOM=dc->getDOM(); + if (cachedDOM) { + if (parentElement->getOwnerDocument()==cachedDOM->getOwnerDocument()) { + XT_log.debug("XMLObject has a usable cached DOM, reusing it"); + if (parentElement!=cachedDOM->getParentNode()) { + parentElement->appendChild(cachedDOM); + dc->releaseParentDOM(true); + } + return cachedDOM; + } + + // We have a DOM but it doesn't match the document we were given. This both sucks and blows. + // Without an adoptNode option to maintain the child pointers, we have to either import the + // DOM while somehow reassigning all the nested references (which amounts to a complete + // *unmarshall* operation), or we just release the existing DOM and hope that we can get + // it back. This depends on all objects being able to preserve their DOM at all costs. + dc->releaseChildrenDOM(true); + dc->releaseDOM(); + } + } + + // If we get here, we didn't have a usable DOM (and/or we released the one we had). + XT_log.debug("creating root element to marshall"); + DOMElement* domElement = parentElement->getOwnerDocument()->createElementNS( + xmlObject->getElementQName().getNamespaceURI(), xmlObject->getElementQName().getLocalPart() + ); + parentElement->appendChild(domElement); + marshallInto(xmlObject, domElement); //Recache the DOM. if (dc) { XT_log.debug("caching DOM for XMLObject"); - dc->setDOM(domElement, bindDocument); + dc->setDOM(domElement, false); + dc->releaseParentDOM(true); } return domElement; } - + +void AbstractXMLObjectMarshaller::marshallInto(XMLObject* xmlObject, DOMElement* targetElement) const +{ + targetElement->setPrefix(xmlObject->getElementQName().getPrefix()); + marshallNamespaces(xmlObject, targetElement); + marshallAttributes(xmlObject, targetElement); + marshallChildElements(xmlObject, targetElement); + marshallElementContent(xmlObject, targetElement); + marshallElementType(xmlObject, targetElement); + + /* TODO Signing/Encryption + if (xmlObject instanceof SignableXMLObject) { + signElement(targetElement, xmlObject); + } + + if (xmlObject instanceof EncryptableXMLObject) { + encryptElement(targetElement, xmlObject); + } + */ +} + void AbstractXMLObjectMarshaller::marshallElementType(XMLObject* xmlObject, DOMElement* domElement) const { const QName* type = xmlObject->getSchemaType(); diff --git a/xmltooling/io/AbstractXMLObjectMarshaller.h b/xmltooling/io/AbstractXMLObjectMarshaller.h index e893b80..e93a4b3 100644 --- a/xmltooling/io/AbstractXMLObjectMarshaller.h +++ b/xmltooling/io/AbstractXMLObjectMarshaller.h @@ -36,9 +36,14 @@ namespace xmltooling { virtual ~AbstractXMLObjectMarshaller() {} /** - * @see Marshaller::marshall() + * @see Marshaller::marshall(XMLObject*,DOMDocument*) */ DOMElement* marshall(XMLObject* xmlObject, DOMDocument* document=NULL) const; + + /** + * @see Marshaller::marshall(XMLObject*,DOMElement*) + */ + DOMElement* marshall(XMLObject* xmlObject, DOMElement* parentElement) const; protected: @@ -53,6 +58,31 @@ namespace xmltooling { AbstractXMLObjectMarshaller(const XMLCh* targetNamespaceURI, const XMLCh* targetLocalName); /** + * Sets the given element as the Document Element of the given Document. + * If the document already has a Document Element it is replaced by the given element. + * + * @param document the document + * @param element the Element that will serve as the Document Element + */ + void setDocumentElement(DOMDocument* document, DOMElement* element) const { + DOMElement* documentRoot = document->getDocumentElement(); + if (documentRoot) + document->replaceChild(documentRoot, element); + else + document->appendChild(element); + } + + /** + * Marshalls the given XMLObject into the given DOM Element. + * The DOM Element must be within a DOM tree rooted in the owning Document. + * + * @param xmlObject the XMLObject to marshall + * @param targetElement the Element into which the XMLObject is marshalled into + * @throws MarshallingException thrown if there is a problem marshalling the object + */ + void marshallInto(XMLObject* xmlObject, DOMElement* targetElement) const; + + /** * Creates an xsi:type attribute, corresponding to the given type of the XMLObject, on the DOM element. * * @param xmlObject the XMLObject diff --git a/xmltooling/io/Marshaller.h b/xmltooling/io/Marshaller.h index e9d6995..0beb061 100644 --- a/xmltooling/io/Marshaller.h +++ b/xmltooling/io/Marshaller.h @@ -47,18 +47,32 @@ namespace xmltooling { virtual ~Marshaller() {} /** - * Marshall this element, and its children, into a W3C DOM element rooted in a given document. - * If no document is provided, then the marshaller will create one, and bind it to the - * object being marshalled. + * Marshalls an object, and its children, into a DOM element. + * If a document is supplied, then it will be used to create the resulting elements. + * If the document does not have a Document Element set, then the resulting + * element will be set as the Document Element. If no document is supplied, then + * a new document will be created and bound to the lifetime of the root object being + * marshalled, unless an existing DOM can be reused without creating a new document. * * @param xmlObject the object to marshall - * @param document the DOM document the marshalled element will be rooted in - * - * @return the W3C DOM element representing this XML element - * + * @param document the DOM document the marshalled element will be placed in, or NULL + * @return the DOM element representing this XMLObject * @throws MarshallingException thrown if there is a problem marshalling the given object */ virtual DOMElement* marshall(XMLObject* xmlObject, DOMDocument* document=NULL) const=0; + + /** + * Marshall the given XMLObject and append it as a child of the given parent element. + * + * NOTE: The given Element must be within a DOM tree rooted in + * the Document owning the given Element. + * + * @param xmlObject the XMLObject to be marshalled + * @param parentElement the parent element to append the resulting DOM tree + * @return the marshalled element tree + * @throws MarshallingException thrown if the given XMLObject can not be marshalled. + */ + virtual DOMElement* marshall(XMLObject* xmlObject, DOMElement* parentElement) const=0; /** * Retrieves a Marshaller using the key it was registered with. diff --git a/xmltooling/io/Unmarshaller.h b/xmltooling/io/Unmarshaller.h index 0f99c6e..4e99a8d 100644 --- a/xmltooling/io/Unmarshaller.h +++ b/xmltooling/io/Unmarshaller.h @@ -78,7 +78,7 @@ namespace xmltooling { * @param element the element for which to return an unmarshaller * @return the unmarshaller or NULL */ - static const Unmarshaller* getUnmarshaller(const DOMElement* key); + static const Unmarshaller* getUnmarshaller(const DOMElement* element); /** * Retrieves the default Unmarshaller for an unknown DOM element -- 2.1.4