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 "DOMCachingXMLObject.h"
\r
25 #include "exceptions.h"
\r
26 #include "io/AbstractXMLObjectMarshaller.h"
\r
27 #include "util/NDC.h"
\r
28 #include "util/XMLConstants.h"
\r
29 #include "util/XMLHelper.h"
\r
31 #include <algorithm>
\r
32 #include <functional>
\r
33 #include <xercesc/util/XMLUniDefs.hpp>
\r
34 #include <log4cpp/Category.hh>
\r
36 using namespace xmltooling;
\r
37 using namespace log4cpp;
\r
38 using namespace std;
\r
40 #define XT_log (*static_cast<Category*>(m_log))
\r
42 AbstractXMLObjectMarshaller::AbstractXMLObjectMarshaller(const XMLCh* targetNamespaceURI, const XMLCh* targetLocalName)
\r
43 : m_targetQName(targetNamespaceURI, targetLocalName),
\r
44 m_log(&Category::getInstance(XMLTOOLING_LOGCAT".Marshaller")) {
\r
45 if (!targetLocalName || !*targetLocalName)
\r
46 throw MarshallingException("targetLocalName cannot be null or empty");
\r
49 DOMElement* AbstractXMLObjectMarshaller::marshall(XMLObject* xmlObject, DOMDocument* document) const
\r
52 xmltooling::NDC ndc("marshall");
\r
55 if (XT_log.isDebugEnabled()) {
\r
56 XT_log.debug("starting to marshalling %s", xmlObject->getElementQName().toString().c_str());
\r
59 DOMCachingXMLObject* dc=dynamic_cast<DOMCachingXMLObject*>(xmlObject);
\r
61 DOMElement* cachedDOM=dc->getDOM();
\r
63 if (!document || document==cachedDOM->getOwnerDocument()) {
\r
64 XT_log.debug("XMLObject has a usable cached DOM, reusing it");
\r
65 setDocumentElement(cachedDOM->getOwnerDocument(),cachedDOM);
\r
66 dc->releaseParentDOM(true);
\r
70 // We have a DOM but it doesn't match the document we were given. This both sucks and blows.
\r
71 // Without an adoptNode option to maintain the child pointers, we have to either import the
\r
72 // DOM while somehow reassigning all the nested references (which amounts to a complete
\r
73 // *unmarshall* operation), or we just release the existing DOM and hope that we can get
\r
74 // it back. This depends on all objects being able to preserve their DOM at all costs.
\r
75 dc->releaseChildrenDOM(true);
\r
80 // If we get here, we didn't have a usable DOM (and/or we released the one we had).
\r
81 // We may need to create our own document.
\r
82 bool bindDocument=false;
\r
84 document=DOMImplementationRegistry::getDOMImplementation(NULL)->createDocument();
\r
89 XT_log.debug("creating root element to marshall");
\r
90 DOMElement* domElement = document->createElementNS(
\r
91 xmlObject->getElementQName().getNamespaceURI(), xmlObject->getElementQName().getLocalPart()
\r
93 setDocumentElement(document, domElement);
\r
94 marshallInto(xmlObject, domElement);
\r
98 XT_log.debug("caching DOM for XMLObject (document is %sbound)", bindDocument ? "" : "not ");
\r
99 dc->setDOM(domElement, bindDocument);
\r
100 dc->releaseParentDOM(true);
\r
106 // Delete the document if need be, and rethrow.
\r
107 if (bindDocument) {
\r
108 document->release();
\r
114 DOMElement* AbstractXMLObjectMarshaller::marshall(XMLObject* xmlObject, DOMElement* parentElement) const
\r
117 xmltooling::NDC ndc("marshall");
\r
120 if (XT_log.isDebugEnabled()) {
\r
121 XT_log.debug("starting to marshalling %s", xmlObject->getElementQName().toString().c_str());
\r
124 DOMCachingXMLObject* dc=dynamic_cast<DOMCachingXMLObject*>(xmlObject);
\r
126 DOMElement* cachedDOM=dc->getDOM();
\r
128 if (parentElement->getOwnerDocument()==cachedDOM->getOwnerDocument()) {
\r
129 XT_log.debug("XMLObject has a usable cached DOM, reusing it");
\r
130 if (parentElement!=cachedDOM->getParentNode()) {
\r
131 parentElement->appendChild(cachedDOM);
\r
132 dc->releaseParentDOM(true);
\r
137 // We have a DOM but it doesn't match the document we were given. This both sucks and blows.
\r
138 // Without an adoptNode option to maintain the child pointers, we have to either import the
\r
139 // DOM while somehow reassigning all the nested references (which amounts to a complete
\r
140 // *unmarshall* operation), or we just release the existing DOM and hope that we can get
\r
141 // it back. This depends on all objects being able to preserve their DOM at all costs.
\r
142 dc->releaseChildrenDOM(true);
\r
147 // If we get here, we didn't have a usable DOM (and/or we released the one we had).
\r
148 XT_log.debug("creating root element to marshall");
\r
149 DOMElement* domElement = parentElement->getOwnerDocument()->createElementNS(
\r
150 xmlObject->getElementQName().getNamespaceURI(), xmlObject->getElementQName().getLocalPart()
\r
152 parentElement->appendChild(domElement);
\r
153 marshallInto(xmlObject, domElement);
\r
157 XT_log.debug("caching DOM for XMLObject");
\r
158 dc->setDOM(domElement, false);
\r
159 dc->releaseParentDOM(true);
\r
165 void AbstractXMLObjectMarshaller::marshallInto(XMLObject* xmlObject, DOMElement* targetElement) const
\r
167 targetElement->setPrefix(xmlObject->getElementQName().getPrefix());
\r
168 marshallNamespaces(xmlObject, targetElement);
\r
169 marshallAttributes(xmlObject, targetElement);
\r
170 marshallChildElements(xmlObject, targetElement);
\r
171 marshallElementContent(xmlObject, targetElement);
\r
172 marshallElementType(xmlObject, targetElement);
\r
174 /* TODO Signing/Encryption
\r
175 if (xmlObject instanceof SignableXMLObject) {
\r
176 signElement(targetElement, xmlObject);
\r
179 if (xmlObject instanceof EncryptableXMLObject) {
\r
180 encryptElement(targetElement, xmlObject);
\r
185 void AbstractXMLObjectMarshaller::marshallElementType(XMLObject* xmlObject, DOMElement* domElement) const
\r
187 const QName* type = xmlObject->getSchemaType();
\r
189 XT_log.debug("setting xsi:type attribute for XMLObject");
\r
191 const XMLCh* typeLocalName = type->getLocalPart();
\r
192 if (!typeLocalName || !*typeLocalName) {
\r
193 throw MarshallingException("Schema type of XMLObject may not have an empty local name.");
\r
196 static const XMLCh xsitype[] = {
\r
197 chLatin_x, chLatin_s, chLatin_i, chColon, chLatin_t, chLatin_y, chLatin_p, chLatin_e, chNull
\r
200 XMLCh* xsivalue=const_cast<XMLCh*>(typeLocalName);
\r
201 const XMLCh* prefix=type->getPrefix();
\r
202 if (prefix && *prefix) {
\r
203 xsivalue=new XMLCh[XMLString::stringLen(typeLocalName) + XMLString::stringLen(prefix) + 2*sizeof(XMLCh)];
\r
205 XMLString::catString(xsivalue,prefix);
\r
206 static const XMLCh colon[] = {chColon, chNull};
\r
207 XMLString::catString(xsivalue,colon);
\r
208 XMLString::catString(xsivalue,typeLocalName);
\r
210 domElement->setAttributeNS(XMLConstants::XSI_NS, xsitype, xsivalue);
\r
211 if (xsivalue != typeLocalName)
\r
212 XMLString::release(&xsivalue);
\r
214 XT_log.debug("Adding XSI namespace to list of namespaces used by XMLObject");
\r
215 xmlObject->addNamespace(Namespace(XMLConstants::XSI_NS, XMLConstants::XSI_PREFIX));
\r
219 class _addns : public binary_function<DOMElement*,Namespace,void> {
\r
221 void operator()(DOMElement* domElement, const Namespace& ns) const {
\r
222 const XMLCh* prefix=ns.getNamespacePrefix();
\r
223 const XMLCh* uri=ns.getNamespaceURI();
\r
224 if (prefix && *prefix) {
\r
225 XMLCh* xmlns=new XMLCh[XMLString::stringLen(XMLConstants::XMLNS_PREFIX) + XMLString::stringLen(prefix) + 2*sizeof(XMLCh)];
\r
227 XMLString::catString(xmlns,XMLConstants::XMLNS_PREFIX);
\r
228 static const XMLCh colon[] = {chColon, chNull};
\r
229 XMLString::catString(xmlns,colon);
\r
230 XMLString::catString(xmlns,prefix);
\r
231 domElement->setAttributeNS(XMLConstants::XMLNS_NS, xmlns, uri);
\r
234 domElement->setAttributeNS(XMLConstants::XMLNS_NS, XMLConstants::XMLNS_PREFIX, uri);
\r
239 void AbstractXMLObjectMarshaller::marshallNamespaces(const XMLObject* xmlObject, DOMElement* domElement) const
\r
241 XT_log.debug("marshalling namespace attributes for XMLObject");
\r
242 const set<Namespace>& namespaces = xmlObject->getNamespaces();
\r
243 for_each(namespaces.begin(),namespaces.end(),bind1st(_addns(),domElement));
\r
246 class _marshallchild : public binary_function<XMLObject*,DOMElement*,void> {
\r
249 _marshallchild(void* log) : m_log(log) {}
\r
250 void operator()(XMLObject* obj, DOMElement* element) const {
\r
251 if (XT_log.isDebugEnabled()) {
\r
252 XT_log.debug("getting marshaller for child XMLObject: %s", obj->getElementQName().toString().c_str());
\r
255 const Marshaller* marshaller = Marshaller::getMarshaller(obj);
\r
258 "no default unmarshaller installed, unknown child object: %s",
\r
259 obj->getElementQName().toString().c_str()
\r
261 throw MarshallingException("Marshaller found unknown child element, but no default marshaller was found.");
\r
263 element->appendChild(marshaller->marshall(obj, element->getOwnerDocument()));
\r
267 void AbstractXMLObjectMarshaller::marshallChildElements(const XMLObject* xmlObject, DOMElement* domElement) const
\r
269 XT_log.debug("marshalling child elements for XMLObject");
\r
271 vector<XMLObject*> children;
\r
272 if (xmlObject->getOrderedChildren(children)) {
\r
273 for_each(children.begin(),children.end(),bind2nd(_marshallchild(m_log),domElement));
\r