Set xsi:type during object construction.
[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 "exceptions.h"\r
25 #include "XMLObjectBuilder.h"\r
26 #include "io/AbstractXMLObjectUnmarshaller.h"\r
27 #include "util/NDC.h"\r
28 #include "util/XMLConstants.h"\r
29 #include "util/XMLHelper.h"\r
30 \r
31 #include <xercesc/util/XMLUniDefs.hpp>\r
32 #include <log4cpp/Category.hh>\r
33 \r
34 using namespace xmltooling;\r
35 using namespace log4cpp;\r
36 using namespace std;\r
37 \r
38 #define XT_log (*static_cast<Category*>(m_log))\r
39 \r
40 XMLObject* AbstractXMLObjectUnmarshaller::unmarshall(DOMElement* element, bool bindDocument)\r
41 {\r
42 #ifdef _DEBUG\r
43     xmltooling::NDC ndc("unmarshall");\r
44 #endif\r
45 \r
46     if (getDOM() || hasParent())\r
47         throw UnmarshallingException("Object already contains data, it cannot be unmarshalled at this stage.");\r
48 \r
49     if (!XMLString::equals(element->getNamespaceURI(),getElementQName().getNamespaceURI()) ||\r
50         !XMLString::equals(element->getLocalName(),getElementQName().getLocalPart())) {\r
51         throw UnmarshallingException("Unrecognized element supplied to implementation for unmarshalling.");\r
52     }\r
53 \r
54     if (XT_log.isDebugEnabled()) {\r
55         auto_ptr_char dname(element->getNodeName());\r
56         XT_log.debug("unmarshalling DOM element (%s)", dname.get());\r
57     }\r
58 \r
59     if (element->hasAttributes()) {\r
60         unmarshallAttributes(element);\r
61     }\r
62 \r
63     unmarshallChildElements(element);\r
64 \r
65     /* TODO: Signing\r
66     if (xmlObject instanceof SignableXMLObject) {\r
67         verifySignature(domElement, xmlObject);\r
68     }\r
69     */\r
70 \r
71     setDOM(element,bindDocument);\r
72     return this;\r
73 }\r
74 \r
75 void AbstractXMLObjectUnmarshaller::unmarshallAttributes(const DOMElement* domElement)\r
76 {\r
77 #ifdef _DEBUG\r
78     xmltooling::NDC ndc("unmarshallAttributes");\r
79 #endif\r
80     static const XMLCh type[]={chLatin_t, chLatin_y, chLatin_p, chLatin_e, chNull};\r
81 \r
82     if (XT_log.isDebugEnabled()) {\r
83         auto_ptr_char dname(domElement->getNodeName());\r
84         XT_log.debug("unmarshalling attributes for DOM element (%s)", dname.get());\r
85     }\r
86 \r
87     DOMNamedNodeMap* attributes = domElement->getAttributes();\r
88     if (!attributes) {\r
89         XT_log.debug("no attributes to unmarshall");\r
90         return;\r
91     }\r
92 \r
93     DOMNode* childNode;\r
94     DOMAttr* attribute;\r
95     for (XMLSize_t i=0; i<attributes->getLength(); i++) {\r
96         childNode = attributes->item(i);\r
97 \r
98         // The child node should always be an attribute, but just in case\r
99         if (childNode->getNodeType() != DOMNode::ATTRIBUTE_NODE) {\r
100             XT_log.debug("encountered child node of type %d in attribute list, ignoring it", childNode->getNodeType());\r
101             continue;\r
102         }\r
103 \r
104         attribute = static_cast<DOMAttr*>(childNode);\r
105         \r
106         const XMLCh* nsuri=attribute->getNamespaceURI();\r
107         if (XMLString::equals(nsuri,XMLConstants::XMLNS_NS)) {\r
108             if (XMLString::equals(attribute->getLocalName(),XMLConstants::XMLNS_PREFIX)) {\r
109                 XT_log.debug("found default namespace declaration, adding it to the list of namespaces on the XMLObject");\r
110                 addNamespace(Namespace(attribute->getValue(), NULL, true));\r
111                 continue;\r
112             }\r
113             else {\r
114                 XT_log.debug("found namespace declaration, adding it to the list of namespaces on the XMLObject");\r
115                 addNamespace(Namespace(attribute->getValue(), attribute->getLocalName(), true));\r
116                 continue;\r
117             }\r
118         }\r
119         else if (XMLString::equals(nsuri,XMLConstants::XSI_NS) && XMLString::equals(attribute->getLocalName(),type)) {\r
120             XT_log.debug("skipping xsi:type declaration");\r
121             continue;\r
122         }\r
123         else if (nsuri && !XMLString::equals(nsuri,XMLConstants::XML_NS)) {\r
124             XT_log.debug("found namespace-qualified attribute, adding prefix to the list of namespaces on the XMLObject");\r
125             addNamespace(Namespace(nsuri, attribute->getPrefix()));\r
126         }\r
127 \r
128         XT_log.debug("processing generic attribute");\r
129         processAttribute(attribute);\r
130     }\r
131 }\r
132 \r
133 void AbstractXMLObjectUnmarshaller::unmarshallChildElements(const DOMElement* domElement)\r
134 {\r
135 #ifdef _DEBUG\r
136     xmltooling::NDC ndc("unmarshallChildElements");\r
137 #endif\r
138 \r
139     if (XT_log.isDebugEnabled()) {\r
140         auto_ptr_char dname(domElement->getNodeName());\r
141         XT_log.debug("unmarshalling child elements of DOM element (%s)", dname.get());\r
142     }\r
143 \r
144     DOMNodeList* childNodes = domElement->getChildNodes();\r
145     DOMNode* childNode;\r
146     if (!childNodes || childNodes->getLength()==0) {\r
147         XT_log.debug("element had no children");\r
148         return;\r
149     }\r
150 \r
151     XMLToolingConfig& config=XMLToolingConfig::getConfig();\r
152     for (XMLSize_t i = 0; i < childNodes->getLength(); i++) {\r
153         childNode = childNodes->item(i);\r
154         if (childNode->getNodeType() == DOMNode::ELEMENT_NODE) {\r
155             const XMLObjectBuilder* builder = XMLObjectBuilder::getBuilder(static_cast<DOMElement*>(childNode));\r
156             if (!builder) {\r
157                 auto_ptr<QName> cname(XMLHelper::getNodeQName(childNode));\r
158                 XT_log.error("no default builder installed, found unknown child element (%s)", cname->toString().c_str());\r
159                 throw UnmarshallingException("Unmarshaller found unknown child element, but no default builder was found.");\r
160             }\r
161 \r
162             if (XT_log.isDebugEnabled()) {\r
163                 auto_ptr<QName> cname(XMLHelper::getNodeQName(childNode));\r
164                 XT_log.debug("unmarshalling child element (%s)", cname->toString().c_str());\r
165             }\r
166 \r
167             // Retain ownership of the unmarshalled child until it's processed by the parent.\r
168             auto_ptr<XMLObject> childObject(builder->buildFromElement(static_cast<DOMElement*>(childNode)));\r
169             processChildElement(childObject.get(), static_cast<DOMElement*>(childNode));\r
170             childObject.release();\r
171         }\r
172         else if (childNode->getNodeType() == DOMNode::TEXT_NODE) {\r
173             XT_log.debug("processing element content");\r
174             processElementContent(childNode->getNodeValue());\r
175         }\r
176     }\r
177 }\r