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 * AbstractXMLObjectMarshaller.cpp
24 * A thread-safe abstract marshaller.
28 #include "exceptions.h"
29 #include "io/AbstractXMLObjectMarshaller.h"
30 #ifndef XMLTOOLING_NO_XMLSEC
31 #include "security/Credential.h"
32 #include "signature/Signature.h"
35 #include "util/XMLConstants.h"
36 #include "util/XMLHelper.h"
40 #include <xercesc/util/XMLUniDefs.hpp>
42 #ifndef XMLTOOLING_NO_XMLSEC
43 using namespace xmlsignature;
45 using namespace xmlconstants;
46 using namespace xmltooling;
47 using namespace xercesc;
50 AbstractXMLObjectMarshaller::AbstractXMLObjectMarshaller()
54 AbstractXMLObjectMarshaller::~AbstractXMLObjectMarshaller()
58 void AbstractXMLObjectMarshaller::setDocumentElement(DOMDocument* document, DOMElement* element) const
60 DOMElement* documentRoot = document->getDocumentElement();
62 document->replaceChild(element, documentRoot);
64 document->appendChild(element);
67 DOMElement* AbstractXMLObjectMarshaller::marshall(
69 #ifndef XMLTOOLING_NO_XMLSEC
70 ,const vector<Signature*>* sigs
71 ,const Credential* credential
76 xmltooling::NDC ndc("marshall");
79 if (m_log.isDebugEnabled()) {
80 m_log.debug("starting to marshal %s", getElementQName().toString().c_str());
83 DOMElement* cachedDOM=getDOM();
85 if (!document || document==cachedDOM->getOwnerDocument()) {
86 m_log.debug("XMLObject has a usable cached DOM, reusing it");
88 setDocumentElement(cachedDOM->getOwnerDocument(),cachedDOM);
89 releaseParentDOM(true);
93 // We have a DOM but it doesn't match the document we were given. This both sucks and blows.
94 // Without an adoptNode option to maintain the child pointers, we have to either import the
95 // DOM while somehow reassigning all the nested references (which amounts to a complete
96 // *unmarshall* operation), or we just release the existing DOM and hope that we can get
97 // it back. This depends on all objects being able to preserve their DOM at all costs.
98 releaseChildrenDOM(true);
102 // If we get here, we didn't have a usable DOM (and/or we released the one we had).
103 prepareForMarshalling();
105 // We may need to create our own document.
106 bool bindDocument=false;
108 document=DOMImplementationRegistry::getDOMImplementation(nullptr)->createDocument();
112 XercesJanitor<DOMDocument> janitor(bindDocument ? document : nullptr);
114 m_log.debug("creating root element to marshall");
115 DOMElement* domElement = document->createElementNS(
116 getElementQName().getNamespaceURI(), getElementQName().getLocalPart()
118 setDocumentElement(document, domElement);
119 #ifndef XMLTOOLING_NO_XMLSEC
120 marshallInto(domElement, sigs, credential);
122 marshallInto(domElement);
125 m_log.debug("caching DOM for XMLObject (document is %sbound)", bindDocument ? "" : "not ");
126 setDOM(domElement, bindDocument);
127 janitor.release(); // safely transferred
128 releaseParentDOM(true);
133 DOMElement* AbstractXMLObjectMarshaller::marshall(
134 DOMElement* parentElement
135 #ifndef XMLTOOLING_NO_XMLSEC
136 ,const vector<Signature*>* sigs
137 ,const Credential* credential
142 xmltooling::NDC ndc("marshall");
145 if (m_log.isDebugEnabled()) {
146 m_log.debug("starting to marshalling %s", getElementQName().toString().c_str());
149 DOMElement* cachedDOM=getDOM();
151 if (parentElement->getOwnerDocument()==cachedDOM->getOwnerDocument()) {
152 m_log.debug("XMLObject has a usable cached DOM, reusing it");
153 if (parentElement!=cachedDOM->getParentNode()) {
154 parentElement->appendChild(cachedDOM);
155 releaseParentDOM(true);
160 // We have a DOM but it doesn't match the document we were given. This both sucks and blows.
161 // Without an adoptNode option to maintain the child pointers, we have to either import the
162 // DOM while somehow reassigning all the nested references (which amounts to a complete
163 // *unmarshall* operation), or we just release the existing DOM and hope that we can get
164 // it back. This depends on all objects being able to preserve their DOM at all costs.
165 releaseChildrenDOM(true);
169 // If we get here, we didn't have a usable DOM (and/or we released the one we had).
170 prepareForMarshalling();
172 m_log.debug("creating root element to marshall");
173 DOMElement* domElement = parentElement->getOwnerDocument()->createElementNS(
174 getElementQName().getNamespaceURI(), getElementQName().getLocalPart()
176 parentElement->appendChild(domElement);
177 #ifndef XMLTOOLING_NO_XMLSEC
178 marshallInto(domElement, sigs, credential);
180 marshallInto(domElement);
184 m_log.debug("caching DOM for XMLObject");
185 setDOM(domElement, false);
186 releaseParentDOM(true);
191 void AbstractXMLObjectMarshaller::marshallInto(
192 DOMElement* targetElement
193 #ifndef XMLTOOLING_NO_XMLSEC
194 ,const vector<Signature*>* sigs
195 ,const Credential* credential
199 if (getElementQName().hasPrefix())
200 targetElement->setPrefix(getElementQName().getPrefix());
202 if (m_schemaLocation || m_noNamespaceSchemaLocation) {
203 static const XMLCh schemaLocation[] = {
204 chLatin_x, chLatin_s, chLatin_i, chColon,
205 chLatin_s, chLatin_c, chLatin_h, chLatin_e, chLatin_m, chLatin_a,
206 chLatin_L, chLatin_o, chLatin_c, chLatin_a, chLatin_t, chLatin_i, chLatin_o, chLatin_n, chNull
208 static const XMLCh noNamespaceSchemaLocation[] = {
209 chLatin_x, chLatin_s, chLatin_i, chColon,
210 chLatin_n, chLatin_o, chLatin_N, chLatin_a, chLatin_m, chLatin_e, chLatin_s, chLatin_p, chLatin_a, chLatin_c, chLatin_e,
211 chLatin_S, chLatin_c, chLatin_h, chLatin_e, chLatin_m, chLatin_a,
212 chLatin_L, chLatin_o, chLatin_c, chLatin_a, chLatin_t, chLatin_i, chLatin_o, chLatin_n, chNull
214 if (targetElement->getParentNode()==nullptr || targetElement->getParentNode()->getNodeType()==DOMNode::DOCUMENT_NODE) {
215 if (m_schemaLocation)
216 targetElement->setAttributeNS(XSI_NS,schemaLocation,m_schemaLocation);
217 if (m_noNamespaceSchemaLocation)
218 targetElement->setAttributeNS(XSI_NS,noNamespaceSchemaLocation,m_noNamespaceSchemaLocation);
222 static const XMLCh _nil[] = { chLatin_x, chLatin_s, chLatin_i, chColon, chLatin_n, chLatin_i, chLatin_l, chNull };
224 if (m_nil != xmlconstants::XML_BOOL_NULL) {
226 case xmlconstants::XML_BOOL_TRUE:
227 targetElement->setAttributeNS(XSI_NS, _nil, xmlconstants::XML_TRUE);
229 case xmlconstants::XML_BOOL_ONE:
230 targetElement->setAttributeNS(XSI_NS, _nil, xmlconstants::XML_ONE);
232 case xmlconstants::XML_BOOL_FALSE:
233 targetElement->setAttributeNS(XSI_NS, _nil, xmlconstants::XML_FALSE);
235 case xmlconstants::XML_BOOL_ZERO:
236 targetElement->setAttributeNS(XSI_NS, _nil, xmlconstants::XML_ZERO);
239 m_log.debug("adding XSI namespace to list of namespaces visibly used by XMLObject");
240 addNamespace(Namespace(XSI_NS, XSI_PREFIX, false, Namespace::VisiblyUsed));
243 marshallElementType(targetElement);
244 marshallNamespaces(targetElement);
245 marshallAttributes(targetElement);
247 #ifndef XMLTOOLING_NO_XMLSEC
248 marshallContent(targetElement,credential);
250 for_each(sigs->begin(),sigs->end(),bind2nd(mem_fun1_t<void,Signature,const Credential*>(&Signature::sign),credential));
253 marshallContent(targetElement);
257 void AbstractXMLObjectMarshaller::marshallElementType(DOMElement* domElement) const
259 const QName* type = getSchemaType();
261 m_log.debug("setting xsi:type attribute for XMLObject");
263 const XMLCh* typeLocalName = type->getLocalPart();
264 if (!typeLocalName || !*typeLocalName) {
265 throw MarshallingException("Schema type of XMLObject may not have an empty local name.");
268 static const XMLCh xsitype[] = {
269 chLatin_x, chLatin_s, chLatin_i, chColon, chLatin_t, chLatin_y, chLatin_p, chLatin_e, chNull
272 XMLCh* xsivalue=const_cast<XMLCh*>(typeLocalName);
273 const XMLCh* prefix=type->getPrefix();
274 if (prefix && *prefix) {
275 xsivalue=new XMLCh[XMLString::stringLen(typeLocalName) + XMLString::stringLen(prefix) + 2*sizeof(XMLCh)];
277 XMLString::catString(xsivalue,prefix);
278 static const XMLCh colon[] = {chColon, chNull};
279 XMLString::catString(xsivalue,colon);
280 XMLString::catString(xsivalue,typeLocalName);
282 domElement->setAttributeNS(XSI_NS, xsitype, xsivalue);
283 if (xsivalue != typeLocalName)
286 m_log.debug("adding XSI namespace to list of namespaces visibly used by XMLObject");
287 addNamespace(Namespace(XSI_NS, XSI_PREFIX, false, Namespace::VisiblyUsed));
291 class _addns : public binary_function<DOMElement*,Namespace,void> {
293 void operator()(DOMElement* domElement, const Namespace& ns) const {
294 const XMLCh* prefix=ns.getNamespacePrefix();
295 const XMLCh* uri=ns.getNamespaceURI();
297 // Check for xmlns:xml.
298 if (XMLString::equals(prefix, XML_PREFIX) && XMLString::equals(uri, XML_NS))
301 // Check to see if the prefix is already declared properly above this node.
302 if (!ns.alwaysDeclare()) {
303 const XMLCh* declared=lookupNamespaceURI(domElement->getParentNode(),prefix);
304 if (declared && XMLString::equals(declared,uri))
308 if (prefix && *prefix) {
309 XMLCh* xmlns=new XMLCh[XMLString::stringLen(XMLNS_PREFIX) + XMLString::stringLen(prefix) + 2*sizeof(XMLCh)];
311 XMLString::catString(xmlns,XMLNS_PREFIX);
312 static const XMLCh colon[] = {chColon, chNull};
313 XMLString::catString(xmlns,colon);
314 XMLString::catString(xmlns,prefix);
315 domElement->setAttributeNS(XMLNS_NS, xmlns, uri);
319 domElement->setAttributeNS(XMLNS_NS, XMLNS_PREFIX, uri);
323 const XMLCh* lookupNamespaceURI(const DOMNode* n, const XMLCh* prefix) const {
324 // Return nullptr if no declaration in effect. The empty string signifies the null namespace.
325 if (!n || n->getNodeType()!=DOMNode::ELEMENT_NODE) {
326 // At the root, the default namespace is set to the null namespace.
327 if (!prefix || !*prefix)
329 return nullptr; // we're done
331 DOMNamedNodeMap* attributes = static_cast<const DOMElement*>(n)->getAttributes();
333 return lookupNamespaceURI(n->getParentNode(),prefix); // defer to parent
336 for (XMLSize_t i=0; i<attributes->getLength(); i++) {
337 childNode = attributes->item(i);
338 if (childNode->getNodeType() != DOMNode::ATTRIBUTE_NODE) // not an attribute?
340 attribute = static_cast<DOMAttr*>(childNode);
341 if (!XMLString::equals(attribute->getNamespaceURI(),XMLNS_NS))
342 continue; // not a namespace declaration
343 // Local name should be the prefix and the value would be the URI, except for the default namespace.
344 if ((!prefix || !*prefix) && XMLString::equals(attribute->getLocalName(),XMLNS_PREFIX))
345 return attribute->getNodeValue();
346 else if (XMLString::equals(prefix,attribute->getLocalName()))
347 return attribute->getNodeValue();
350 return lookupNamespaceURI(n->getParentNode(),prefix);
354 void AbstractXMLObjectMarshaller::marshallNamespaces(DOMElement* domElement) const
356 m_log.debug("marshalling namespace attributes for XMLObject");
357 const set<Namespace>& namespaces = getNamespaces();
358 for_each(namespaces.begin(),namespaces.end(),bind1st(_addns(),domElement));
361 void AbstractXMLObjectMarshaller::marshallContent(
362 DOMElement* domElement
363 #ifndef XMLTOOLING_NO_XMLSEC
364 ,const Credential* credential
368 m_log.debug("marshalling text and child elements for XMLObject");
371 const XMLCh* val = getTextContent(pos);
373 domElement->appendChild(domElement->getOwnerDocument()->createTextNode(val));
375 const list<XMLObject*>& children=getOrderedChildren();
376 for (list<XMLObject*>::const_iterator i=children.begin(); i!=children.end(); ++i) {
378 #ifndef XMLTOOLING_NO_XMLSEC
379 (*i)->marshall(domElement,nullptr,credential);
381 (*i)->marshall(domElement);
383 val = getTextContent(++pos);
385 domElement->appendChild(domElement->getOwnerDocument()->createTextNode(val));
390 void AbstractXMLObjectMarshaller::marshallAttributes(DOMElement* domElement) const
394 void AbstractXMLObjectMarshaller::prepareForMarshalling() const