Initial marshalling support.
authorcantor <cantor@de75baf8-a10c-0410-a50a-987c0e22f00f>
Wed, 22 Feb 2006 18:02:55 +0000 (18:02 +0000)
committercantor <cantor@de75baf8-a10c-0410-a50a-987c0e22f00f>
Wed, 22 Feb 2006 18:02:55 +0000 (18:02 +0000)
git-svn-id: https://svn.middleware.georgetown.edu/cpp-xmltooling/trunk@30 de75baf8-a10c-0410-a50a-987c0e22f00f

xmltooling/AbstractDOMCachingXMLObject.cpp
xmltooling/AbstractDOMCachingXMLObject.h
xmltooling/DOMCachingXMLObject.h
xmltooling/XMLObject.h
xmltooling/io/AbstractXMLObjectMarshaller.cpp [new file with mode: 0644]
xmltooling/io/AbstractXMLObjectMarshaller.h [new file with mode: 0644]
xmltooling/io/AbstractXMLObjectUnmarshaller.cpp
xmltooling/io/AbstractXMLObjectUnmarshaller.h
xmltooling/xmltooling.vcproj

index 62ea3e9..3a3d853 100644 (file)
@@ -59,9 +59,6 @@ void AbstractDOMCachingXMLObject::releaseDOM()
     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".DOM");\r
     if (log.isDebugEnabled())\r
         log.debug("Releasing cached DOM reprsentation for %s", getElementQName().toString().c_str());\r
-\r
-    // We don't get rid of the document we're holding, if any.\r
-    // The marshalling process deals with that.\r
     setDOM(NULL);\r
 }\r
 \r
