Initial unit test plus fixes
[shibboleth/cpp-xmltooling.git] / xmltooling / io / AbstractXMLObjectUnmarshaller.cpp
1 /*\r
2 *  Copyright 2001-2006 Internet2\r
3  * \r
4 * Licensed under the Apache License, Version 2.0 (the "License");\r
5  * you may not use this file except in compliance with the License.\r
6  * You may obtain a copy of the License at\r
7  *\r
8  *     http://www.apache.org/licenses/LICENSE-2.0\r
9  *\r
10  * Unless required by applicable law or agreed to in writing, software\r
11  * distributed under the License is distributed on an "AS IS" BASIS,\r
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
13  * See the License for the specific language governing permissions and\r
14  * limitations under the License.\r
15  */\r
16 \r
17 /**\r
18  * AbstractXMLObjectUnmarshaller.cpp\r
19  * \r
20  * A thread-safe abstract unmarshaller.\r
21  */\r
22 \r
23 #include "internal.h"\r
24 #include "DOMCachingXMLObject.h"\r
25 #include "exceptions.h"\r
26 #include "XMLObjectBuilder.h"\r
27 #include "io/AbstractXMLObjectUnmarshaller.h"\r
28 #include "util/NDC.h"\r
29 #include "util/XMLConstants.h"\r
30 #include "util/XMLHelper.h"\r
31 \r
32 #include <xercesc/util/XMLUniDefs.hpp>\r
33 #include <log4cpp/Category.hh>\r
34 \r
35 using namespace xmltooling;\r
36 using namespace log4cpp;\r
37 using namespace std;\r
38 \r
39 #define XT_log (*static_cast<Category*>(m_log))\r
40 \r
41 AbstractXMLObjectUnmarshaller::AbstractXMLObjectUnmarshaller(const XMLCh* targetNamespaceURI, const XMLCh* targetLocalName)\r
42         : m_targetQName(targetNamespaceURI, targetLocalName),\r
43         m_log(&Category::getInstance(XMLTOOLING_LOGCAT".Unmarshaller")) {\r
44     if (!targetLocalName || !*targetLocalName)\r
45         throw UnmarshallingException("targetLocalName cannot be null or empty");\r
46 }\r
47 \r
48 XMLObject* AbstractXMLObjectUnmarshaller::unmarshall(DOMElement* element, bool bindDocument) const\r
49 {\r
50 #ifdef _DEBUG\r
51     xmltooling::NDC ndc("unmarshall");\r
52 #endif\r
53 \r
54     if (XT_log.isDebugEnabled()) {\r
55         auto_ptr_char dname(element->getLocalName());\r
56         XT_log.debug("unmarshalling DOM element %s", dname.get());\r
57     }\r
58 \r
59     XMLObject* xmlObject = buildXMLObject(element);\r
60 \r
61     if (element->hasAttributes()) {\r
62         unmarshallAttributes(element, xmlObject);\r
63     }\r
64 \r
65     if (element->getTextContent()) {\r
66         processElementContent(xmlObject, element->getTextContent());\r
67     }\r
68 \r
69     unmarshallChildElements(element, xmlObject);\r
70 \r
71     /* TODO: Signing\r
72     if (xmlObject instanceof SignableXMLObject) {\r
73         verifySignature(domElement, xmlObject);\r
74     }\r
75     */\r
76 \r
77     DOMCachingXMLObject* dc=dynamic_cast<DOMCachingXMLObject*>(xmlObject);\r
78     if (dc)\r
79         dc->setDOM(element,bindDocument);\r
80         \r
81     return xmlObject;\r
82 }\r
83 \r
84 XMLObject* AbstractXMLObjectUnmarshaller::buildXMLObject(const DOMElement* domElement) const\r
85 {\r
86     const XMLObjectBuilder* xmlObjectBuilder = XMLObjectBuilder::getBuilder(domElement);\r
87     if (xmlObjectBuilder)\r
88         return xmlObjectBuilder->buildObject();\r
89     throw UnmarshallingException("Failed to locate XMLObjectBuilder for element.");\r
90 }\r
91 \r
92 void AbstractXMLObjectUnmarshaller::unmarshallAttributes(const DOMElement* domElement, XMLObject* xmlObject) const\r
93 {\r
94 #ifdef _DEBUG\r
95     xmltooling::NDC ndc("unmarshallAttributes");\r
96 #endif\r
97     static const XMLCh type[]={chLatin_t, chLatin_y, chLatin_p, chLatin_e, chNull};\r
98 \r
99     if (XT_log.isDebugEnabled()) {\r
100         auto_ptr_char dname(domElement->getLocalName());\r
101         XT_log.debug("unmarshalling attributes for DOM element %s", dname.get());\r
102     }\r
103 \r
104     DOMNamedNodeMap* attributes = domElement->getAttributes();\r
105     if (!attributes) {\r
106         XT_log.debug("no attributes to unmarshall");\r
107         return;\r
108     }\r
109 \r
110     DOMNode* childNode;\r
111     DOMAttr* attribute;\r
112     for (XMLSize_t i=0; i<attributes->getLength(); i++) {\r
113         childNode = attributes->item(i);\r
114 \r
115         // The child node should always be an attribute, but just in case\r
116         if (childNode->getNodeType() != DOMNode::ATTRIBUTE_NODE) {\r
117             XT_log.debug("encountered child node of type %d in attribute list, ignoring it", childNode->getNodeType());\r
118             continue;\r
119         }\r
120 \r
121         attribute = static_cast<DOMAttr*>(childNode);\r
122         \r
123         const XMLCh* nsuri=attribute->getNamespaceURI();\r
124         if (XMLString::equals(nsuri,XMLConstants::XMLNS_NS)) {\r
125             XT_log.debug("found namespace declaration, adding it to the list of namespaces on the XMLObject");\r
126             xmlObject->addNamespace(Namespace(attribute->getValue(), attribute->getLocalName(), true));\r
127             continue;\r
128         }\r
129         else if (XMLString::equals(nsuri,XMLConstants::XSI_NS) && XMLString::equals(attribute->getLocalName(),type)) {\r
130             XT_log.debug("found xsi:type declaration, setting the schema type of the XMLObject");\r
131             auto_ptr<QName> xsitype(XMLHelper::getAttributeValueAsQName(attribute));\r
132             xmlObject->setSchemaType(xsitype.get());\r
133             continue;\r
134         }\r
135         else if (nsuri) {\r
136             XT_log.debug("found namespace-qualified attribute, adding prefix to the list of namespaces on the XMLObject");\r
137             xmlObject->addNamespace(Namespace(nsuri, attribute->getPrefix()));\r
138         }\r
139 \r
140         XT_log.debug("processing generic attribute");\r
141         processAttribute(xmlObject, attribute);\r
142     }\r
143 }\r
144 \r
145 void AbstractXMLObjectUnmarshaller::unmarshallChildElements(const DOMElement* domElement, XMLObject* xmlObject) const\r
146 {\r
147 #ifdef _DEBUG\r
148     xmltooling::NDC ndc("unmarshallChildElements");\r
149 #endif\r
150 \r
151     if (XT_log.isDebugEnabled()) {\r
152         auto_ptr_char dname(domElement->getLocalName());\r
153         XT_log.debug("unmarshalling child elements of DOM element %s", dname.get());\r
154     }\r
155 \r
156     DOMNodeList* childNodes = domElement->getChildNodes();\r
157     DOMNode* childNode;\r
158     const Unmarshaller* unmarshaller;\r
159     if (!childNodes || childNodes->getLength()==0) {\r
160         XT_log.debug("element had no children");\r
161         return;\r
162     }\r
163 \r
164     XMLToolingConfig& config=XMLToolingConfig::getConfig();\r
165     for (XMLSize_t i = 0; i < childNodes->getLength(); i++) {\r
166         childNode = childNodes->item(i);\r
167         if (childNode->getNodeType() == DOMNode::ELEMENT_NODE) {\r
168             unmarshaller = Unmarshaller::getUnmarshaller(static_cast<DOMElement*>(childNode));\r
169             if (!unmarshaller) {\r
170                 auto_ptr<QName> cname(XMLHelper::getNodeQName(childNode));\r
171                 XT_log.error(\r
172                     "no default unmarshaller installed, found unknown child element %s", cname->toString().c_str()\r
173                     );\r
174                 throw UnmarshallingException("Unmarshaller found unknown child element, but no default unmarshaller was found.");\r
175             }\r
176 \r
177             if (XT_log.isDebugEnabled()) {\r
178                 auto_ptr<QName> cname(XMLHelper::getNodeQName(childNode));\r
179                 XT_log.debug("unmarshalling child element %s", cname->toString().c_str());\r
180             }\r
181             processChildElement(xmlObject, unmarshaller->unmarshall(static_cast<DOMElement*>(childNode)));\r
182         }\r
183     }\r
184 }\r