DOMElement* cachedDOM=unk->getDOM();\r
if (cachedDOM) {\r
if (!document || document==cachedDOM->getOwnerDocument()) {\r
- log.debug("XMLObject has a usable cached DOM, using it");\r
+ log.debug("XMLObject has a usable cached DOM, reusing it");\r
+ setDocumentElement(cachedDOM->getOwnerDocument(),cachedDOM);\r
+ unk->releaseParentDOM(true);\r
return cachedDOM;\r
}\r
\r
log.debug("parsing XML back into DOM tree");\r
DOMDocument* internalDoc=XMLToolingInternalConfig::getInternalConfig().m_parserPool->parse(dsrc);\r
if (document) {\r
- // The caller insists on using his own document, so we now have to import the damn thing\r
+ // The caller insists on using his own document, so we now have to import the thing\r
// into it. Then we're just dumping the one we built.\r
log.debug("reimporting new DOM into caller-supplied document");\r
cachedDOM=static_cast<DOMElement*>(document->importNode(internalDoc->getDocumentElement(), true));\r
}\r
else {\r
// We just bind the document we built to the object as the result.\r
+ cachedDOM=static_cast<DOMElement*>(internalDoc->getDocumentElement());\r
document=internalDoc;\r
bindDocument=true;\r
}\r
\r
// Recache the DOM and clear the serialized copy.\r
- log.debug("caching DOM for XMLObject");\r
+ setDocumentElement(document, cachedDOM);\r
+ log.debug("caching DOM for XMLObject (document is %sbound)", bindDocument ? "" : "not ");\r
unk->setDOM(cachedDOM, bindDocument);\r
+ unk->releaseParentDOM(true);\r
+ unk->m_xml.erase();\r
+ return cachedDOM;\r
+}\r
+\r
+DOMElement* UnknownElementMarshaller::marshall(XMLObject* xmlObject, DOMElement* parentElement) const\r
+{\r
+#ifdef _DEBUG\r
+ xmltooling::NDC ndc("marshall");\r
+#endif\r
+ \r
+ Category& log=Category::getInstance(XMLTOOLING_LOGCAT".Marshaller");\r
+ log.debug("marshalling unknown content");\r
+\r
+ UnknownElementImpl* unk=dynamic_cast<UnknownElementImpl*>(xmlObject);\r
+ if (!unk)\r
+ throw MarshallingException("Only objects of class UnknownElementImpl can be marshalled.");\r
+ \r
+ DOMElement* cachedDOM=unk->getDOM();\r
+ if (cachedDOM) {\r
+ if (parentElement->getOwnerDocument()==cachedDOM->getOwnerDocument()) {\r
+ log.debug("XMLObject has a usable cached DOM, reusing it");\r
+ parentElement->appendChild(cachedDOM);\r
+ unk->releaseParentDOM(true);\r
+ return cachedDOM;\r
+ }\r
+ \r
+ // We have a DOM but it doesn't match the document we were given. This both sucks and blows.\r
+ // Without an adoptNode option to maintain the child pointers, we rely on our custom\r
+ // implementation class to preserve the XML when we release the existing DOM.\r
+ unk->releaseDOM();\r
+ }\r
+ \r
+ // If we get here, we didn't have a usable DOM (and/or we flushed the one we had).\r
+ // We need to reparse the XML we saved off into a new DOM.\r
+ MemBufInputSource src(reinterpret_cast<const XMLByte*>(unk->m_xml.c_str()),unk->m_xml.length(),"UnknownElementImpl");\r
+ Wrapper4InputSource dsrc(&src,false);\r
+ log.debug("parsing XML back into DOM tree");\r
+ DOMDocument* internalDoc=XMLToolingInternalConfig::getInternalConfig().m_parserPool->parse(dsrc);\r
+ \r
+ log.debug("reimporting new DOM into caller-supplied document");\r
+ cachedDOM=static_cast<DOMElement*>(parentElement->getOwnerDocument()->importNode(internalDoc->getDocumentElement(), true));\r
+ internalDoc->release();\r
+\r
+ // Recache the DOM and clear the serialized copy.\r
+ parentElement->appendChild(cachedDOM);\r
+ log.debug("caching DOM for XMLObject");\r
+ unk->setDOM(cachedDOM, false);\r
+ unk->releaseParentDOM(true);\r
unk->m_xml.erase();\r
return cachedDOM;\r
}\r
virtual ~UnknownElementMarshaller() {}\r
\r
/**\r
- * @see Marshaller::marshall()\r
+ * @see Marshaller::marshall(XMLObject*,DOMDocument*)\r
*/\r
DOMElement* marshall(XMLObject* xmlObject, DOMDocument* document=NULL) const;\r
+\r
+ /**\r
+ * @see Marshaller::marshall(XMLObject*,DOMElement*)\r
+ */\r
+ DOMElement* marshall(XMLObject* xmlObject, DOMElement* parentElement) const;\r
+ \r
+ protected:\r
+ void setDocumentElement(DOMDocument* document, DOMElement* element) const {\r
+ DOMElement* documentRoot = document->getDocumentElement();\r
+ if (documentRoot)\r
+ document->replaceChild(documentRoot, element);\r
+ else\r
+ document->appendChild(element);\r
+ }\r
};\r
\r
/**\r
if (!targetLocalName || !*targetLocalName)\r
throw MarshallingException("targetLocalName cannot be null or empty");\r
}\r
- \r
+\r
DOMElement* AbstractXMLObjectMarshaller::marshall(XMLObject* xmlObject, DOMDocument* document) const\r
{\r
#ifdef _DEBUG\r
#endif\r
\r
if (XT_log.isDebugEnabled()) {\r
- XT_log.debug("marshalling %s", xmlObject->getElementQName().toString().c_str());\r
+ XT_log.debug("starting to marshalling %s", xmlObject->getElementQName().toString().c_str());\r
}\r
\r
DOMCachingXMLObject* dc=dynamic_cast<DOMCachingXMLObject*>(xmlObject);\r
DOMElement* cachedDOM=dc->getDOM();\r
if (cachedDOM) {\r
if (!document || document==cachedDOM->getOwnerDocument()) {\r
- XT_log.debug("XMLObject has a usable cached DOM, using it.");\r
+ XT_log.debug("XMLObject has a usable cached DOM, reusing it");\r
+ setDocumentElement(cachedDOM->getOwnerDocument(),cachedDOM);\r
+ dc->releaseParentDOM(true);\r
return cachedDOM;\r
}\r
\r
}\r
}\r
\r
- // If we get here, we didn't have a usable DOM (and/or we flushed the one we had).\r
+ // If we get here, we didn't have a usable DOM (and/or we released the one we had).\r
// We may need to create our own document.\r
bool bindDocument=false;\r
if (!document) {\r
bindDocument=true;\r
}\r
\r
- XT_log.debug("creating root element to marshall");\r
- DOMElement* domElement = document->createElementNS(\r
- xmlObject->getElementQName().getNamespaceURI(), xmlObject->getElementQName().getLocalPart()\r
- );\r
- domElement->setPrefix(xmlObject->getElementQName().getPrefix());\r
-\r
try {\r
- marshallNamespaces(xmlObject, domElement);\r
- marshallAttributes(xmlObject, domElement);\r
- marshallChildElements(xmlObject, domElement);\r
- marshallElementContent(xmlObject, domElement);\r
- marshallElementType(xmlObject, domElement);\r
- \r
- /* TODO: signing\r
- if (xmlObject instanceof SignableXMLObject) {\r
- signElement(domElement, xmlObject);\r
+ XT_log.debug("creating root element to marshall");\r
+ DOMElement* domElement = document->createElementNS(\r
+ xmlObject->getElementQName().getNamespaceURI(), xmlObject->getElementQName().getLocalPart()\r
+ );\r
+ setDocumentElement(document, domElement);\r
+ marshallInto(xmlObject, domElement);\r
+\r
+ //Recache the DOM.\r
+ if (dc) {\r
+ XT_log.debug("caching DOM for XMLObject (document is %sbound)", bindDocument ? "" : "not ");\r
+ dc->setDOM(domElement, bindDocument);\r
+ dc->releaseParentDOM(true);\r
}\r
- */\r
+\r
+ return domElement;\r
}\r
catch (...) {\r
// Delete the document if need be, and rethrow.\r
if (bindDocument) {\r
document->release();\r
- document=NULL;\r
}\r
throw;\r
}\r
+}\r
+\r
+DOMElement* AbstractXMLObjectMarshaller::marshall(XMLObject* xmlObject, DOMElement* parentElement) const\r
+{\r
+#ifdef _DEBUG\r
+ xmltooling::NDC ndc("marshall");\r
+#endif\r
+\r
+ if (XT_log.isDebugEnabled()) {\r
+ XT_log.debug("starting to marshalling %s", xmlObject->getElementQName().toString().c_str());\r
+ }\r
+\r
+ DOMCachingXMLObject* dc=dynamic_cast<DOMCachingXMLObject*>(xmlObject);\r
+ if (dc) {\r
+ DOMElement* cachedDOM=dc->getDOM();\r
+ if (cachedDOM) {\r
+ if (parentElement->getOwnerDocument()==cachedDOM->getOwnerDocument()) {\r
+ XT_log.debug("XMLObject has a usable cached DOM, reusing it");\r
+ if (parentElement!=cachedDOM->getParentNode()) {\r
+ parentElement->appendChild(cachedDOM);\r
+ dc->releaseParentDOM(true);\r
+ }\r
+ return cachedDOM;\r
+ }\r
+ \r
+ // We have a DOM but it doesn't match the document we were given. This both sucks and blows.\r
+ // Without an adoptNode option to maintain the child pointers, we have to either import the\r
+ // DOM while somehow reassigning all the nested references (which amounts to a complete\r
+ // *unmarshall* operation), or we just release the existing DOM and hope that we can get\r
+ // it back. This depends on all objects being able to preserve their DOM at all costs.\r
+ dc->releaseChildrenDOM(true);\r
+ dc->releaseDOM();\r
+ }\r
+ }\r
+ \r
+ // If we get here, we didn't have a usable DOM (and/or we released the one we had).\r
+ XT_log.debug("creating root element to marshall");\r
+ DOMElement* domElement = parentElement->getOwnerDocument()->createElementNS(\r
+ xmlObject->getElementQName().getNamespaceURI(), xmlObject->getElementQName().getLocalPart()\r
+ );\r
+ parentElement->appendChild(domElement);\r
+ marshallInto(xmlObject, domElement);\r
\r
//Recache the DOM.\r
if (dc) {\r
XT_log.debug("caching DOM for XMLObject");\r
- dc->setDOM(domElement, bindDocument);\r
+ dc->setDOM(domElement, false);\r
+ dc->releaseParentDOM(true);\r
}\r
\r
return domElement;\r
}\r
- \r
+ \r
+void AbstractXMLObjectMarshaller::marshallInto(XMLObject* xmlObject, DOMElement* targetElement) const\r
+{\r
+ targetElement->setPrefix(xmlObject->getElementQName().getPrefix());\r
+ marshallNamespaces(xmlObject, targetElement);\r
+ marshallAttributes(xmlObject, targetElement);\r
+ marshallChildElements(xmlObject, targetElement);\r
+ marshallElementContent(xmlObject, targetElement);\r
+ marshallElementType(xmlObject, targetElement);\r
+\r
+ /* TODO Signing/Encryption\r
+ if (xmlObject instanceof SignableXMLObject) {\r
+ signElement(targetElement, xmlObject);\r
+ }\r
+\r
+ if (xmlObject instanceof EncryptableXMLObject) {\r
+ encryptElement(targetElement, xmlObject);\r
+ }\r
+ */\r
+}\r
+\r
void AbstractXMLObjectMarshaller::marshallElementType(XMLObject* xmlObject, DOMElement* domElement) const\r
{\r
const QName* type = xmlObject->getSchemaType();\r
virtual ~AbstractXMLObjectMarshaller() {}\r
\r
/**\r
- * @see Marshaller::marshall()\r
+ * @see Marshaller::marshall(XMLObject*,DOMDocument*)\r
*/\r
DOMElement* marshall(XMLObject* xmlObject, DOMDocument* document=NULL) const;\r
+\r
+ /**\r
+ * @see Marshaller::marshall(XMLObject*,DOMElement*)\r
+ */\r
+ DOMElement* marshall(XMLObject* xmlObject, DOMElement* parentElement) const;\r
\r
\r
protected:\r
AbstractXMLObjectMarshaller(const XMLCh* targetNamespaceURI, const XMLCh* targetLocalName);\r
\r
/**\r
+ * Sets the given element as the Document Element of the given Document.\r
+ * If the document already has a Document Element it is replaced by the given element.\r
+ * \r
+ * @param document the document\r
+ * @param element the Element that will serve as the Document Element\r
+ */\r
+ void setDocumentElement(DOMDocument* document, DOMElement* element) const {\r
+ DOMElement* documentRoot = document->getDocumentElement();\r
+ if (documentRoot)\r
+ document->replaceChild(documentRoot, element);\r
+ else\r
+ document->appendChild(element);\r
+ }\r
+ \r
+ /**\r
+ * Marshalls the given XMLObject into the given DOM Element.\r
+ * The DOM Element must be within a DOM tree rooted in the owning Document.\r
+ * \r
+ * @param xmlObject the XMLObject to marshall\r
+ * @param targetElement the Element into which the XMLObject is marshalled into\r
+ * @throws MarshallingException thrown if there is a problem marshalling the object\r
+ */\r
+ void marshallInto(XMLObject* xmlObject, DOMElement* targetElement) const;\r
+ \r
+ /**\r
* Creates an xsi:type attribute, corresponding to the given type of the XMLObject, on the DOM element.\r
* \r
* @param xmlObject the XMLObject\r
virtual ~Marshaller() {}\r
\r
/**\r
- * Marshall this element, and its children, into a W3C DOM element rooted in a given document.\r
- * If no document is provided, then the marshaller will create one, and bind it to the\r
- * object being marshalled.\r
+ * Marshalls an object, and its children, into a DOM element.\r
+ * If a document is supplied, then it will be used to create the resulting elements.\r
+ * If the document does not have a Document Element set, then the resulting\r
+ * element will be set as the Document Element. If no document is supplied, then\r
+ * a new document will be created and bound to the lifetime of the root object being\r
+ * marshalled, unless an existing DOM can be reused without creating a new document. \r
* \r
* @param xmlObject the object to marshall\r
- * @param document the DOM document the marshalled element will be rooted in\r
- * \r
- * @return the W3C DOM element representing this XML element\r
- * \r
+ * @param document the DOM document the marshalled element will be placed in, or NULL\r
+ * @return the DOM element representing this XMLObject\r
* @throws MarshallingException thrown if there is a problem marshalling the given object\r
*/\r
virtual DOMElement* marshall(XMLObject* xmlObject, DOMDocument* document=NULL) const=0;\r
+ \r
+ /**\r
+ * Marshall the given XMLObject and append it as a child of the given parent element.\r
+ * \r
+ * <strong>NOTE:</strong> The given Element must be within a DOM tree rooted in \r
+ * the Document owning the given Element.\r
+ * \r
+ * @param xmlObject the XMLObject to be marshalled\r
+ * @param parentElement the parent element to append the resulting DOM tree\r
+ * @return the marshalled element tree\r
+ * @throws MarshallingException thrown if the given XMLObject can not be marshalled.\r
+ */\r
+ virtual DOMElement* marshall(XMLObject* xmlObject, DOMElement* parentElement) const=0;\r
\r
/**\r
* Retrieves a Marshaller using the key it was registered with.\r
* @param element the element for which to return an unmarshaller\r
* @return the unmarshaller or NULL\r
*/\r
- static const Unmarshaller* getUnmarshaller(const DOMElement* key);\r
+ static const Unmarshaller* getUnmarshaller(const DOMElement* element);\r
\r
/**\r
* Retrieves the default Unmarshaller for an unknown DOM element\r