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