12e03270aa02fc4b13671aa19c9a423b686817a1
[shibboleth/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("found xsi:type declaration, setting the schema type of the XMLObject");\r
121             auto_ptr<QName> xsitype(XMLHelper::getAttributeValueAsQName(attribute));\r
122             setSchemaType(xsitype.get());\r
123             continue;\r
124         }\r
125         else if (nsuri && !XMLString::equals(nsuri,XMLConstants::XML_NS)) {\r
126             XT_log.debug("found namespace-qualified attribute, adding prefix to the list of namespaces on the XMLObject");\r
127             addNamespace(Namespace(nsuri, attribute->getPrefix()));\r
128         }\r
129 \r
130         XT_log.debug("processing generic attribute");\r
131         processAttribute(attribute);\r
132     }\r
133 }\r
134 \r
135 void AbstractXMLObjectUnmarshaller::unmarshallChildElements(const DOMElement* domElement)\r
136 {\r
137 #ifdef _DEBUG\r
138     xmltooling::NDC ndc("unmarshallChildElements");\r
139 #endif\r
140 \r
141     if (XT_log.isDebugEnabled()) {\r
142         auto_ptr_char dname(domElement->getNodeName());\r
143         XT_log.debug("unmarshalling child elements of DOM element (%s)", dname.get());\r
144     }\r
145 \r
146     DOMNodeList* childNodes = domElement->getChildNodes();\r
147     DOMNode* childNode;\r
148     if (!childNodes || childNodes->getLength()==0) {\r
149         XT_log.debug("element had no children");\r
150         return;\r
151     }\r
152 \r
153     XMLToolingConfig& config=XMLToolingConfig::getConfig();\r
154     for (XMLSize_t i = 0; i < childNodes->getLength(); i++) {\r
155         childNode = childNodes->item(i);\r
156         if (childNode->getNodeType() == DOMNode::ELEMENT_NODE) {\r
157             const XMLObjectBuilder* builder = XMLObjectBuilder::getBuilder(static_cast<DOMElement*>(childNode));\r
158             if (!builder) {\r
159                 auto_ptr<QName> cname(XMLHelper::getNodeQName(childNode));\r
160                 XT_log.error("no default builder installed, found unknown child element (%s)", cname->toString().c_str());\r
161                 throw UnmarshallingException("Unmarshaller found unknown child element, but no default builder was found.");\r
162             }\r
163 \r
164             if (XT_log.isDebugEnabled()) {\r
165                 auto_ptr<QName> cname(XMLHelper::getNodeQName(childNode));\r
166                 XT_log.debug("unmarshalling child element (%s)", cname->toString().c_str());\r
167             }\r
168 \r
169             // Retain ownership of the unmarshalled child until it's processed by the parent.\r
170             auto_ptr<XMLObject> childObject(builder->buildFromElement(static_cast<DOMElement*>(childNode)));\r
171             processChildElement(childObject.get(), static_cast<DOMElement*>(childNode));\r
172             childObject.release();\r
173         }\r
174         else if (childNode->getNodeType() == DOMNode::TEXT_NODE) {\r
175             XT_log.debug("processing element content");\r
176             processElementContent(childNode->getNodeValue());\r
177         }\r
178     }\r
179 }\r