Abstract unmarshaller minus signatures.
[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 #ifdef _DEBUG\r
60     checkElementIsTarget(element);\r
61 #endif\r
62 \r
63     XMLObject* xmlObject = buildXMLObject(element);\r
64 \r
65     if (element->hasAttributes()) {\r
66         unmarshallAttributes(element, xmlObject);\r
67     }\r
68 \r
69     if (element->getTextContent()) {\r
70         processElementContent(xmlObject, element->getTextContent());\r
71     }\r
72 \r
73     unmarshallChildElements(element, xmlObject);\r
74 \r
75     /* TODO: Signing\r
76     if (xmlObject instanceof SignableXMLObject) {\r
77         verifySignature(domElement, xmlObject);\r
78     }\r
79     */\r
80 \r
81     DOMCachingXMLObject* dc=dynamic_cast<DOMCachingXMLObject*>(xmlObject);\r
82     if (dc)\r
83         dc->setDOM(element,bindDocument);\r
84         \r
85     return xmlObject;\r
86 }\r
87 \r
88 void AbstractXMLObjectUnmarshaller::checkElementIsTarget(const DOMElement* domElement) const\r
89 {\r
90     auto_ptr<QName> elementName(XMLHelper::getNodeQName(domElement));\r
91 \r
92     XT_log.debug("checking that root element meets target criteria");\r
93 \r
94     auto_ptr<QName> type(XMLHelper::getXSIType(domElement));\r
95 \r
96     if (type.get() && m_targetQName==*(type.get())) {\r
97         XT_log.debug("schema type of element matches target");\r
98         return;\r
99     }\r
100     else {\r
101         if (m_targetQName==*(elementName.get())) {\r
102             XT_log.debug("element name matches target");\r
103             return;\r
104         }\r
105         else {\r
106             XT_log.errorStream() << "unmarshaller for (" << m_targetQName.toString()\r
107                 << ") passed (" << elementName->toString() << ")" << CategoryStream::ENDLINE;\r
108             throw UnmarshallingException("Incorrect element type passed to unmarshaller.");\r
109         }\r
110     }\r
111 }\r
112 \r
113 XMLObject* AbstractXMLObjectUnmarshaller::buildXMLObject(const DOMElement* domElement) const\r
114 {\r
115     const XMLObjectBuilder* xmlObjectBuilder = XMLObjectBuilder::getBuilder(domElement);\r
116     if (xmlObjectBuilder)\r
117         return xmlObjectBuilder->buildObject();\r
118     throw UnmarshallingException("Failed to locate XMLObjectBuilder for element.");\r
119 }\r
120 \r
121 void AbstractXMLObjectUnmarshaller::unmarshallAttributes(const DOMElement* domElement, XMLObject* xmlObject) const\r
122 {\r
123 #ifdef _DEBUG\r
124     xmltooling::NDC ndc("unmarshallAttributes");\r
125 #endif\r
126     static const XMLCh type[]={chLatin_t, chLatin_y, chLatin_p, chLatin_e, chNull};\r
127 \r
128     if (XT_log.isDebugEnabled()) {\r
129         auto_ptr_char dname(domElement->getLocalName());\r
130         XT_log.debug("unmarshalling attributes for DOM element %s", dname.get());\r
131     }\r
132 \r
133     DOMNamedNodeMap* attributes = domElement->getAttributes();\r
134     if (!attributes) {\r
135         XT_log.debug("no attributes to unmarshall");\r
136         return;\r
137     }\r
138 \r
139     DOMNode* childNode;\r
140     DOMAttr* attribute;\r
141     for (XMLSize_t i=0; i<attributes->getLength(); i++) {\r
142         childNode = attributes->item(i);\r
143 \r
144         // The child node should always be an attribute, but just in case\r
145         if (childNode->getNodeType() != DOMNode::ATTRIBUTE_NODE) {\r
146             XT_log.debug("encountered child node of type %d in attribute list, ignoring it", childNode->getNodeType());\r
147             continue;\r
148         }\r
149 \r
150         attribute = static_cast<DOMAttr*>(childNode);\r
151         \r
152         const XMLCh* nsuri=attribute->getNamespaceURI();\r
153         if (!XMLString::compareString(nsuri,XMLConstants::XMLNS_NS)) {\r
154             XT_log.debug("found namespace declaration, adding it to the list of namespaces on the XMLObject");\r
155             xmlObject->addNamespace(Namespace(attribute->getValue(), attribute->getLocalName()));\r
156             continue;\r
157         }\r
158         else if (!XMLString::compareString(nsuri,XMLConstants::XSI_NS) &&\r
159                     !XMLString::compareString(attribute->getLocalName(),type)) {\r
160             XT_log.debug("found xsi:type declaration, setting the schema type of the XMLObject");\r
161             auto_ptr<QName> xsitype(XMLHelper::getAttributeValueAsQName(attribute));\r
162             xmlObject->setSchemaType(xsitype.get());\r
163             continue;\r
164         }\r
165 \r
166         XT_log.debug("processing generic attribute");\r
167         processAttribute(xmlObject, attribute);\r
168     }\r
169 }\r
170 \r
171 void AbstractXMLObjectUnmarshaller::unmarshallChildElements(const DOMElement* domElement, XMLObject* xmlObject) const\r
172 {\r
173 #ifdef _DEBUG\r
174     xmltooling::NDC ndc("unmarshallChildElements");\r
175 #endif\r
176 \r
177     if (XT_log.isDebugEnabled()) {\r
178         auto_ptr_char dname(domElement->getLocalName());\r
179         XT_log.debug("unmarshalling child elements of DOM element %s", dname.get());\r
180     }\r
181 \r
182     DOMNodeList* childNodes = domElement->getChildNodes();\r
183     DOMNode* childNode;\r
184     const Unmarshaller* unmarshaller;\r
185     if (!childNodes || childNodes->getLength()==0) {\r
186         XT_log.debug("element had no children");\r
187         return;\r
188     }\r
189 \r
190     XMLToolingConfig& config=XMLToolingConfig::getConfig();\r
191     for (XMLSize_t i = 0; i < childNodes->getLength(); i++) {\r
192         childNode = childNodes->item(i);\r
193         if (childNode->getNodeType() == DOMNode::ELEMENT_NODE) {\r
194             unmarshaller = Unmarshaller::getUnmarshaller(static_cast<DOMElement*>(childNode));\r
195             if (!unmarshaller) {\r
196                 if (config.ignoreUnknownElements) {\r
197                     unmarshaller=Unmarshaller::getDefaultUnmarshaller();\r
198                     if (!unmarshaller) {\r
199                         auto_ptr<QName> cname(XMLHelper::getNodeQName(childNode));\r
200                         XT_log.error("no default unmarshaller installed, detected unknown child element %s", cname->toString().c_str());\r
201                         throw UnmarshallingException("Unmarshaller detected unknown child element, but no default unmarshaller was found.");\r
202                     }\r
203                     else {\r
204                         XT_log.debug("using default unmarshaller");\r
205                     }\r
206                 }\r
207                 else {\r
208                     auto_ptr<QName> cname(XMLHelper::getNodeQName(childNode));\r
209                     XT_log.error("detected unknown child element %s", cname->toString().c_str());\r
210                     throw UnknownElementException("Unmarshaller detected unknown child element.");\r
211                 }\r
212             }\r
213 \r
214             if (XT_log.isDebugEnabled()) {\r
215                 auto_ptr<QName> cname(XMLHelper::getNodeQName(childNode));\r
216                 XT_log.debug("unmarshalling child element %s", cname->toString().c_str());\r
217             }\r
218             processChildElement(xmlObject, unmarshaller->unmarshall(static_cast<DOMElement*>(childNode)));\r
219         }\r
220     }\r
221 }\r