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