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 * AbstractXMLObjectMarshaller.cpp
20 * A thread-safe abstract marshaller.
24 #include "exceptions.h"
25 #include "io/AbstractXMLObjectMarshaller.h"
26 #ifndef XMLTOOLING_NO_XMLSEC
27 #include "signature/Signature.h"
30 #include "util/XMLConstants.h"
31 #include "util/XMLHelper.h"
35 #include <xercesc/util/XMLUniDefs.hpp>
37 #ifndef XMLTOOLING_NO_XMLSEC
38 using namespace xmlsignature;
40 using namespace xmlconstants;
41 using namespace xmltooling;
44 DOMElement* AbstractXMLObjectMarshaller::marshall(
46 #ifndef XMLTOOLING_NO_XMLSEC
47 ,const std::vector<xmlsignature::Signature*>* sigs
52 xmltooling::NDC ndc("marshall");
55 if (m_log.isDebugEnabled()) {
56 m_log.debug("starting to marshal %s", getElementQName().toString().c_str());
59 DOMElement* cachedDOM=getDOM();
61 if (!document || document==cachedDOM->getOwnerDocument()) {
62 m_log.debug("XMLObject has a usable cached DOM, reusing it");
64 setDocumentElement(cachedDOM->getOwnerDocument(),cachedDOM);
65 releaseParentDOM(true);
69 // We have a DOM but it doesn't match the document we were given. This both sucks and blows.
70 // Without an adoptNode option to maintain the child pointers, we have to either import the
71 // DOM while somehow reassigning all the nested references (which amounts to a complete
72 // *unmarshall* operation), or we just release the existing DOM and hope that we can get
73 // it back. This depends on all objects being able to preserve their DOM at all costs.
74 releaseChildrenDOM(true);
78 // If we get here, we didn't have a usable DOM (and/or we released the one we had).
79 // We may need to create our own document.
80 bool bindDocument=false;
82 document=DOMImplementationRegistry::getDOMImplementation(NULL)->createDocument();
86 XercesJanitor<DOMDocument> janitor(bindDocument ? document : NULL);
88 m_log.debug("creating root element to marshall");
89 DOMElement* domElement = document->createElementNS(
90 getElementQName().getNamespaceURI(), getElementQName().getLocalPart()
92 setDocumentElement(document, domElement);
93 #ifndef XMLTOOLING_NO_XMLSEC
94 marshallInto(domElement, sigs);
96 marshallInto(domElement);
99 m_log.debug("caching DOM for XMLObject (document is %sbound)", bindDocument ? "" : "not ");
100 setDOM(domElement, bindDocument);
101 janitor.release(); // safely transferred
102 releaseParentDOM(true);
107 DOMElement* AbstractXMLObjectMarshaller::marshall(
108 DOMElement* parentElement
109 #ifndef XMLTOOLING_NO_XMLSEC
110 ,const std::vector<xmlsignature::Signature*>* sigs
115 xmltooling::NDC ndc("marshall");
118 if (m_log.isDebugEnabled()) {
119 m_log.debug("starting to marshalling %s", getElementQName().toString().c_str());
122 DOMElement* cachedDOM=getDOM();
124 if (parentElement->getOwnerDocument()==cachedDOM->getOwnerDocument()) {
125 m_log.debug("XMLObject has a usable cached DOM, reusing it");
126 if (parentElement!=cachedDOM->getParentNode()) {
127 parentElement->appendChild(cachedDOM);
128 releaseParentDOM(true);
133 // We have a DOM but it doesn't match the document we were given. This both sucks and blows.
134 // Without an adoptNode option to maintain the child pointers, we have to either import the
135 // DOM while somehow reassigning all the nested references (which amounts to a complete
136 // *unmarshall* operation), or we just release the existing DOM and hope that we can get
137 // it back. This depends on all objects being able to preserve their DOM at all costs.
138 releaseChildrenDOM(true);
142 // If we get here, we didn't have a usable DOM (and/or we released the one we had).
143 m_log.debug("creating root element to marshall");
144 DOMElement* domElement = parentElement->getOwnerDocument()->createElementNS(
145 getElementQName().getNamespaceURI(), getElementQName().getLocalPart()
147 parentElement->appendChild(domElement);
148 #ifndef XMLTOOLING_NO_XMLSEC
149 marshallInto(domElement, sigs);
151 marshallInto(domElement);
155 m_log.debug("caching DOM for XMLObject");
156 setDOM(domElement, false);
157 releaseParentDOM(true);
162 void AbstractXMLObjectMarshaller::marshallInto(
163 DOMElement* targetElement
164 #ifndef XMLTOOLING_NO_XMLSEC
165 ,const std::vector<xmlsignature::Signature*>* sigs
169 if (getElementQName().hasPrefix())
170 targetElement->setPrefix(getElementQName().getPrefix());
172 if (m_schemaLocation) {
173 static const XMLCh schemaLocation[]= UNICODE_LITERAL_14(s,c,h,e,m,a,L,o,c,a,t,i,o,n);
174 if (targetElement->getParentNode()==NULL || targetElement->getParentNode()->getNodeType()==DOMNode::DOCUMENT_NODE)
175 targetElement->setAttributeNS(XSI_NS,schemaLocation,m_schemaLocation);
178 marshallElementType(targetElement);
179 marshallNamespaces(targetElement);
180 marshallAttributes(targetElement);
181 marshallContent(targetElement);
183 #ifndef XMLTOOLING_NO_XMLSEC
185 for_each(sigs->begin(),sigs->end(),mem_fun<void,Signature>(&Signature::sign));
190 void AbstractXMLObjectMarshaller::marshallElementType(DOMElement* domElement) const
192 const QName* type = getSchemaType();
194 m_log.debug("setting xsi:type attribute for XMLObject");
196 const XMLCh* typeLocalName = type->getLocalPart();
197 if (!typeLocalName || !*typeLocalName) {
198 throw MarshallingException("Schema type of XMLObject may not have an empty local name.");
201 static const XMLCh xsitype[] = {
202 chLatin_x, chLatin_s, chLatin_i, chColon, chLatin_t, chLatin_y, chLatin_p, chLatin_e, chNull
205 XMLCh* xsivalue=const_cast<XMLCh*>(typeLocalName);
206 const XMLCh* prefix=type->getPrefix();
207 if (prefix && *prefix) {
208 xsivalue=new XMLCh[XMLString::stringLen(typeLocalName) + XMLString::stringLen(prefix) + 2*sizeof(XMLCh)];
210 XMLString::catString(xsivalue,prefix);
211 static const XMLCh colon[] = {chColon, chNull};
212 XMLString::catString(xsivalue,colon);
213 XMLString::catString(xsivalue,typeLocalName);
215 domElement->setAttributeNS(XSI_NS, xsitype, xsivalue);
216 if (xsivalue != typeLocalName)
217 XMLString::release(&xsivalue);
219 m_log.debug("Adding XSI namespace to list of namespaces used by XMLObject");
220 addNamespace(Namespace(XSI_NS, XSI_PREFIX));
224 class _addns : public binary_function<DOMElement*,Namespace,void> {
226 void operator()(DOMElement* domElement, const Namespace& ns) const {
227 const XMLCh* prefix=ns.getNamespacePrefix();
228 const XMLCh* uri=ns.getNamespaceURI();
230 // Check to see if the prefix is already declared properly above this node.
231 if (!ns.alwaysDeclare()) {
232 const XMLCh* declared=lookupNamespaceURI(domElement->getParentNode(),prefix);
233 if (declared && XMLString::equals(declared,uri))
237 if (prefix && *prefix) {
238 XMLCh* xmlns=new XMLCh[XMLString::stringLen(XMLNS_PREFIX) + XMLString::stringLen(prefix) + 2*sizeof(XMLCh)];
240 XMLString::catString(xmlns,XMLNS_PREFIX);
241 static const XMLCh colon[] = {chColon, chNull};
242 XMLString::catString(xmlns,colon);
243 XMLString::catString(xmlns,prefix);
244 domElement->setAttributeNS(XMLNS_NS, xmlns, uri);
245 XMLString::release(&xmlns);
248 domElement->setAttributeNS(XMLNS_NS, XMLNS_PREFIX, uri);
252 const XMLCh* lookupNamespaceURI(const DOMNode* n, const XMLCh* prefix) const {
253 // Return NULL if no declaration in effect. The empty string signifies the null namespace.
254 if (!n || n->getNodeType()!=DOMNode::ELEMENT_NODE) {
255 // At the root, the default namespace is set to the null namespace.
256 if (!prefix || !*prefix)
258 return NULL; // we're done
260 DOMNamedNodeMap* attributes = static_cast<const DOMElement*>(n)->getAttributes();
262 return lookupNamespaceURI(n->getParentNode(),prefix); // defer to parent
265 for (XMLSize_t i=0; i<attributes->getLength(); i++) {
266 childNode = attributes->item(i);
267 if (childNode->getNodeType() != DOMNode::ATTRIBUTE_NODE) // not an attribute?
269 attribute = static_cast<DOMAttr*>(childNode);
270 if (!XMLString::equals(attribute->getNamespaceURI(),XMLNS_NS))
271 continue; // not a namespace declaration
272 // Local name should be the prefix and the value would be the URI, except for the default namespace.
273 if ((!prefix || !*prefix) && XMLString::equals(attribute->getLocalName(),XMLNS_PREFIX))
274 return attribute->getNodeValue();
275 else if (XMLString::equals(prefix,attribute->getLocalName()))
276 return attribute->getNodeValue();
279 return lookupNamespaceURI(n->getParentNode(),prefix);
283 void AbstractXMLObjectMarshaller::marshallNamespaces(DOMElement* domElement) const
285 m_log.debug("marshalling namespace attributes for XMLObject");
286 const set<Namespace>& namespaces = getNamespaces();
287 for_each(namespaces.begin(),namespaces.end(),bind1st(_addns(),domElement));
290 void AbstractXMLObjectMarshaller::marshallContent(DOMElement* domElement) const
292 m_log.debug("marshalling text and child elements for XMLObject");
296 const list<XMLObject*>& children=getOrderedChildren();
297 for (list<XMLObject*>::const_iterator i=children.begin(); i!=children.end(); ++i, ++pos) {
298 val = getTextContent(pos);
300 domElement->appendChild(domElement->getOwnerDocument()->createTextNode(val));
302 (*i)->marshall(domElement);
304 val = getTextContent(pos);
306 domElement->appendChild(domElement->getOwnerDocument()->createTextNode(val));