2 * Copyright 2001-2007 Internet2
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * AbstractXMLObjectUnmarshaller.cpp
20 * A thread-safe abstract unmarshaller.
24 #include "exceptions.h"
25 #include "XMLObjectBuilder.h"
26 #include "io/AbstractXMLObjectUnmarshaller.h"
28 #include "util/XMLConstants.h"
29 #include "util/XMLHelper.h"
31 #include <xercesc/util/XMLUniDefs.hpp>
33 using namespace xmlconstants;
34 using namespace xmltooling;
35 using namespace xercesc;
39 XMLObject* AbstractXMLObjectUnmarshaller::unmarshall(DOMElement* element, bool bindDocument)
42 xmltooling::NDC ndc("unmarshall");
45 if (getDOM() || hasParent())
46 throw UnmarshallingException("Object already contains data, it cannot be unmarshalled at this stage.");
48 if (!XMLString::equals(element->getNamespaceURI(),getElementQName().getNamespaceURI()) ||
49 !XMLString::equals(element->getLocalName(),getElementQName().getLocalPart())) {
50 throw UnmarshallingException("Unrecognized element supplied to implementation for unmarshalling.");
53 if (m_log.isDebugEnabled()) {
54 auto_ptr_char dname(element->getNodeName());
55 m_log.debug("unmarshalling DOM element (%s)", dname.get());
58 if (element->hasAttributes()) {
59 unmarshallAttributes(element);
62 unmarshallContent(element);
64 setDOM(element,bindDocument);
68 void AbstractXMLObjectUnmarshaller::unmarshallAttributes(const DOMElement* domElement)
71 xmltooling::NDC ndc("unmarshallAttributes");
74 if (m_log.isDebugEnabled()) {
75 auto_ptr_char dname(domElement->getNodeName());
76 m_log.debug("unmarshalling attributes for DOM element (%s)", dname.get());
79 DOMNamedNodeMap* attributes = domElement->getAttributes();
81 m_log.debug("no attributes to unmarshall");
87 for (XMLSize_t i=0; i<attributes->getLength(); i++) {
88 childNode = attributes->item(i);
90 // The child node should always be an attribute, but just in case
91 if (childNode->getNodeType() != DOMNode::ATTRIBUTE_NODE) {
92 m_log.debug("encountered child node of type %d in attribute list, ignoring it", childNode->getNodeType());
96 attribute = static_cast<DOMAttr*>(childNode);
98 const XMLCh* nsuri=attribute->getNamespaceURI();
99 if (XMLString::equals(nsuri,XMLNS_NS)) {
100 if (XMLString::equals(attribute->getLocalName(),XMLNS_PREFIX)) {
101 m_log.debug("found default namespace declaration, adding it to the list of namespaces on the XMLObject");
102 addNamespace(Namespace(attribute->getValue(), NULL, true));
106 m_log.debug("found namespace declaration, adding it to the list of namespaces on the XMLObject");
107 addNamespace(Namespace(attribute->getValue(), attribute->getLocalName(), true));
111 else if (XMLString::equals(nsuri,XSI_NS)) {
112 static const XMLCh type[]= UNICODE_LITERAL_4(t,y,p,e);
113 static const XMLCh schemaLocation[]= UNICODE_LITERAL_14(s,c,h,e,m,a,L,o,c,a,t,i,o,n);
114 static const XMLCh noNamespaceSchemaLocation[]= UNICODE_LITERAL_25(n,o,N,a,m,e,s,p,a,c,e,S,c,h,e,m,a,L,o,c,a,t,i,o,n);
115 static const XMLCh _nil[]= UNICODE_LITERAL_3(n,i,l);
116 if (XMLString::equals(attribute->getLocalName(),type)) {
117 m_log.debug("skipping xsi:type declaration");
120 else if (XMLString::equals(attribute->getLocalName(),schemaLocation)) {
121 m_log.debug("storing off xsi:schemaLocation attribute");
122 if (m_schemaLocation)
123 XMLString::release(&m_schemaLocation);
124 m_schemaLocation=XMLString::replicate(attribute->getValue());
127 else if (XMLString::equals(attribute->getLocalName(),noNamespaceSchemaLocation)) {
128 m_log.debug("storing off xsi:noNamespaceSchemaLocation attribute");
129 if (m_noNamespaceSchemaLocation)
130 XMLString::release(&m_noNamespaceSchemaLocation);
131 m_schemaLocation=XMLString::replicate(attribute->getValue());
132 m_noNamespaceSchemaLocation=XMLString::replicate(attribute->getValue());
135 else if (XMLString::equals(attribute->getLocalName(), _nil)) {
136 m_log.debug("processing xsi:nil attribute");
137 setNil(attribute->getValue());
141 else if (nsuri && !XMLString::equals(nsuri,XML_NS)) {
142 m_log.debug("found namespace-qualified attribute, adding prefix to the list of namespaces on the XMLObject");
143 addNamespace(Namespace(nsuri, attribute->getPrefix()));
146 m_log.debug("processing generic attribute");
147 processAttribute(attribute);
151 void AbstractXMLObjectUnmarshaller::unmarshallContent(const DOMElement* domElement)
154 xmltooling::NDC ndc("unmarshallContent");
157 if (m_log.isDebugEnabled()) {
158 auto_ptr_char dname(domElement->getNodeName());
159 m_log.debug("unmarshalling child nodes of DOM element (%s)", dname.get());
162 DOMNode* childNode = domElement->getFirstChild();
164 m_log.debug("element had no children");
168 unsigned int position = 0;
170 if (childNode->getNodeType() == DOMNode::ELEMENT_NODE) {
171 const XMLObjectBuilder* builder = XMLObjectBuilder::getBuilder(static_cast<DOMElement*>(childNode));
173 auto_ptr<QName> cname(XMLHelper::getNodeQName(childNode));
174 m_log.error("no default builder installed, found unknown child element (%s)", cname->toString().c_str());
175 throw UnmarshallingException("Unmarshaller found unknown child element, but no default builder was found.");
178 if (m_log.isDebugEnabled()) {
179 auto_ptr<QName> cname(XMLHelper::getNodeQName(childNode));
180 m_log.debug("unmarshalling child element (%s)", cname->toString().c_str());
183 // Retain ownership of the unmarshalled child until it's processed by the parent.
184 auto_ptr<XMLObject> childObject(builder->buildFromElement(static_cast<DOMElement*>(childNode)));
185 processChildElement(childObject.get(), static_cast<DOMElement*>(childNode));
186 childObject.release();
188 // Advance the text node position marker.
191 else if (childNode->getNodeType() == DOMNode::TEXT_NODE || childNode->getNodeType() == DOMNode::CDATA_SECTION_NODE) {
192 m_log.debug("processing text content at position (%d)", position);
193 setTextContent(childNode->getNodeValue(), position);
196 childNode = childNode->getNextSibling();
200 void AbstractXMLObjectUnmarshaller::processChildElement(XMLObject* child, const DOMElement* childRoot)
202 throw UnmarshallingException("Invalid child element: $1",params(1,child->getElementQName().toString().c_str()));
205 void AbstractXMLObjectUnmarshaller::processAttribute(const DOMAttr* attribute)
207 auto_ptr<QName> q(XMLHelper::getNodeQName(attribute));
208 throw UnmarshallingException("Invalid attribute: $1",params(1,q->toString().c_str()));