Merge branch '1.x' of ssh://authdev.it.ohio-state.edu/~scantor/git/cpp-xmltooling...
[shibboleth/cpp-xmltooling.git] / xmltooling / io / AbstractXMLObjectMarshaller.cpp
index da5cf65..51fc136 100644 (file)
-/*\r
-*  Copyright 2001-2006 Internet2\r
- * \r
-* Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- *     http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-/**\r
- * AbstractXMLObjectMarshaller.cpp\r
- * \r
- * A thread-safe abstract marshaller.\r
- */\r
-\r
-#include "internal.h"\r
-#include "DOMCachingXMLObject.h"\r
-#include "exceptions.h"\r
-#include "io/AbstractXMLObjectMarshaller.h"\r
-#include "util/NDC.h"\r
-#include "util/XMLConstants.h"\r
-#include "util/XMLHelper.h"\r
-\r
-#include <algorithm>\r
-#include <functional>\r
-#include <xercesc/util/XMLUniDefs.hpp>\r
-#include <log4cpp/Category.hh>\r
-\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
-{\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 (!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
-        }\r
-    }\r
-    \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
-        document=DOMImplementationRegistry::getDOMImplementation(NULL)->createDocument();\r
-        bindDocument=true;\r
-    }\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
-\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
-}\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, false);\r
-        dc->releaseParentDOM(true);\r
-    }\r
-\r
-    return domElement;\r
-}\r
-        \r
-void AbstractXMLObjectMarshaller::marshallInto(XMLObject& xmlObject, DOMElement* targetElement) 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
-\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
-    if (type) {\r
-        XT_log.debug("setting xsi:type attribute for XMLObject");\r
-        \r
-        const XMLCh* typeLocalName = type->getLocalPart();\r
-        if (!typeLocalName || !*typeLocalName) {\r
-            throw MarshallingException("Schema type of XMLObject may not have an empty local name.");\r
-        }\r
-\r
-        static const XMLCh xsitype[] = {\r
-            chLatin_x, chLatin_s, chLatin_i, chColon, chLatin_t, chLatin_y, chLatin_p, chLatin_e, chNull\r
-            };\r
-        \r
-        XMLCh* xsivalue=const_cast<XMLCh*>(typeLocalName);\r
-        const XMLCh* prefix=type->getPrefix();\r
-        if (prefix && *prefix) {\r
-            xsivalue=new XMLCh[XMLString::stringLen(typeLocalName) + XMLString::stringLen(prefix) + 2*sizeof(XMLCh)];\r
-            *xsivalue=chNull;\r
-            XMLString::catString(xsivalue,prefix);\r
-            static const XMLCh colon[] = {chColon, chNull};\r
-            XMLString::catString(xsivalue,colon);\r
-            XMLString::catString(xsivalue,typeLocalName);\r
-        }   \r
-        domElement->setAttributeNS(XMLConstants::XSI_NS, xsitype, xsivalue);\r
-        if (xsivalue != typeLocalName)\r
-            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
-    }\r
-}\r
-\r
-class _addns : public binary_function<DOMElement*,Namespace,void> {\r
-public:\r
-    void operator()(DOMElement* domElement, const Namespace& ns) const {\r
-        const XMLCh* prefix=ns.getNamespacePrefix();\r
-        const XMLCh* uri=ns.getNamespaceURI();\r
-        \r
-        // Check to see if the prefix is already declared properly above this node.\r
-        if (!ns.alwaysDeclare()) {\r
-            const XMLCh* declared=lookupNamespaceURI(domElement->getParentNode(),prefix);\r
-            if (declared && XMLString::equals(declared,uri))\r
-                return;\r
-        }\r
-            \r
-        if (prefix && *prefix) {\r
-            XMLCh* xmlns=new XMLCh[XMLString::stringLen(XMLConstants::XMLNS_PREFIX) + XMLString::stringLen(prefix) + 2*sizeof(XMLCh)];\r
-            *xmlns=chNull;\r
-            XMLString::catString(xmlns,XMLConstants::XMLNS_PREFIX);\r
-            static const XMLCh colon[] = {chColon, chNull};\r
-            XMLString::catString(xmlns,colon);\r
-            XMLString::catString(xmlns,prefix);\r
-            domElement->setAttributeNS(XMLConstants::XMLNS_NS, xmlns, uri);\r
-        }\r
-        else {\r
-            domElement->setAttributeNS(XMLConstants::XMLNS_NS, XMLConstants::XMLNS_PREFIX, uri);\r
-        }\r
-    }\r
-\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
-            return NULL;    // we're done\r
-        DOMNamedNodeMap* attributes = static_cast<const DOMElement*>(n)->getAttributes();\r
-        if (!attributes)\r
-            return lookupNamespaceURI(n->getParentNode(),prefix);   // defer to parent\r
-        DOMNode* childNode;\r
-        DOMAttr* attribute;\r
-        for (XMLSize_t i=0; i<attributes->getLength(); i++) {\r
-            childNode = attributes->item(i);\r
-            if (childNode->getNodeType() != DOMNode::ATTRIBUTE_NODE)    // not an attribute?\r
-                continue;\r
-            attribute = static_cast<DOMAttr*>(childNode);\r
-            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
-                return attribute->getNodeValue();\r
-            else if (XMLString::equals(prefix,attribute->getLocalName()))\r
-                return attribute->getNodeValue();\r
-        }\r
-        // Defer to parent.\r
-        return lookupNamespaceURI(n->getParentNode(),prefix);\r
-    }\r
-};\r
-\r
-void AbstractXMLObjectMarshaller::marshallNamespaces(const XMLObject& xmlObject, DOMElement* domElement) const\r
-{\r
-    XT_log.debug("marshalling namespace attributes for XMLObject");\r
-    const set<Namespace>& namespaces = xmlObject.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
-{\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
-}\r
+/**
+ * Licensed to the University Corporation for Advanced Internet
+ * Development, Inc. (UCAID) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
+ *
+ * UCAID licenses this file to you 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.
+ */
+
+/**
+ * AbstractXMLObjectMarshaller.cpp
+ * 
+ * A thread-safe abstract marshaller.
+ */
+
+#include "internal.h"
+#include "exceptions.h"
+#include "io/AbstractXMLObjectMarshaller.h"
+#ifndef XMLTOOLING_NO_XMLSEC
+    #include "security/Credential.h"
+    #include "signature/Signature.h"
+#endif
+#include "util/NDC.h"
+#include "util/XMLConstants.h"
+#include "util/XMLHelper.h"
+
+#include <algorithm>
+#include <functional>
+#include <xercesc/util/XMLUniDefs.hpp>
+
+#ifndef XMLTOOLING_NO_XMLSEC
+    using namespace xmlsignature;
+#endif
+using namespace xmlconstants;
+using namespace xmltooling;
+using namespace xercesc;
+using namespace std;
+
+AbstractXMLObjectMarshaller::AbstractXMLObjectMarshaller()
+{
+}
+
+AbstractXMLObjectMarshaller::~AbstractXMLObjectMarshaller()
+{
+}
+
+void AbstractXMLObjectMarshaller::setDocumentElement(DOMDocument* document, DOMElement* element) const
+{
+    DOMElement* documentRoot = document->getDocumentElement();
+    if (documentRoot)
+        document->replaceChild(element, documentRoot);
+    else
+        document->appendChild(element);
+}
+
+DOMElement* AbstractXMLObjectMarshaller::marshall(
+    DOMDocument* document
+#ifndef XMLTOOLING_NO_XMLSEC
+    ,const vector<Signature*>* sigs
+    ,const Credential* credential
+#endif
+    ) const
+{
+#ifdef _DEBUG
+    xmltooling::NDC ndc("marshall");
+#endif
+
+    if (m_log.isDebugEnabled()) {
+        m_log.debug("starting to marshal %s", getElementQName().toString().c_str());
+    }
+
+    DOMElement* cachedDOM=getDOM();
+    if (cachedDOM) {
+        if (!document || document==cachedDOM->getOwnerDocument()) {
+            m_log.debug("XMLObject has a usable cached DOM, reusing it");
+            if (document)
+                setDocumentElement(cachedDOM->getOwnerDocument(),cachedDOM);
+            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.
+        releaseChildrenDOM(true);
+        releaseDOM();
+    }
+    
+    // If we get here, we didn't have a usable DOM (and/or we released the one we had).
+    prepareForMarshalling();
+
+    // We may need to create our own document.
+    bool bindDocument=false;
+    if (!document) {
+        document=DOMImplementationRegistry::getDOMImplementation(nullptr)->createDocument();
+        bindDocument=true;
+    }
+    
+    XercesJanitor<DOMDocument> janitor(bindDocument ? document : nullptr);
+
+    m_log.debug("creating root element to marshall");
+    DOMElement* domElement = document->createElementNS(
+        getElementQName().getNamespaceURI(), getElementQName().getLocalPart()
+        );
+    setDocumentElement(document, domElement);
+#ifndef XMLTOOLING_NO_XMLSEC
+    marshallInto(domElement, sigs, credential);
+#else
+    marshallInto(domElement);
+#endif
+    //Recache the DOM.
+    m_log.debug("caching DOM for XMLObject (document is %sbound)", bindDocument ? "" : "not ");
+    setDOM(domElement, bindDocument);
+    janitor.release();  // safely transferred
+    releaseParentDOM(true);
+
+    return domElement;
+}
+
+DOMElement* AbstractXMLObjectMarshaller::marshall(
+    DOMElement* parentElement
+#ifndef XMLTOOLING_NO_XMLSEC
+    ,const vector<Signature*>* sigs
+    ,const Credential* credential
+#endif
+    ) const
+{
+#ifdef _DEBUG
+    xmltooling::NDC ndc("marshall");
+#endif
+
+    if (m_log.isDebugEnabled()) {
+        m_log.debug("starting to marshalling %s", getElementQName().toString().c_str());
+    }
+
+    DOMElement* cachedDOM=getDOM();
+    if (cachedDOM) {
+        if (parentElement->getOwnerDocument()==cachedDOM->getOwnerDocument()) {
+            m_log.debug("XMLObject has a usable cached DOM, reusing it");
+            if (parentElement!=cachedDOM->getParentNode()) {
+                parentElement->appendChild(cachedDOM);
+                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.
+        releaseChildrenDOM(true);
+        releaseDOM();
+    }
+    
+    // If we get here, we didn't have a usable DOM (and/or we released the one we had).
+    prepareForMarshalling();
+
+    m_log.debug("creating root element to marshall");
+    DOMElement* domElement = parentElement->getOwnerDocument()->createElementNS(
+        getElementQName().getNamespaceURI(), getElementQName().getLocalPart()
+        );
+    parentElement->appendChild(domElement);
+#ifndef XMLTOOLING_NO_XMLSEC
+    marshallInto(domElement, sigs, credential);
+#else
+    marshallInto(domElement);
+#endif
+
+    //Recache the DOM.
+    m_log.debug("caching DOM for XMLObject");
+    setDOM(domElement, false);
+    releaseParentDOM(true);
+
+    return domElement;
+}
+
+void AbstractXMLObjectMarshaller::marshallInto(
+    DOMElement* targetElement
+#ifndef XMLTOOLING_NO_XMLSEC
+    ,const vector<Signature*>* sigs
+    ,const Credential* credential
+#endif
+    ) const
+{
+    if (getElementQName().hasPrefix())
+        targetElement->setPrefix(getElementQName().getPrefix());
+
+    if (m_schemaLocation || m_noNamespaceSchemaLocation) {
+        static const XMLCh schemaLocation[] = {
+            chLatin_x, chLatin_s, chLatin_i, chColon,
+            chLatin_s, chLatin_c, chLatin_h, chLatin_e, chLatin_m, chLatin_a,
+            chLatin_L, chLatin_o, chLatin_c, chLatin_a, chLatin_t, chLatin_i, chLatin_o, chLatin_n, chNull
+            };
+        static const XMLCh noNamespaceSchemaLocation[] = {
+            chLatin_x, chLatin_s, chLatin_i, chColon,
+            chLatin_n, chLatin_o, chLatin_N, chLatin_a, chLatin_m, chLatin_e, chLatin_s, chLatin_p, chLatin_a, chLatin_c, chLatin_e,
+            chLatin_S, chLatin_c, chLatin_h, chLatin_e, chLatin_m, chLatin_a,
+            chLatin_L, chLatin_o, chLatin_c, chLatin_a, chLatin_t, chLatin_i, chLatin_o, chLatin_n, chNull
+            };
+        if (targetElement->getParentNode()==nullptr || targetElement->getParentNode()->getNodeType()==DOMNode::DOCUMENT_NODE) {
+            if (m_schemaLocation)
+                targetElement->setAttributeNS(XSI_NS,schemaLocation,m_schemaLocation); 
+            if (m_noNamespaceSchemaLocation)
+                targetElement->setAttributeNS(XSI_NS,noNamespaceSchemaLocation,m_noNamespaceSchemaLocation); 
+        }
+    }
+    
+    static const XMLCh _nil[] = { chLatin_x, chLatin_s, chLatin_i, chColon, chLatin_n, chLatin_i, chLatin_l, chNull };
+    
+    if (m_nil != xmlconstants::XML_BOOL_NULL) {
+        switch (m_nil) {
+            case xmlconstants::XML_BOOL_TRUE:
+               targetElement->setAttributeNS(XSI_NS, _nil, xmlconstants::XML_TRUE);
+                break;
+            case xmlconstants::XML_BOOL_ONE:
+               targetElement->setAttributeNS(XSI_NS, _nil, xmlconstants::XML_ONE);
+                break;
+            case xmlconstants::XML_BOOL_FALSE:
+               targetElement->setAttributeNS(XSI_NS, _nil, xmlconstants::XML_FALSE);
+                break;
+            case xmlconstants::XML_BOOL_ZERO:
+               targetElement->setAttributeNS(XSI_NS, _nil, xmlconstants::XML_ZERO);
+                break;
+        }
+        m_log.debug("adding XSI namespace to list of namespaces visibly used by XMLObject");
+        addNamespace(Namespace(XSI_NS, XSI_PREFIX, false, Namespace::VisiblyUsed));
+    }
+
+    marshallElementType(targetElement);
+    marshallNamespaces(targetElement);
+    marshallAttributes(targetElement);
+    
+#ifndef XMLTOOLING_NO_XMLSEC
+    marshallContent(targetElement,credential);
+    if (sigs) {
+        for_each(sigs->begin(),sigs->end(),bind2nd(mem_fun1_t<void,Signature,const Credential*>(&Signature::sign),credential));
+    }
+#else
+    marshallContent(targetElement);
+#endif
+}
+
+void AbstractXMLObjectMarshaller::marshallElementType(DOMElement* domElement) const
+{
+    const QName* type = getSchemaType();
+    if (type) {
+        m_log.debug("setting xsi:type attribute for XMLObject");
+        
+        const XMLCh* typeLocalName = type->getLocalPart();
+        if (!typeLocalName || !*typeLocalName) {
+            throw MarshallingException("Schema type of XMLObject may not have an empty local name.");
+        }
+
+        static const XMLCh xsitype[] = {
+            chLatin_x, chLatin_s, chLatin_i, chColon, chLatin_t, chLatin_y, chLatin_p, chLatin_e, chNull
+            };
+        
+        XMLCh* xsivalue=const_cast<XMLCh*>(typeLocalName);
+        const XMLCh* prefix=type->getPrefix();
+        if (prefix && *prefix) {
+            xsivalue=new XMLCh[XMLString::stringLen(typeLocalName) + XMLString::stringLen(prefix) + 2*sizeof(XMLCh)];
+            *xsivalue=chNull;
+            XMLString::catString(xsivalue,prefix);
+            static const XMLCh colon[] = {chColon, chNull};
+            XMLString::catString(xsivalue,colon);
+            XMLString::catString(xsivalue,typeLocalName);
+        }   
+        domElement->setAttributeNS(XSI_NS, xsitype, xsivalue);
+        if (xsivalue != typeLocalName)
+            XMLString::release(&xsivalue);
+
+        m_log.debug("adding XSI namespace to list of namespaces visibly used by XMLObject");
+        addNamespace(Namespace(XSI_NS, XSI_PREFIX, false, Namespace::VisiblyUsed));
+    }
+}
+
+class _addns : public binary_function<DOMElement*,Namespace,void> {
+public:
+    void operator()(DOMElement* domElement, const Namespace& ns) const {
+        const XMLCh* prefix=ns.getNamespacePrefix();
+        const XMLCh* uri=ns.getNamespaceURI();
+        
+        // Check for xmlns:xml.
+        if (XMLString::equals(prefix, XML_PREFIX) && XMLString::equals(uri, XML_NS))
+            return;
+
+        // Check to see if the prefix is already declared properly above this node.
+        if (!ns.alwaysDeclare()) {
+            const XMLCh* declared=lookupNamespaceURI(domElement->getParentNode(),prefix);
+            if (declared && XMLString::equals(declared,uri))
+                return;
+        }
+            
+        if (prefix && *prefix) {
+            XMLCh* xmlns=new XMLCh[XMLString::stringLen(XMLNS_PREFIX) + XMLString::stringLen(prefix) + 2*sizeof(XMLCh)];
+            *xmlns=chNull;
+            XMLString::catString(xmlns,XMLNS_PREFIX);
+            static const XMLCh colon[] = {chColon, chNull};
+            XMLString::catString(xmlns,colon);
+            XMLString::catString(xmlns,prefix);
+            domElement->setAttributeNS(XMLNS_NS, xmlns, uri);
+            delete[] xmlns;
+        }
+        else {
+            domElement->setAttributeNS(XMLNS_NS, XMLNS_PREFIX, uri);
+        }
+    }
+
+    const XMLCh* lookupNamespaceURI(const DOMNode* n, const XMLCh* prefix) const {
+        // Return nullptr if no declaration in effect. The empty string signifies the null namespace.
+        if (!n || n->getNodeType()!=DOMNode::ELEMENT_NODE) {
+            // At the root, the default namespace is set to the null namespace.
+            if (!prefix || !*prefix)
+                return &chNull;
+            return nullptr;    // we're done
+        }
+        DOMNamedNodeMap* attributes = static_cast<const DOMElement*>(n)->getAttributes();
+        if (!attributes)
+            return lookupNamespaceURI(n->getParentNode(),prefix);   // defer to parent
+        DOMNode* childNode;
+        DOMAttr* attribute;
+        for (XMLSize_t i=0; i<attributes->getLength(); i++) {
+            childNode = attributes->item(i);
+            if (childNode->getNodeType() != DOMNode::ATTRIBUTE_NODE)    // not an attribute?
+                continue;
+            attribute = static_cast<DOMAttr*>(childNode);
+            if (!XMLString::equals(attribute->getNamespaceURI(),XMLNS_NS))
+                continue;   // not a namespace declaration
+            // Local name should be the prefix and the value would be the URI, except for the default namespace.
+            if ((!prefix || !*prefix) && XMLString::equals(attribute->getLocalName(),XMLNS_PREFIX))
+                return attribute->getNodeValue();
+            else if (XMLString::equals(prefix,attribute->getLocalName()))
+                return attribute->getNodeValue();
+        }
+        // Defer to parent.
+        return lookupNamespaceURI(n->getParentNode(),prefix);
+    }
+};
+
+void AbstractXMLObjectMarshaller::marshallNamespaces(DOMElement* domElement) const
+{
+    m_log.debug("marshalling namespace attributes for XMLObject");
+    const set<Namespace>& namespaces = getNamespaces();
+    for_each(namespaces.begin(),namespaces.end(),bind1st(_addns(),domElement));
+}
+
+void AbstractXMLObjectMarshaller::marshallContent(
+    DOMElement* domElement
+#ifndef XMLTOOLING_NO_XMLSEC
+    ,const Credential* credential
+#endif
+    ) const
+{
+    m_log.debug("marshalling text and child elements for XMLObject");
+    
+    unsigned int pos=0;
+    const XMLCh* val = getTextContent(pos);
+    if (val && *val)
+        domElement->appendChild(domElement->getOwnerDocument()->createTextNode(val));
+    
+    const list<XMLObject*>& children=getOrderedChildren();
+    for (list<XMLObject*>::const_iterator i=children.begin(); i!=children.end(); ++i) {
+        if (*i) {
+#ifndef XMLTOOLING_NO_XMLSEC
+            (*i)->marshall(domElement,nullptr,credential);
+#else
+            (*i)->marshall(domElement);
+#endif
+            val = getTextContent(++pos);
+            if (val && *val)
+                domElement->appendChild(domElement->getOwnerDocument()->createTextNode(val));
+        }
+    }
+}
+
+void AbstractXMLObjectMarshaller::marshallAttributes(DOMElement* domElement) const
+{
+}
+
+void AbstractXMLObjectMarshaller::prepareForMarshalling() const
+{
+}