@@ -86,11 +83,11 @@ void AbstractDOMCachingXMLObject::releaseParentDOM(bool propagateRelease)
 class _release : public binary_function<XMLObject*,bool,void> {\r
 public:\r
     void operator()(XMLObject* obj, bool propagate) const {\r
-        DOMCachingXMLObject* domCachingParent = dynamic_cast<DOMCachingXMLObject*>(obj);\r
-        if (domCachingParent) {\r
-            domCachingParent->releaseDOM();\r
+        DOMCachingXMLObject* domCaching = dynamic_cast<DOMCachingXMLObject*>(obj);\r
+        if (domCaching) {\r
+            domCaching->releaseDOM();\r
             if (propagate)\r
-                domCachingParent->releaseChildrenDOM(propagate);\r
+                domCaching->releaseChildrenDOM(propagate);\r
         }\r
     }\r
 };\r
index 4c90d81..35bd2e1 100644 (file)
@@ -44,7 +44,7 @@ namespace xmltooling {
         /**\r
          * @see DOMCachingXMLObject::getDOM()\r
          */\r
-        const DOMElement* getDOM() const {\r
+        DOMElement* getDOM() const {\r
             return m_dom;\r
         }\r
         \r
@@ -92,8 +92,8 @@ namespace xmltooling {
          */\r
         void releaseThisAndChildrenDOM() {\r
             if (m_dom) {\r
-                releaseDOM();\r
                 releaseChildrenDOM(true);\r
+                releaseDOM();\r
             }\r
         }\r
     \r
index bf14c8c..dfbe84c 100644 (file)
@@ -44,7 +44,7 @@ namespace xmltooling {
          * \r
          * @return the DOM representation of this XMLObject\r
          */\r
-        virtual const DOMElement* getDOM() const=0;\r
+        virtual DOMElement* getDOM() const=0;\r
         \r
         /**\r
          * Sets the DOM representation of this XMLObject.\r
index 5ed3363..83e003e 100644 (file)
@@ -143,7 +143,7 @@ namespace xmltooling {
          * @param v     vector in which to store pointers to child objects\r
          * @return the number of children\r
          */\r
-        virtual size_t getOrderedChildren(std::vector<XMLObject*>& v)=0;\r
+        virtual size_t getOrderedChildren(std::vector<XMLObject*>& v) const=0;\r
  };\r
 \r
 };\r
diff --git a/xmltooling/io/AbstractXMLObjectMarshaller.cpp b/xmltooling/io/AbstractXMLObjectMarshaller.cpp
new file mode 100644 (file)
index 0000000..7fd4e50
--- /dev/null
@@ -0,0 +1,224 @@
+/*\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(const XMLCh* targetNamespaceURI, const XMLCh* targetLocalName)\r
+        : m_targetQName(targetNamespaceURI, targetLocalName),\r
+        m_log(&Category::getInstance(XMLTOOLING_LOGCAT".Marshaller")) {\r
+    if (!targetLocalName || !*targetLocalName)\r
+        throw MarshallingException("targetLocalName cannot be null or empty");\r
+}\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("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, using it.");\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 flushed 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
+    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
+        }\r
+        */\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
+    //Recache the DOM.\r
+    if (dc) {\r
+        XT_log.debug("caching DOM for XMLObject");\r
+        dc->setDOM(domElement, bindDocument);\r
+    }\r
+\r
+    return domElement;\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
+        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
+\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 (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
+            if (XMLToolingConfig::getConfig().ignoreUnknownElements) {\r
+                marshaller=Marshaller::getDefaultMarshaller();\r
+                if (marshaller)\r
+                    XT_log.debug("using default marshaller");\r
+                else {\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
+            }\r
+            else {\r
+                XT_log.error("unknown child object: %s", obj->getElementQName().toString().c_str());\r
+                throw UnknownElementException("Marshaller found unknown child object.");\r
+            }\r
+        }\r
+        element->appendChild(marshaller->marshall(obj, element->getOwnerDocument()));\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
+    vector<XMLObject*> children;\r
+    if (xmlObject->getOrderedChildren(children)) {\r
+        for_each(children.begin(),children.end(),bind2nd(_marshallchild(m_log),domElement));\r
+    }\r
+}\r
diff --git a/xmltooling/io/AbstractXMLObjectMarshaller.h b/xmltooling/io/AbstractXMLObjectMarshaller.h
new file mode 100644 (file)
index 0000000..e893b80
--- /dev/null
@@ -0,0 +1,111 @@
+/*\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
+ * @file AbstractXMLObjectMarshaller.h\r
+ * \r
+ * A thread-safe abstract marshaller.\r
+ */\r
+\r
+#if !defined(__xmltooling_xmlmarshaller_h__)\r
+#define __xmltooling_xmlmarshaller_h__\r
+\r
+#include <xmltooling/io/Marshaller.h>\r
+\r
+namespace xmltooling {\r
+\r
+    /**\r
+     * A thread-safe abstract marshaller.\r
+     */\r
+    class XMLTOOL_API AbstractXMLObjectMarshaller : public virtual Marshaller\r
+    {\r
+    public:\r
+        virtual ~AbstractXMLObjectMarshaller() {}\r
+\r
+        /**\r
+         * @see Marshaller::marshall()\r
+         */\r
+        DOMElement* marshall(XMLObject* xmlObject, DOMDocument* document=NULL) const;\r
+    \r
+        \r
+    protected:\r
+        /**\r
+         * Constructor.\r
+         * \r
+         * @param targetNamespaceURI the namespace URI of either the schema type QName or element QName of the elements this\r
+         *            marshaller operates on\r
+         * @param targetLocalName the local name of either the schema type QName or element QName of the elements this\r
+         *            marshaller operates on\r
+         */\r
+        AbstractXMLObjectMarshaller(const XMLCh* targetNamespaceURI, const XMLCh* targetLocalName);\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
+         * @param domElement the DOM element\r
+         * \r
+         * @throws MarshallingException thrown if the type on the XMLObject is doesn't contain\r
+         * a local name, prefix, and namespace URI\r
+         */\r
+        void marshallElementType(XMLObject* xmlObject, DOMElement* domElement) const;\r
+\r
+        /**\r
+         * Creates the xmlns attributes for any namespaces set on the given XMLObject.\r
+         * \r
+         * @param xmlObject the XMLObject\r
+         * @param domElement the DOM element the namespaces will be added to\r
+         */\r
+        void marshallNamespaces(const XMLObject* xmlObject, DOMElement* domElement) const;\r
+    \r
+        /**\r
+         * Marshalls the child elements of the given XMLObject.\r
+         * \r
+         * @param xmlObject the XMLObject whose children will be marshalled\r
+         * @param domElement the DOM element that will recieved the marshalled children\r
+         * \r
+         * @throws MarshallingException thrown if there is a problem marshalling a child element\r
+         */\r
+        void marshallChildElements(const XMLObject* xmlObject, DOMElement* domElement) const;\r
+\r
+        /**\r
+         * Marshalls the attributes from the given XMLObject into the given DOM element.\r
+         * The XMLObject passed to this method is guaranteed to be of the target name\r
+         * specified during this marshaller's construction.\r
+         * \r
+         * @param xmlObject the XMLObject being marshalled\r
+         * @param domElement the DOM Element into which attributes will be marshalled\r
+         * \r
+         * @throws UnmarshallingException thrown if there is a problem unmarshalling an attribute\r
+         */\r
+        virtual void marshallAttributes(const XMLObject* xmlObject, DOMElement* domElement) const=0;\r
+\r
+        /**\r
+         * Marshalls data from the XMLObject into content of the DOM Element.\r
+         * \r
+         * @param xmlObject the XMLObject\r
+         * @param domElement the DOM element recieving the content\r
+         */\r
+        virtual void marshallElementContent(const XMLObject* xmlObject, DOMElement* domElement) const=0;\r
+\r
+        void* m_log;\r
+    private:\r
+        QName m_targetQName;\r
+    };\r
+    \r
+};\r
+\r
+#endif /* __xmltooling_xmlmarshaller_h__ */\r
index 9aefabc..ce3f358 100644 (file)
@@ -56,10 +56,6 @@ XMLObject* AbstractXMLObjectUnmarshaller::unmarshall(DOMElement* element, bool b
         XT_log.debug("unmarshalling DOM element %s", dname.get());\r
     }\r
 \r
-#ifdef _DEBUG\r
-    checkElementIsTarget(element);\r
-#endif\r
-\r
     XMLObject* xmlObject = buildXMLObject(element);\r
 \r
     if (element->hasAttributes()) {\r
@@ -85,31 +81,6 @@ XMLObject* AbstractXMLObjectUnmarshaller::unmarshall(DOMElement* element, bool b
     return xmlObject;\r
 }\r
 \r
-void AbstractXMLObjectUnmarshaller::checkElementIsTarget(const DOMElement* domElement) const\r
-{\r
-    auto_ptr<QName> elementName(XMLHelper::getNodeQName(domElement));\r
-\r
-    XT_log.debug("checking that root element meets target criteria");\r
-\r
-    auto_ptr<QName> type(XMLHelper::getXSIType(domElement));\r
-\r
-    if (type.get() && m_targetQName==*(type.get())) {\r
-        XT_log.debug("schema type of element matches target");\r
-        return;\r
-    }\r
-    else {\r
-        if (m_targetQName==*(elementName.get())) {\r
-            XT_log.debug("element name matches target");\r
-            return;\r
-        }\r
-        else {\r
-            XT_log.errorStream() << "unmarshaller for (" << m_targetQName.toString()\r
-                << ") passed (" << elementName->toString() << ")" << CategoryStream::ENDLINE;\r
-            throw UnmarshallingException("Incorrect element type passed to unmarshaller.");\r
-        }\r
-    }\r
-}\r
-\r
 XMLObject* AbstractXMLObjectUnmarshaller::buildXMLObject(const DOMElement* domElement) const\r
 {\r
     const XMLObjectBuilder* xmlObjectBuilder = XMLObjectBuilder::getBuilder(domElement);\r
@@ -197,8 +168,13 @@ void AbstractXMLObjectUnmarshaller::unmarshallChildElements(const DOMElement* do
                     unmarshaller=Unmarshaller::getDefaultUnmarshaller();\r
                     if (!unmarshaller) {\r
                         auto_ptr<QName> cname(XMLHelper::getNodeQName(childNode));\r
-                        XT_log.error("no default unmarshaller installed, detected unknown child element %s", cname->toString().c_str());\r
-                        throw UnmarshallingException("Unmarshaller detected unknown child element, but no default unmarshaller was found.");\r
+                        XT_log.error(\r
+                            "no default unmarshaller installed, found unknown child element %s",\r
+                            cname->toString().c_str()\r
+                            );\r
+                        throw UnmarshallingException(\r
+                            "Unmarshaller found unknown child element, but no default unmarshaller was found."\r
+                            );\r
                     }\r
                     else {\r
                         XT_log.debug("using default unmarshaller");\r
@@ -207,7 +183,7 @@ void AbstractXMLObjectUnmarshaller::unmarshallChildElements(const DOMElement* do
                 else {\r
                     auto_ptr<QName> cname(XMLHelper::getNodeQName(childNode));\r
                     XT_log.error("detected unknown child element %s", cname->toString().c_str());\r
-                    throw UnknownElementException("Unmarshaller detected unknown child element.");\r
+                    throw UnknownElementException("Unmarshaller found unknown child element.");\r
                 }\r
             }\r
 \r
index fb45191..a0ec6f4 100644 (file)
@@ -23,7 +23,6 @@
 #if !defined(__xmltooling_xmlunmarshaller_h__)\r
 #define __xmltooling_xmlunmarshaller_h__\r
 \r
-#include <xmltooling/XMLObject.h>\r
 #include <xmltooling/io/Unmarshaller.h>\r
 \r
 namespace xmltooling {\r
@@ -54,16 +53,6 @@ namespace xmltooling {
         AbstractXMLObjectUnmarshaller(const XMLCh* targetNamespaceURI, const XMLCh* targetLocalName);\r
 \r
         /**\r
-         * Checks that the given DOM Element's XSI type or namespace qualified element name matches the target QName of this\r
-         * unmarshaller.\r
-         * \r
-         * @param domElement the DOM element to check\r
-         * \r
-         * @throws UnmarshallingException thrown if the DOM Element does not match the target of this unmarshaller\r
-         */\r
-        void checkElementIsTarget(const DOMElement* domElement) const;\r
-        \r
-        /**\r
          * Constructs the XMLObject that the given DOM Element will be unmarshalled into. If the DOM element has an XML\r
          * Schema type defined this method will attempt to retrieve an XMLObjectBuilder using the schema type. If no\r
          * schema type is present or no builder is registered for the schema type, the element's QName is used. Once\r
index 6f02046..230a8fc 100644 (file)
                                Name="io"\r
                                >\r
                                <File\r
+                                       RelativePath=".\io\AbstractXMLObjectMarshaller.cpp"\r
+                                       >\r
+                               </File>\r
+                               <File\r
                                        RelativePath=".\io\AbstractXMLObjectUnmarshaller.cpp"\r
                                        >\r
                                </File>\r
                                Name="io"\r
                                >\r
                                <File\r
+                                       RelativePath=".\io\AbstractXMLObjectMarshaller.h"\r
+                                       >\r
+                               </File>\r
+                               <File\r
                                        RelativePath=".\io\AbstractXMLObjectUnmarshaller.h"\r
                                        >\r
                                </File>\r