First cut at signing support.
[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()\r
42     : m_log(&Category::getInstance(XMLTOOLING_LOGCAT".Unmarshaller")) {}\r
43 \r
44 XMLObject* AbstractXMLObjectUnmarshaller::unmarshall(DOMElement* element, bool bindDocument) const\r
45 {\r
46 #ifdef _DEBUG\r
47     xmltooling::NDC ndc("unmarshall");\r
48 #endif\r
49 \r
50     if (XT_log.isDebugEnabled()) {\r
51         auto_ptr_char dname(element->getNodeName());\r
52         XT_log.debug("unmarshalling DOM element (%s)", dname.get());\r
53     }\r
54 \r
55     auto_ptr<XMLObject> xmlObject(buildXMLObject(element));\r
56 \r
57     if (element->hasAttributes()) {\r
58         unmarshallAttributes(element, *(xmlObject.get()));\r
59     }\r
60 \r
61     unmarshallChildElements(element, *(xmlObject.get()));\r
62 \r
63     /* TODO: Signing\r
64     if (xmlObject instanceof SignableXMLObject) {\r
65         verifySignature(domElement, xmlObject);\r
66     }\r
67     */\r
68 \r
69     DOMCachingXMLObject* dc=dynamic_cast<DOMCachingXMLObject*>(xmlObject.get());\r
70     if (dc)\r
71         dc->setDOM(element,bindDocument);\r
72     else if (bindDocument)\r
73         throw UnmarshallingException("Unable to bind document to non-DOM caching XMLObject instance.");\r
74         \r
75     return xmlObject.release();\r
76 }\r
77 \r
78 XMLObject* AbstractXMLObjectUnmarshaller::buildXMLObject(const DOMElement* domElement) const\r
79 {\r
80     const XMLObjectBuilder* xmlObjectBuilder = XMLObjectBuilder::getBuilder(domElement);\r
81     if (xmlObjectBuilder)\r
82         return xmlObjectBuilder->buildObject();\r
83     throw UnmarshallingException("Failed to locate XMLObjectBuilder for element.");\r
84 }\r
85 \r
86 void AbstractXMLObjectUnmarshaller::unmarshallAttributes(const DOMElement* domElement, XMLObject& xmlObject) const\r
87 {\r
88 #ifdef _DEBUG\r
89     xmltooling::NDC ndc("unmarshallAttributes");\r
90 #endif\r
91     static const XMLCh type[]={chLatin_t, chLatin_y, chLatin_p, chLatin_e, chNull};\r
92 \r
93     if (XT_log.isDebugEnabled()) {\r
94         auto_ptr_char dname(domElement->getNodeName());\r
95         XT_log.debug("unmarshalling attributes for DOM element (%s)", dname.get());\r
96     }\r
97 \r
98     DOMNamedNodeMap* attributes = domElement->getAttributes();\r
99     if (!attributes) {\r
100         XT_log.debug("no attributes to unmarshall");\r
101         return;\r
102     }\r
103 \r
104     DOMNode* childNode;\r
105     DOMAttr* attribute;\r
106     for (XMLSize_t i=0; i<attributes->getLength(); i++) {\r
107         childNode = attributes->item(i);\r
108 \r
109         // The child node should always be an attribute, but just in case\r
110         if (childNode->getNodeType() != DOMNode::ATTRIBUTE_NODE) {\r
111             XT_log.debug("encountered child node of type %d in attribute list, ignoring it", childNode->getNodeType());\r
112             continue;\r
113         }\r
114 \r
115         attribute = static_cast<DOMAttr*>(childNode);\r
116         \r
117         const XMLCh* nsuri=attribute->getNamespaceURI();\r
118         if (XMLString::equals(nsuri,XMLConstants::XMLNS_NS)) {\r
119             if (XMLString::equals(attribute->getLocalName(),XMLConstants::XMLNS_PREFIX)) {\r
120                 XT_log.debug("found default namespace declaration, adding it to the list of namespaces on the XMLObject");\r
121                 xmlObject.addNamespace(Namespace(attribute->getValue(), NULL, true));\r
122                 continue;\r
123             }\r
124             else {\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         }\r
130         else if (XMLString::equals(nsuri,XMLConstants::XSI_NS) && XMLString::equals(attribute->getLocalName(),type)) {\r
131             XT_log.debug("found xsi:type declaration, setting the schema type of the XMLObject");\r
132             auto_ptr<QName> xsitype(XMLHelper::getAttributeValueAsQName(attribute));\r
133             xmlObject.setSchemaType(xsitype.get());\r
134             continue;\r
135         }\r
136         else if (nsuri && !XMLString::equals(nsuri,XMLConstants::XML_NS)) {\r
137             XT_log.debug("found namespace-qualified attribute, adding prefix to the list of namespaces on the XMLObject");\r
138             xmlObject.addNamespace(Namespace(nsuri, attribute->getPrefix()));\r
139         }\r
140 \r
141         XT_log.debug("processing generic attribute");\r
142         processAttribute(xmlObject, attribute);\r
143     }\r
144 }\r
145 \r
146 void AbstractXMLObjectUnmarshaller::unmarshallChildElements(const DOMElement* domElement, XMLObject& xmlObject) const\r
147 {\r
148 #ifdef _DEBUG\r
149     xmltooling::NDC ndc("unmarshallChildElements");\r
150 #endif\r
151 \r
152     if (XT_log.isDebugEnabled()) {\r
153         auto_ptr_char dname(domElement->getNodeName());\r
154         XT_log.debug("unmarshalling child elements of DOM element (%s)", dname.get());\r
155     }\r
156 \r
157     DOMNodeList* childNodes = domElement->getChildNodes();\r
158     DOMNode* childNode;\r
159     const Unmarshaller* unmarshaller;\r
160     if (!childNodes || childNodes->getLength()==0) {\r
161         XT_log.debug("element had no children");\r
162         return;\r
163     }\r
164 \r
165     XMLToolingConfig& config=XMLToolingConfig::getConfig();\r
166     for (XMLSize_t i = 0; i < childNodes->getLength(); i++) {\r
167         childNode = childNodes->item(i);\r
168         if (childNode->getNodeType() == DOMNode::ELEMENT_NODE) {\r
169             unmarshaller = Unmarshaller::getUnmarshaller(static_cast<DOMElement*>(childNode));\r
170             if (!unmarshaller) {\r
171                 auto_ptr<QName> cname(XMLHelper::getNodeQName(childNode));\r
172                 XT_log.error(\r
173                     "no default unmarshaller installed, found unknown child element (%s)", cname->toString().c_str()\r
174                     );\r
175                 throw UnmarshallingException("Unmarshaller found unknown child element, but no default unmarshaller was found.");\r
176             }\r
177 \r
178             if (XT_log.isDebugEnabled()) {\r
179                 auto_ptr<QName> cname(XMLHelper::getNodeQName(childNode));\r
180                 XT_log.debug("unmarshalling child element (%s)", cname->toString().c_str());\r
181             }\r
182 \r
183             // Retain ownership of the unmarshalled child until it's processed by the parent.\r
184             auto_ptr<XMLObject> childObject(unmarshaller->unmarshall(static_cast<DOMElement*>(childNode)));\r
185             processChildElement(xmlObject, childObject.get(), static_cast<DOMElement*>(childNode));\r
186             childObject.release();\r
187         }\r
188         else if (childNode->getNodeType() == DOMNode::TEXT_NODE) {\r
189             XT_log.debug("processing element content");\r
190             processElementContent(xmlObject, childNode->getNodeValue());\r
191         }\r
192     }\r
193 }\r