DOM fixup changes
authorScott Cantor <cantor.2@osu.edu>
Tue, 28 Feb 2006 04:39:39 +0000 (04:39 +0000)
committerScott Cantor <cantor.2@osu.edu>
Tue, 28 Feb 2006 04:39:39 +0000 (04:39 +0000)
xmltooling/impl/UnknownElement.cpp
xmltooling/impl/UnknownElement.h
xmltooling/io/AbstractXMLObjectMarshaller.cpp
xmltooling/io/AbstractXMLObjectMarshaller.h
xmltooling/io/Marshaller.h
xmltooling/io/Unmarshaller.h

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