Fixed inheritance hierarchy, namespace handling
authorScott Cantor <cantor.2@osu.edu>
Thu, 2 Mar 2006 21:37:07 +0000 (21:37 +0000)
committerScott Cantor <cantor.2@osu.edu>
Thu, 2 Mar 2006 21:37:07 +0000 (21:37 +0000)
xmltooling/AbstractDOMCachingXMLObject.h
xmltooling/AbstractXMLObject.h
xmltooling/io/AbstractXMLObjectMarshaller.cpp
xmltooling/io/AbstractXMLObjectUnmarshaller.cpp
xmltoolingtest/UnmarshallingTest.h
xmltoolingtest/XMLObjectBaseTestCase.h
xmltoolingtest/data/SimpleXMLObjectWithUnknownChild.xml [new file with mode: 0644]
xmltoolingtest/xmltoolingtest.h
xmltoolingtest/xmltoolingtest.vcproj

index 453c634..9622d1c 100644 (file)
@@ -36,7 +36,7 @@ namespace xmltooling {
     /**\r
      * Extension of AbstractXMLObject that implements a DOMCachingXMLObject.\r
      */\r
-    class XMLTOOL_API AbstractDOMCachingXMLObject : public virtual AbstractXMLObject, public virtual DOMCachingXMLObject\r
+    class XMLTOOL_API AbstractDOMCachingXMLObject : public AbstractXMLObject, public DOMCachingXMLObject\r
     {\r
     public:\r
         virtual ~AbstractDOMCachingXMLObject();\r
@@ -132,15 +132,13 @@ namespace xmltooling {
          */\r
         XMLObject* prepareForAssignment(const XMLObject* oldValue, XMLObject* newValue);\r
 \r
-        AbstractDOMCachingXMLObject() : m_dom(NULL), m_document(NULL) {}\r
-\r
         /**\r
          * Constructor\r
          * \r
          * @param namespaceURI the namespace the element is in\r
          * @param elementLocalName the local name of the XML element this Object represents\r
          */\r
-        AbstractDOMCachingXMLObject(const XMLCh* namespaceURI, const XMLCh* elementLocalName, const XMLCh* namespacePrefix)\r
+        AbstractDOMCachingXMLObject(const XMLCh* namespaceURI=NULL, const XMLCh* elementLocalName=NULL, const XMLCh* namespacePrefix=NULL)\r
             : AbstractXMLObject(namespaceURI,elementLocalName, namespacePrefix), m_dom(NULL), m_document(NULL) {}\r
 \r
     private:\r
index 12d8408..0eeae30 100644 (file)
@@ -120,15 +120,13 @@ namespace xmltooling {
         }\r
     \r
      protected:\r
-         AbstractXMLObject() : m_typeQname(NULL), m_parent(NULL) {}\r
-\r
         /**\r
          * Constructor\r
          * \r
          * @param namespaceURI the namespace the element is in\r
          * @param elementLocalName the local name of the XML element this Object represents\r
          */\r
-        AbstractXMLObject(const XMLCh* namespaceURI, const XMLCh* elementLocalName, const XMLCh* namespacePrefix)\r
+        AbstractXMLObject(const XMLCh* namespaceURI=NULL, const XMLCh* elementLocalName=NULL, const XMLCh* namespacePrefix=NULL)\r
             : m_elementQname(namespaceURI,elementLocalName, namespacePrefix), m_typeQname(NULL), m_parent(NULL) {\r
             addNamespace(Namespace(namespaceURI, namespacePrefix));\r
         }\r
index 2c0d537..65bc509 100644 (file)
@@ -224,9 +224,11 @@ public:
         const XMLCh* uri=ns.getNamespaceURI();\r
         \r
         // Check to see if the prefix is already declared properly above this node.\r
-        if (!ns.alwaysDeclare() && domElement->getParentNode() &&\r
-                XMLString::equals(domElement->getParentNode()->lookupNamespaceURI(prefix),uri))\r
-            return;\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
@@ -241,6 +243,32 @@ public:
             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
index ab69684..939311f 100644 (file)
@@ -126,9 +126,16 @@ void AbstractXMLObjectUnmarshaller::unmarshallAttributes(const DOMElement* domEl
         \r
         const XMLCh* nsuri=attribute->getNamespaceURI();\r
         if (XMLString::equals(nsuri,XMLConstants::XMLNS_NS)) {\r
-            XT_log.debug("found namespace declaration, adding it to the list of namespaces on the XMLObject");\r
-            xmlObject.addNamespace(Namespace(attribute->getValue(), attribute->getLocalName(), true));\r
-            continue;\r
+            if (XMLString::equals(attribute->getLocalName(),XMLConstants::XMLNS_PREFIX)) {\r
+                XT_log.debug("found default namespace declaration, adding it to the list of namespaces on the XMLObject");\r
+                xmlObject.addNamespace(Namespace(attribute->getValue(), NULL, true));\r
+                continue;\r
+            }\r
+            else {\r
+                XT_log.debug("found namespace declaration, adding it to the list of namespaces on the XMLObject");\r
+                xmlObject.addNamespace(Namespace(attribute->getValue(), attribute->getLocalName(), true));\r
+                continue;\r
+            }\r
         }\r
         else if (XMLString::equals(nsuri,XMLConstants::XSI_NS) && XMLString::equals(attribute->getLocalName(),type)) {\r
             XT_log.debug("found xsi:type declaration, setting the schema type of the XMLObject");\r
index efe505f..d4ba4f6 100644 (file)
@@ -46,20 +46,23 @@ class UnmarshallingTest : public CxxTest::TestSuite {
 public:\r
     UnmarshallingTest() : m_qname(SimpleXMLObject::NAMESPACE,SimpleXMLObject::LOCAL_NAME) {}\r
 \r
-    void setUp() {
-        XMLObjectBuilder::registerBuilder(m_qname, new SimpleXMLObjectBuilder());
-        Marshaller::registerMarshaller(m_qname, new SimpleXMLObjectMarshaller());
-        Unmarshaller::registerUnmarshaller(m_qname, new SimpleXMLObjectUnmarshaller());
-    }
-
-    void tearDown() {
-        XMLObjectBuilder::deregisterBuilder(m_qname);
-        Marshaller::deregisterMarshaller(m_qname);
-        Unmarshaller::deregisterUnmarshaller(m_qname);
-    }
+    void setUp() {\r
+        XMLObjectBuilder::registerBuilder(m_qname, new SimpleXMLObjectBuilder());\r
+        Marshaller::registerMarshaller(m_qname, new SimpleXMLObjectMarshaller());\r
+        Unmarshaller::registerUnmarshaller(m_qname, new SimpleXMLObjectUnmarshaller());\r
+    }\r
+\r
+    void tearDown() {\r
+        XMLObjectBuilder::deregisterBuilder(m_qname);\r
+        Marshaller::deregisterMarshaller(m_qname);\r
+        Unmarshaller::deregisterUnmarshaller(m_qname);\r
+    }\r
 \r
     void testUnmarshallingWithAttributes() {\r
-        ifstream fs("../xmltoolingtest/data/SimpleXMLObjectWithAttribute.xml");\r
+        TS_TRACE("testUnmarshallingWithAttributes");\r
+\r
+        string path=data_path + "SimpleXMLObjectWithAttribute.xml";\r
+        ifstream fs(path.c_str());\r
         DOMDocument* doc=nonvalidatingPool->parse(fs);\r
         TS_ASSERT(doc!=NULL);\r
 \r
@@ -69,7 +72,57 @@ public:
         auto_ptr<SimpleXMLObject> sxObject(dynamic_cast<SimpleXMLObject*>(u->unmarshall(doc->getDocumentElement(),true)));\r
         TS_ASSERT(sxObject.get()!=NULL);\r
 \r
-        auto_ptr_XMLCh expectedId("Firefly");\r
-        TSM_ASSERT_SAME_DATA("ID was not expected value", expectedId.get(), sxObject->getId(), XMLString::stringLen(expectedId.get()));\r
+        auto_ptr_XMLCh expected("Firefly");\r
+        TSM_ASSERT_SAME_DATA("ID was not expected value", expected.get(), sxObject->getId(), XMLString::stringLen(expected.get()));\r
+    }\r
+\r
+    void testUnmarshallingWithElementContent() {\r
+        TS_TRACE("testUnmarshallingWithElementContent");\r
+\r
+        string path=data_path + "SimpleXMLObjectWithContent.xml";\r
+        ifstream fs(path.c_str());\r
+        DOMDocument* doc=nonvalidatingPool->parse(fs);\r
+        TS_ASSERT(doc!=NULL);\r
+\r
+        const Unmarshaller* u = Unmarshaller::getUnmarshaller(doc->getDocumentElement());\r
+        TS_ASSERT(u!=NULL);\r
+\r
+        auto_ptr<SimpleXMLObject> sxObject(dynamic_cast<SimpleXMLObject*>(u->unmarshall(doc->getDocumentElement(),true)));\r
+        TS_ASSERT(sxObject.get()!=NULL);\r
+\r
+        auto_ptr_XMLCh expected("Sample Content");\r
+        TSM_ASSERT_SAME_DATA("Element content was not expected value", expected.get(), sxObject->getValue(), XMLString::stringLen(expected.get()));\r
+    }\r
+\r
+    void testUnmarshallingWithChildElements() {\r
+        TS_TRACE("testUnmarshallingWithChildElements");\r
+\r
+        string path=data_path + "SimpleXMLObjectWithChildren.xml";\r
+        ifstream fs(path.c_str());\r
+        DOMDocument* doc=nonvalidatingPool->parse(fs);\r
+        TS_ASSERT(doc!=NULL);\r
+\r
+        const Unmarshaller* u = Unmarshaller::getUnmarshaller(doc->getDocumentElement());\r
+        TS_ASSERT(u!=NULL);\r
+\r
+        auto_ptr<SimpleXMLObject> sxObject(dynamic_cast<SimpleXMLObject*>(u->unmarshall(doc->getDocumentElement(),true)));\r
+        TS_ASSERT(sxObject.get()!=NULL);\r
+\r
+        TSM_ASSERT_EQUALS("Number of child elements was not expected value", 2, sxObject->getSimpleXMLObjects().size());\r
+    }\r
+\r
+    void testUnmarshallingWithUnknownChild() {\r
+        TS_TRACE("testUnmarshallingWithUnknownChild");\r
+\r
+        string path=data_path + "SimpleXMLObjectWithUnknownChild.xml";\r
+        ifstream fs(path.c_str());\r
+        DOMDocument* doc=nonvalidatingPool->parse(fs);\r
+        TS_ASSERT(doc!=NULL);\r
+\r
+        const Unmarshaller* u = Unmarshaller::getUnmarshaller(doc->getDocumentElement());\r
+        TS_ASSERT(u!=NULL);\r
+\r
+        TS_ASSERT_THROWS(u->unmarshall(doc->getDocumentElement(),true),UnmarshallingException);\r
+        doc->release();\r
     }\r
 };\r
index d0fb2d0..ff3f3a8 100644 (file)
@@ -29,6 +29,7 @@ using namespace std;
 \r
 extern ParserPool* validatingPool;\r
 extern ParserPool* nonvalidatingPool;\r
+extern string data_path;\r
 \r
 #if defined (_MSC_VER)\r
     #pragma warning( push )\r
@@ -137,6 +138,9 @@ private:
         if (XMLString::equals(attribute->getLocalName(),SimpleXMLObject::ID_ATTRIB_NAME)) {\r
             simpleXMLObject.setId(attribute->getValue());\r
         }\r
+        else {\r
+            throw UnmarshallingException("Unknown attribute cannot be processed by parent object.");\r
+        }\r
     }\r
 \r
     void processElementContent(XMLObject& xmlObject, const XMLCh* elementContent) const {\r
diff --git a/xmltoolingtest/data/SimpleXMLObjectWithUnknownChild.xml b/xmltoolingtest/data/SimpleXMLObjectWithUnknownChild.xml
new file mode 100644 (file)
index 0000000..2e8083c
Binary files /dev/null and b/xmltoolingtest/data/SimpleXMLObjectWithUnknownChild.xml differ
index f3fb5af..543758d 100644 (file)
@@ -24,6 +24,7 @@ using namespace xmltooling;
 \r
 ParserPool* validatingPool=NULL;\r
 ParserPool* nonvalidatingPool=NULL;\r
+std::string data_path = "../xmltoolingtest/data/";\r
 \r
 class ToolingFixture : public CxxTest::GlobalFixture\r
 {\r
@@ -34,6 +35,8 @@ public:
             return false;\r
         validatingPool = new ParserPool(true,true);\r
         nonvalidatingPool = new ParserPool();\r
+        if (getenv("XMLTOOLINGTEST_DATA"))\r
+            data_path=std::string(getenv("XMLTOOLINGTEST_DATA")) + "/";\r
         return true;\r
     }\r
     bool tearDownWorld() {\r
@@ -61,7 +64,8 @@ class CatalogTest : public CxxTest::TestSuite
 {\r
 public:\r
     void testCatalog(void) {\r
-        auto_ptr_XMLCh temp("../xmltoolingtest/data/catalog.xml");\r
+        std::string path=data_path + "catalog.xml";\r
+        auto_ptr_XMLCh temp(path.c_str());\r
         TS_ASSERT(validatingPool->loadCatalog(temp.get()));\r
     }\r
 };\r
index 1da845b..561606c 100644 (file)
                        UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
                        >\r
                        <File\r
+                               RelativePath=".\MarshallingTest.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
                                RelativePath=".\UnknownTest.cpp"\r
                                >\r
                        </File>\r
                        UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
                        >\r
                        <File\r
+                               RelativePath=".\MarshallingTest.h"\r
+                               >\r
+                               <FileConfiguration\r
+                                       Name="Debug|Win32"\r
+                                       >\r
+                                       <Tool\r
+                                               Name="VCCustomBuildTool"\r
+                                               CommandLine="\perl\bin\perl.exe -w \cxxtest\cxxtestgen.pl --part --have-eh --have-std --abort-on-fail -o &quot;$(InputName)&quot;.cpp &quot;$(InputPath)&quot;"\r
+                                               Outputs="&quot;$(InputName)&quot;.cpp"\r
+                                       />\r
+                               </FileConfiguration>\r
+                               <FileConfiguration\r
+                                       Name="Release|Win32"\r
+                                       >\r
+                                       <Tool\r
+                                               Name="VCCustomBuildTool"\r
+                                               CommandLine="\perl\bin\perl.exe -w \cxxtest\cxxtestgen.pl --part --have-eh --have-std --abort-on-fail -o &quot;$(InputName)&quot;.cpp &quot;$(InputPath)&quot;"\r
+                                               Outputs="&quot;$(InputName)&quot;.cpp"\r
+                                       />\r
+                               </FileConfiguration>\r
+                       </File>\r
+                       <File\r
                                RelativePath=".\UnknownTest.h"\r
                                >\r
                                <FileConfiguration\r