Multi-line svn commit, see body.
[shibboleth/cpp-xmltooling.git] / xmltooling / io / AbstractXMLObjectMarshaller.cpp
index da5cf65..a33016c 100644 (file)
  */\r
 \r
 #include "internal.h"\r
-#include "DOMCachingXMLObject.h"\r
 #include "exceptions.h"\r
 #include "io/AbstractXMLObjectMarshaller.h"\r
+#ifndef XMLTOOLING_NO_XMLSEC\r
+    #include "signature/Signature.h"\r
+#endif\r
 #include "util/NDC.h"\r
 #include "util/XMLConstants.h"\r
 #include "util/XMLHelper.h"\r
 #include <xercesc/util/XMLUniDefs.hpp>\r
 #include <log4cpp/Category.hh>\r
 \r
+#ifndef XMLTOOLING_NO_XMLSEC\r
+    using namespace xmlsignature;\r
+#endif\r
 using namespace xmltooling;\r
 using namespace log4cpp;\r
 using namespace std;\r
 \r
 #define XT_log (*static_cast<Category*>(m_log))\r
 \r
-AbstractXMLObjectMarshaller::AbstractXMLObjectMarshaller()\r
-    : m_log(&Category::getInstance(XMLTOOLING_LOGCAT".Marshaller")) {}\r
-\r
-DOMElement* AbstractXMLObjectMarshaller::marshall(XMLObject* xmlObject, DOMDocument* document) const\r
+DOMElement* AbstractXMLObjectMarshaller::marshall(\r
+    DOMDocument* document\r
+#ifndef XMLTOOLING_NO_XMLSEC\r
+    ,const std::vector<xmlsignature::Signature*>* sigs\r
+#endif\r
+    ) 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
+        XT_log.debug("starting to marshal %s", 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 (!document || document==cachedDOM->getOwnerDocument()) {\r
-                XT_log.debug("XMLObject has a usable cached DOM, reusing it");\r
-                if (document)\r
-                    setDocumentElement(cachedDOM->getOwnerDocument(),cachedDOM);\r
-                dc->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 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
+    DOMElement* cachedDOM=getDOM();\r
+    if (cachedDOM) {\r
+        if (!document || document==cachedDOM->getOwnerDocument()) {\r
+            XT_log.debug("XMLObject has a usable cached DOM, reusing it");\r
+            if (document)\r
+                setDocumentElement(cachedDOM->getOwnerDocument(),cachedDOM);\r
+            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 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
+        releaseChildrenDOM(true);\r
+        releaseDOM();\r
     }\r
     \r
     // If we get here, we didn't have a usable DOM (and/or we released the one we had).\r
@@ -81,107 +85,114 @@ DOMElement* AbstractXMLObjectMarshaller::marshall(XMLObject* xmlObject, DOMDocum
         document=DOMImplementationRegistry::getDOMImplementation(NULL)->createDocument();\r
         bindDocument=true;\r
     }\r
+    \r
+    XercesJanitor<DOMDocument> janitor(bindDocument ? document : NULL);\r
 \r
-    try {\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
+    XT_log.debug("creating root element to marshall");\r
+    DOMElement* domElement = document->createElementNS(\r
+        getElementQName().getNamespaceURI(), getElementQName().getLocalPart()\r
+        );\r
+    setDocumentElement(document, domElement);\r
+#ifndef XMLTOOLING_NO_XMLSEC\r
+    marshallInto(domElement, sigs);\r
+#else\r
+    marshallInto(domElement);\r
+#endif\r
+    //Recache the DOM.\r
+    XT_log.debug("caching DOM for XMLObject (document is %sbound)", bindDocument ? "" : "not ");\r
+    setDOM(domElement, bindDocument);\r
+    janitor.release();  // safely transferred\r
+    releaseParentDOM(true);\r
 \r
-        return domElement;\r
-    }\r
-    catch (...) {\r
-        // Delete the document if need be, and rethrow.\r
-        if (bindDocument) {\r
-            document->release();\r
-        }\r
-        throw;\r
-    }\r
+    return domElement;\r
 }\r
 \r
-DOMElement* AbstractXMLObjectMarshaller::marshall(XMLObject* xmlObject, DOMElement* parentElement) const\r
+DOMElement* AbstractXMLObjectMarshaller::marshall(\r
+    DOMElement* parentElement\r
+#ifndef XMLTOOLING_NO_XMLSEC\r
+    ,const std::vector<xmlsignature::Signature*>* sigs\r
+#endif\r
+    ) 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
+        XT_log.debug("starting to marshalling %s", 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
+    DOMElement* cachedDOM=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
+                releaseParentDOM(true);\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
+            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
+        releaseChildrenDOM(true);\r
+        releaseDOM();\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
+        getElementQName().getNamespaceURI(), getElementQName().getLocalPart()\r
         );\r
     parentElement->appendChild(domElement);\r
-    marshallInto(*xmlObject, domElement);\r
+#ifndef XMLTOOLING_NO_XMLSEC\r
+    marshallInto(domElement, sigs);\r
+#else\r
+    marshallInto(domElement);\r
+#endif\r
 \r
     //Recache the DOM.\r
-    if (dc) {\r
-        XT_log.debug("caching DOM for XMLObject");\r
-        dc->setDOM(domElement, false);\r
-        dc->releaseParentDOM(true);\r
-    }\r
+    XT_log.debug("caching DOM for XMLObject");\r
+    setDOM(domElement, false);\r
+    releaseParentDOM(true);\r
 \r
     return domElement;\r
 }\r
-        \r
-void AbstractXMLObjectMarshaller::marshallInto(XMLObject& xmlObject, DOMElement* targetElement) const\r
+\r
+void AbstractXMLObjectMarshaller::marshallInto(\r
+    DOMElement* targetElement\r
+#ifndef XMLTOOLING_NO_XMLSEC\r
+    ,const std::vector<xmlsignature::Signature*>* sigs\r
+#endif\r
+    ) const\r
 {\r
-    targetElement->setPrefix(xmlObject.getElementQName().getPrefix());\r
-    marshallElementType(xmlObject, targetElement);\r
-    marshallNamespaces(xmlObject, targetElement);\r
-    marshallAttributes(xmlObject, targetElement);\r
-    marshallChildElements(xmlObject, targetElement);\r
-    marshallElementContent(xmlObject, targetElement);\r
+    if (getElementQName().hasPrefix())\r
+        targetElement->setPrefix(getElementQName().getPrefix());\r
 \r
-    /* TODO Signing/Encryption\r
-    if (xmlObject instanceof SignableXMLObject) {\r
-        signElement(targetElement, xmlObject);\r
+    if (m_schemaLocation) {\r
+        static const XMLCh schemaLocation[]= UNICODE_LITERAL_14(s,c,h,e,m,a,L,o,c,a,t,i,o,n);\r
+        if (targetElement->getParentNode()==NULL || targetElement->getParentNode()->getNodeType()==DOMNode::DOCUMENT_NODE)\r
+            targetElement->setAttributeNS(XMLConstants::XSI_NS,schemaLocation,m_schemaLocation); \r
     }\r
 \r
-    if (xmlObject instanceof EncryptableXMLObject) {\r
-        encryptElement(targetElement, xmlObject);\r
+    marshallElementType(targetElement);\r
+    marshallNamespaces(targetElement);\r
+    marshallAttributes(targetElement);\r
+    marshallContent(targetElement);\r
+    \r
+#ifndef XMLTOOLING_NO_XMLSEC\r
+    if (sigs) {\r
+        for_each(sigs->begin(),sigs->end(),mem_fun<void,Signature>(&Signature::sign));\r
     }\r
-    */\r
+#endif\r
 }\r
 \r
-void AbstractXMLObjectMarshaller::marshallElementType(XMLObject& xmlObject, DOMElement* domElement) const\r
+void AbstractXMLObjectMarshaller::marshallElementType(DOMElement* domElement) const\r
 {\r
-    const QName* type = xmlObject.getSchemaType();\r
+    const QName* type = getSchemaType();\r
     if (type) {\r
         XT_log.debug("setting xsi:type attribute for XMLObject");\r
         \r
@@ -209,7 +220,7 @@ void AbstractXMLObjectMarshaller::marshallElementType(XMLObject& xmlObject, DOME
             XMLString::release(&xsivalue);\r
 \r
         XT_log.debug("Adding XSI namespace to list of namespaces used by XMLObject");\r
-        xmlObject.addNamespace(Namespace(XMLConstants::XSI_NS, XMLConstants::XSI_PREFIX));\r
+        addNamespace(Namespace(XMLConstants::XSI_NS, XMLConstants::XSI_PREFIX));\r
     }\r
 }\r
 \r
@@ -242,8 +253,12 @@ public:
 \r
     const XMLCh* lookupNamespaceURI(const DOMNode* n, const XMLCh* prefix) const {\r
         // Return NULL if no declaration in effect. The empty string signifies the null namespace.\r
-        if (!n || n->getNodeType()!=DOMNode::ELEMENT_NODE)\r
+        if (!n || n->getNodeType()!=DOMNode::ELEMENT_NODE) {\r
+            // At the root, the default namespace is set to the null namespace.\r
+            if (!prefix || !*prefix)\r
+                return &chNull;\r
             return NULL;    // we're done\r
+        }\r
         DOMNamedNodeMap* attributes = static_cast<const DOMElement*>(n)->getAttributes();\r
         if (!attributes)\r
             return lookupNamespaceURI(n->getParentNode(),prefix);   // defer to parent\r
@@ -257,7 +272,7 @@ public:
             if (!XMLString::equals(attribute->getNamespaceURI(),XMLConstants::XMLNS_NS))\r
                 continue;   // not a namespace declaration\r
             // Local name should be the prefix and the value would be the URI, except for the default namespace.\r
-            if (!prefix && XMLString::equals(attribute->getLocalName(),XMLConstants::XMLNS_PREFIX))\r
+            if ((!prefix || !*prefix) && XMLString::equals(attribute->getLocalName(),XMLConstants::XMLNS_PREFIX))\r
                 return attribute->getNodeValue();\r
             else if (XMLString::equals(prefix,attribute->getLocalName()))\r
                 return attribute->getNodeValue();\r
@@ -267,40 +282,28 @@ public:
     }\r
 };\r
 \r
-void AbstractXMLObjectMarshaller::marshallNamespaces(const XMLObject& xmlObject, DOMElement* domElement) const\r
+void AbstractXMLObjectMarshaller::marshallNamespaces(DOMElement* domElement) const\r
 {\r
     XT_log.debug("marshalling namespace attributes for XMLObject");\r
-    const set<Namespace>& namespaces = xmlObject.getNamespaces();\r
+    const set<Namespace>& namespaces = getNamespaces();\r
     for_each(namespaces.begin(),namespaces.end(),bind1st(_addns(),domElement));\r
 }\r
 \r
-class _marshallchild : public binary_function<XMLObject*,DOMElement*,void> {\r
-    void* m_log;\r
-public:\r
-    _marshallchild(void* log) : m_log(log) {}\r
-    void operator()(XMLObject* obj, DOMElement* element) const {\r
-        if (!obj)\r
-            return;\r
-        if (XT_log.isDebugEnabled()) {\r
-            XT_log.debug("getting marshaller for child XMLObject: %s", obj->getElementQName().toString().c_str());\r
-        }\r
-\r
-        const Marshaller* marshaller = Marshaller::getMarshaller(obj);\r
-        if (!marshaller) {\r
-            XT_log.error(\r
-                "no default unmarshaller installed, unknown child object: %s",\r
-                obj->getElementQName().toString().c_str()\r
-                );\r
-            throw MarshallingException("Marshaller found unknown child element, but no default marshaller was found.");\r
-        }\r
-        element->appendChild(marshaller->marshall(obj, element));\r
-    }\r
-};\r
-\r
-void AbstractXMLObjectMarshaller::marshallChildElements(const XMLObject& xmlObject, DOMElement* domElement) const\r
+void AbstractXMLObjectMarshaller::marshallContent(DOMElement* domElement) const\r
 {\r
-    XT_log.debug("marshalling child elements for XMLObject");\r
-\r
-    const list<XMLObject*>& children=xmlObject.getOrderedChildren();\r
-    for_each(children.begin(),children.end(),bind2nd(_marshallchild(m_log),domElement));\r
+    XT_log.debug("marshalling text and child elements for XMLObject");\r
+    \r
+    const XMLCh* val;\r
+    unsigned int pos=0;\r
+    const list<XMLObject*>& children=getOrderedChildren();\r
+    for (list<XMLObject*>::const_iterator i=children.begin(); i!=children.end(); ++i, ++pos) {\r
+        val = getTextContent(pos);\r
+        if (val && *val)\r
+            domElement->appendChild(domElement->getOwnerDocument()->createTextNode(val));\r
+        if (*i)\r
+            (*i)->marshall(domElement);\r
+    }\r
+    val = getTextContent(pos);\r
+    if (val && *val)\r
+        domElement->appendChild(domElement->getOwnerDocument()->createTextNode(val));\r
 }\r