2 * Licensed to the University Corporation for Advanced Internet
3 * Development, Inc. (UCAID) under one or more contributor license
4 * agreements. See the NOTICE file distributed with this work for
5 * additional information regarding copyright ownership.
7 * UCAID licenses this file to you under the Apache License,
8 * Version 2.0 (the "License"); you may not use this file except
9 * in compliance with the License. You may obtain a copy of the
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
17 * either express or implied. See the License for the specific
18 * language governing permissions and limitations under the License.
22 * AbstractXMLObjectUnmarshaller.cpp
24 * A thread-safe abstract unmarshaller.
28 #include "exceptions.h"
29 #include "XMLObjectBuilder.h"
30 #include "io/AbstractXMLObjectUnmarshaller.h"
32 #include "util/XMLConstants.h"
33 #include "util/XMLHelper.h"
35 #include <xercesc/util/XMLUniDefs.hpp>
37 using namespace xmlconstants;
38 using namespace xmltooling;
39 using namespace xercesc;
42 AbstractXMLObjectUnmarshaller::AbstractXMLObjectUnmarshaller()
46 AbstractXMLObjectUnmarshaller::~AbstractXMLObjectUnmarshaller()
50 XMLObject* AbstractXMLObjectUnmarshaller::unmarshall(DOMElement* element, bool bindDocument)
53 xmltooling::NDC ndc("unmarshall");
56 if (getDOM() || hasParent())
57 throw UnmarshallingException("Object already contains data, it cannot be unmarshalled at this stage.");
59 if (!XMLString::equals(element->getNamespaceURI(),getElementQName().getNamespaceURI()) ||
60 !XMLString::equals(element->getLocalName(),getElementQName().getLocalPart())) {
61 throw UnmarshallingException("Unrecognized element supplied to implementation for unmarshalling.");
64 if (m_log.isDebugEnabled()) {
65 auto_ptr_char dname(element->getNodeName());
66 m_log.debug("unmarshalling DOM element (%s)", dname.get());
69 if (element->hasAttributes()) {
70 unmarshallAttributes(element);
73 unmarshallContent(element);
75 setDOM(element,bindDocument);
79 void AbstractXMLObjectUnmarshaller::unmarshallAttributes(const DOMElement* domElement)
82 xmltooling::NDC ndc("unmarshallAttributes");
85 if (m_log.isDebugEnabled()) {
86 auto_ptr_char dname(domElement->getNodeName());
87 m_log.debug("unmarshalling attributes for DOM element (%s)", dname.get());
90 DOMNamedNodeMap* attributes = domElement->getAttributes();
92 m_log.debug("no attributes to unmarshall");
98 for (XMLSize_t i=0; i<attributes->getLength(); i++) {
99 childNode = attributes->item(i);
101 // The child node should always be an attribute, but just in case
102 if (childNode->getNodeType() != DOMNode::ATTRIBUTE_NODE) {
103 m_log.debug("encountered child node of type %d in attribute list, ignoring it", childNode->getNodeType());
107 attribute = static_cast<DOMAttr*>(childNode);
109 const XMLCh* nsuri=attribute->getNamespaceURI();
110 if (XMLString::equals(nsuri,XMLNS_NS)) {
111 if (XMLString::equals(attribute->getLocalName(),XMLNS_PREFIX)) {
112 m_log.debug("found default namespace declaration, adding it to the list of namespaces on the XMLObject");
113 addNamespace(Namespace(attribute->getValue(), nullptr, true));
115 else if (XMLString::equals(attribute->getLocalName(),XML_PREFIX) && XMLString::equals(attribute->getNodeValue(),XML_NS)) {
116 m_log.debug("found standard xml prefix declaration, ignoring as superfluous");
119 m_log.debug("found namespace declaration, adding it to the list of namespaces on the XMLObject");
120 addNamespace(Namespace(attribute->getValue(), attribute->getLocalName(), true));
124 else if (XMLString::equals(nsuri,XSI_NS)) {
125 static const XMLCh type[]= UNICODE_LITERAL_4(t,y,p,e);
126 static const XMLCh schemaLocation[]= UNICODE_LITERAL_14(s,c,h,e,m,a,L,o,c,a,t,i,o,n);
127 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);
128 static const XMLCh _nil[]= UNICODE_LITERAL_3(n,i,l);
129 if (XMLString::equals(attribute->getLocalName(),type)) {
130 m_log.debug("skipping xsi:type declaration");
133 else if (XMLString::equals(attribute->getLocalName(),schemaLocation)) {
134 m_log.debug("storing off xsi:schemaLocation attribute");
135 if (m_schemaLocation)
136 XMLString::release(&m_schemaLocation);
137 m_schemaLocation=XMLString::replicate(attribute->getValue());
140 else if (XMLString::equals(attribute->getLocalName(),noNamespaceSchemaLocation)) {
141 m_log.debug("storing off xsi:noNamespaceSchemaLocation attribute");
142 if (m_noNamespaceSchemaLocation)
143 XMLString::release(&m_noNamespaceSchemaLocation);
144 m_schemaLocation=XMLString::replicate(attribute->getValue());
145 m_noNamespaceSchemaLocation=XMLString::replicate(attribute->getValue());
148 else if (XMLString::equals(attribute->getLocalName(), _nil)) {
149 m_log.debug("processing xsi:nil attribute");
150 setNil(attribute->getValue());
153 // Note that the prefix is visibly used.
154 addNamespace(Namespace(nsuri, attribute->getPrefix(), false, Namespace::VisiblyUsed));
156 else if (nsuri && !XMLString::equals(nsuri,XML_NS)) {
157 m_log.debug("found namespace-qualified attribute, adding prefix to the list of visible namespaces on the XMLObject");
158 addNamespace(Namespace(nsuri, attribute->getPrefix(), false, Namespace::VisiblyUsed));
161 m_log.debug("processing generic attribute");
162 processAttribute(attribute);
166 void AbstractXMLObjectUnmarshaller::unmarshallContent(const DOMElement* domElement)
169 xmltooling::NDC ndc("unmarshallContent");
172 if (m_log.isDebugEnabled()) {
173 auto_ptr_char dname(domElement->getNodeName());
174 m_log.debug("unmarshalling child nodes of DOM element (%s)", dname.get());
177 DOMNode* childNode = domElement->getFirstChild();
179 m_log.debug("element had no children");
183 unsigned int position = 0;
185 if (childNode->getNodeType() == DOMNode::ELEMENT_NODE) {
186 const XMLObjectBuilder* builder = XMLObjectBuilder::getBuilder(static_cast<DOMElement*>(childNode));
188 auto_ptr<QName> cname(XMLHelper::getNodeQName(childNode));
189 m_log.error("no default builder installed, found unknown child element (%s)", cname->toString().c_str());
190 throw UnmarshallingException("Unmarshaller found unknown child element, but no default builder was found.");
193 if (m_log.isDebugEnabled()) {
194 auto_ptr<QName> cname(XMLHelper::getNodeQName(childNode));
195 m_log.debug("unmarshalling child element (%s)", cname->toString().c_str());
198 // Retain ownership of the unmarshalled child until it's processed by the parent.
199 auto_ptr<XMLObject> childObject(builder->buildFromElement(static_cast<DOMElement*>(childNode)));
200 processChildElement(childObject.get(), static_cast<DOMElement*>(childNode));
201 childObject.release();
203 // Advance the text node position marker.
206 else if (childNode->getNodeType() == DOMNode::TEXT_NODE || childNode->getNodeType() == DOMNode::CDATA_SECTION_NODE) {
207 m_log.debug("processing text content at position (%d)", position);
208 setTextContent(childNode->getNodeValue(), position);
211 childNode = childNode->getNextSibling();
215 void AbstractXMLObjectUnmarshaller::processChildElement(XMLObject* child, const DOMElement* childRoot)
217 throw UnmarshallingException("Invalid child element: $1",params(1,child->getElementQName().toString().c_str()));
220 void AbstractXMLObjectUnmarshaller::processAttribute(const DOMAttr* attribute)
222 auto_ptr<QName> q(XMLHelper::getNodeQName(attribute));
223 throw UnmarshallingException("Invalid attribute: $1",params(1,q->toString().c_str()));