2 * Copyright 2001-2006 Internet2
\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
8 * http://www.apache.org/licenses/LICENSE-2.0
\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
18 * AbstractXMLObjectMarshaller.cpp
\r
20 * A thread-safe abstract marshaller.
\r
23 #include "internal.h"
\r
24 #include "exceptions.h"
\r
25 #include "io/AbstractXMLObjectMarshaller.h"
\r
26 #ifndef XMLTOOLING_NO_XMLSEC
\r
27 #include "signature/Signature.h"
\r
29 #include "util/NDC.h"
\r
30 #include "util/XMLConstants.h"
\r
31 #include "util/XMLHelper.h"
\r
33 #include <algorithm>
\r
34 #include <functional>
\r
35 #include <xercesc/util/XMLUniDefs.hpp>
\r
36 #include <log4cpp/Category.hh>
\r
38 #ifndef XMLTOOLING_NO_XMLSEC
\r
39 using namespace xmlsignature;
\r
41 using namespace xmltooling;
\r
42 using namespace log4cpp;
\r
43 using namespace std;
\r
45 #define XT_log (*static_cast<Category*>(m_log))
\r
47 DOMElement* AbstractXMLObjectMarshaller::marshall(
\r
48 DOMDocument* document
\r
49 #ifndef XMLTOOLING_NO_XMLSEC
\r
50 ,const std::vector<xmlsignature::Signature*>* sigs
\r
55 xmltooling::NDC ndc("marshall");
\r
58 if (XT_log.isDebugEnabled()) {
\r
59 XT_log.debug("starting to marshal %s", getElementQName().toString().c_str());
\r
62 DOMElement* cachedDOM=getDOM();
\r
64 if (!document || document==cachedDOM->getOwnerDocument()) {
\r
65 XT_log.debug("XMLObject has a usable cached DOM, reusing it");
\r
67 setDocumentElement(cachedDOM->getOwnerDocument(),cachedDOM);
\r
68 releaseParentDOM(true);
\r
72 // We have a DOM but it doesn't match the document we were given. This both sucks and blows.
\r
73 // Without an adoptNode option to maintain the child pointers, we have to either import the
\r
74 // DOM while somehow reassigning all the nested references (which amounts to a complete
\r
75 // *unmarshall* operation), or we just release the existing DOM and hope that we can get
\r
76 // it back. This depends on all objects being able to preserve their DOM at all costs.
\r
77 releaseChildrenDOM(true);
\r
81 // If we get here, we didn't have a usable DOM (and/or we released the one we had).
\r
82 // We may need to create our own document.
\r
83 bool bindDocument=false;
\r
85 document=DOMImplementationRegistry::getDOMImplementation(NULL)->createDocument();
\r
90 XT_log.debug("creating root element to marshall");
\r
91 DOMElement* domElement = document->createElementNS(
\r
92 getElementQName().getNamespaceURI(), getElementQName().getLocalPart()
\r
94 setDocumentElement(document, domElement);
\r
95 #ifndef XMLTOOLING_NO_XMLSEC
\r
96 marshallInto(domElement, sigs);
\r
98 marshallInto(domElement);
\r
101 XT_log.debug("caching DOM for XMLObject (document is %sbound)", bindDocument ? "" : "not ");
\r
102 setDOM(domElement, bindDocument);
\r
103 releaseParentDOM(true);
\r
108 // Delete the document if need be, and rethrow.
\r
109 if (bindDocument) {
\r
110 document->release();
\r
116 DOMElement* AbstractXMLObjectMarshaller::marshall(
\r
117 DOMElement* parentElement
\r
118 #ifndef XMLTOOLING_NO_XMLSEC
\r
119 ,const std::vector<xmlsignature::Signature*>* sigs
\r
124 xmltooling::NDC ndc("marshall");
\r
127 if (XT_log.isDebugEnabled()) {
\r
128 XT_log.debug("starting to marshalling %s", getElementQName().toString().c_str());
\r
131 DOMElement* cachedDOM=getDOM();
\r
133 if (parentElement->getOwnerDocument()==cachedDOM->getOwnerDocument()) {
\r
134 XT_log.debug("XMLObject has a usable cached DOM, reusing it");
\r
135 if (parentElement!=cachedDOM->getParentNode()) {
\r
136 parentElement->appendChild(cachedDOM);
\r
137 releaseParentDOM(true);
\r
142 // We have a DOM but it doesn't match the document we were given. This both sucks and blows.
\r
143 // Without an adoptNode option to maintain the child pointers, we have to either import the
\r
144 // DOM while somehow reassigning all the nested references (which amounts to a complete
\r
145 // *unmarshall* operation), or we just release the existing DOM and hope that we can get
\r
146 // it back. This depends on all objects being able to preserve their DOM at all costs.
\r
147 releaseChildrenDOM(true);
\r
151 // If we get here, we didn't have a usable DOM (and/or we released the one we had).
\r
152 XT_log.debug("creating root element to marshall");
\r
153 DOMElement* domElement = parentElement->getOwnerDocument()->createElementNS(
\r
154 getElementQName().getNamespaceURI(), getElementQName().getLocalPart()
\r
156 parentElement->appendChild(domElement);
\r
157 #ifndef XMLTOOLING_NO_XMLSEC
\r
158 marshallInto(domElement, sigs);
\r
160 marshallInto(domElement);
\r
164 XT_log.debug("caching DOM for XMLObject");
\r
165 setDOM(domElement, false);
\r
166 releaseParentDOM(true);
\r
171 void AbstractXMLObjectMarshaller::marshallInto(
\r
172 DOMElement* targetElement
\r
173 #ifndef XMLTOOLING_NO_XMLSEC
\r
174 ,const std::vector<xmlsignature::Signature*>* sigs
\r
178 if (getElementQName().hasPrefix())
\r
179 targetElement->setPrefix(getElementQName().getPrefix());
\r
181 if (m_schemaLocation) {
\r
182 static const XMLCh schemaLocation[]= UNICODE_LITERAL_14(s,c,h,e,m,a,L,o,c,a,t,i,o,n);
\r
183 if (targetElement->getParentNode()==NULL || targetElement->getParentNode()->getNodeType()==DOMNode::DOCUMENT_NODE)
\r
184 targetElement->setAttributeNS(XMLConstants::XSI_NS,schemaLocation,m_schemaLocation);
\r
187 marshallElementType(targetElement);
\r
188 marshallNamespaces(targetElement);
\r
189 marshallAttributes(targetElement);
\r
190 marshallChildElements(targetElement);
\r
191 marshallElementContent(targetElement);
\r
193 #ifndef XMLTOOLING_NO_XMLSEC
\r
195 for_each(sigs->begin(),sigs->end(),mem_fun<void,Signature>(&Signature::sign));
\r
200 void AbstractXMLObjectMarshaller::marshallElementType(DOMElement* domElement) const
\r
202 const QName* type = getSchemaType();
\r
204 XT_log.debug("setting xsi:type attribute for XMLObject");
\r
206 const XMLCh* typeLocalName = type->getLocalPart();
\r
207 if (!typeLocalName || !*typeLocalName) {
\r
208 throw MarshallingException("Schema type of XMLObject may not have an empty local name.");
\r
211 static const XMLCh xsitype[] = {
\r
212 chLatin_x, chLatin_s, chLatin_i, chColon, chLatin_t, chLatin_y, chLatin_p, chLatin_e, chNull
\r
215 XMLCh* xsivalue=const_cast<XMLCh*>(typeLocalName);
\r
216 const XMLCh* prefix=type->getPrefix();
\r
217 if (prefix && *prefix) {
\r
218 xsivalue=new XMLCh[XMLString::stringLen(typeLocalName) + XMLString::stringLen(prefix) + 2*sizeof(XMLCh)];
\r
220 XMLString::catString(xsivalue,prefix);
\r
221 static const XMLCh colon[] = {chColon, chNull};
\r
222 XMLString::catString(xsivalue,colon);
\r
223 XMLString::catString(xsivalue,typeLocalName);
\r
225 domElement->setAttributeNS(XMLConstants::XSI_NS, xsitype, xsivalue);
\r
226 if (xsivalue != typeLocalName)
\r
227 XMLString::release(&xsivalue);
\r
229 XT_log.debug("Adding XSI namespace to list of namespaces used by XMLObject");
\r
230 addNamespace(Namespace(XMLConstants::XSI_NS, XMLConstants::XSI_PREFIX));
\r
234 class _addns : public binary_function<DOMElement*,Namespace,void> {
\r
236 void operator()(DOMElement* domElement, const Namespace& ns) const {
\r
237 const XMLCh* prefix=ns.getNamespacePrefix();
\r
238 const XMLCh* uri=ns.getNamespaceURI();
\r
240 // Check to see if the prefix is already declared properly above this node.
\r
241 if (!ns.alwaysDeclare()) {
\r
242 const XMLCh* declared=lookupNamespaceURI(domElement->getParentNode(),prefix);
\r
243 if (declared && XMLString::equals(declared,uri))
\r
247 if (prefix && *prefix) {
\r
248 XMLCh* xmlns=new XMLCh[XMLString::stringLen(XMLConstants::XMLNS_PREFIX) + XMLString::stringLen(prefix) + 2*sizeof(XMLCh)];
\r
250 XMLString::catString(xmlns,XMLConstants::XMLNS_PREFIX);
\r
251 static const XMLCh colon[] = {chColon, chNull};
\r
252 XMLString::catString(xmlns,colon);
\r
253 XMLString::catString(xmlns,prefix);
\r
254 domElement->setAttributeNS(XMLConstants::XMLNS_NS, xmlns, uri);
\r
257 domElement->setAttributeNS(XMLConstants::XMLNS_NS, XMLConstants::XMLNS_PREFIX, uri);
\r
261 const XMLCh* lookupNamespaceURI(const DOMNode* n, const XMLCh* prefix) const {
\r
262 // Return NULL if no declaration in effect. The empty string signifies the null namespace.
\r
263 if (!n || n->getNodeType()!=DOMNode::ELEMENT_NODE) {
\r
264 // At the root, the default namespace is set to the null namespace.
\r
265 if (!prefix || !*prefix)
\r
267 return NULL; // we're done
\r
269 DOMNamedNodeMap* attributes = static_cast<const DOMElement*>(n)->getAttributes();
\r
271 return lookupNamespaceURI(n->getParentNode(),prefix); // defer to parent
\r
272 DOMNode* childNode;
\r
273 DOMAttr* attribute;
\r
274 for (XMLSize_t i=0; i<attributes->getLength(); i++) {
\r
275 childNode = attributes->item(i);
\r
276 if (childNode->getNodeType() != DOMNode::ATTRIBUTE_NODE) // not an attribute?
\r
278 attribute = static_cast<DOMAttr*>(childNode);
\r
279 if (!XMLString::equals(attribute->getNamespaceURI(),XMLConstants::XMLNS_NS))
\r
280 continue; // not a namespace declaration
\r
281 // Local name should be the prefix and the value would be the URI, except for the default namespace.
\r
282 if ((!prefix || !*prefix) && XMLString::equals(attribute->getLocalName(),XMLConstants::XMLNS_PREFIX))
\r
283 return attribute->getNodeValue();
\r
284 else if (XMLString::equals(prefix,attribute->getLocalName()))
\r
285 return attribute->getNodeValue();
\r
287 // Defer to parent.
\r
288 return lookupNamespaceURI(n->getParentNode(),prefix);
\r
292 void AbstractXMLObjectMarshaller::marshallNamespaces(DOMElement* domElement) const
\r
294 XT_log.debug("marshalling namespace attributes for XMLObject");
\r
295 const set<Namespace>& namespaces = getNamespaces();
\r
296 for_each(namespaces.begin(),namespaces.end(),bind1st(_addns(),domElement));
\r
299 class _marshallit : public binary_function<const XMLObject*,DOMElement*,void> {
\r
301 void operator()(const XMLObject* xo, DOMElement* e) const {
\r
302 if (xo) xo->marshall(e);
\r
306 void AbstractXMLObjectMarshaller::marshallChildElements(DOMElement* domElement) const
\r
308 XT_log.debug("marshalling child elements for XMLObject");
\r
310 const list<XMLObject*>& children=getOrderedChildren();
\r
311 for_each(children.begin(),children.end(),bind2nd(_marshallit(),domElement));
\r