From: cantor Date: Wed, 22 Feb 2006 18:02:55 +0000 (+0000) Subject: Initial marshalling support. X-Git-Tag: 1.4.1~793 X-Git-Url: http://www.project-moonshot.org/gitweb/?p=shibboleth%2Fxmltooling.git;a=commitdiff_plain;h=560b05d3f70fb3cc508c1f430198d6e545ef2326 Initial marshalling support. git-svn-id: https://svn.middleware.georgetown.edu/cpp-xmltooling/trunk@30 de75baf8-a10c-0410-a50a-987c0e22f00f --- diff --git a/xmltooling/AbstractDOMCachingXMLObject.cpp b/xmltooling/AbstractDOMCachingXMLObject.cpp index 62ea3e9..3a3d853 100644 --- a/xmltooling/AbstractDOMCachingXMLObject.cpp +++ b/xmltooling/AbstractDOMCachingXMLObject.cpp @@ -59,9 +59,6 @@ void AbstractDOMCachingXMLObject::releaseDOM() Category& log=Category::getInstance(XMLTOOLING_LOGCAT".DOM"); if (log.isDebugEnabled()) log.debug("Releasing cached DOM reprsentation for %s", getElementQName().toString().c_str()); - - // We don't get rid of the document we're holding, if any. - // The marshalling process deals with that. setDOM(NULL); } @@ -86,11 +83,11 @@ void AbstractDOMCachingXMLObject::releaseParentDOM(bool propagateRelease) class _release : public binary_function { public: void operator()(XMLObject* obj, bool propagate) const { - DOMCachingXMLObject* domCachingParent = dynamic_cast(obj); - if (domCachingParent) { - domCachingParent->releaseDOM(); + DOMCachingXMLObject* domCaching = dynamic_cast(obj); + if (domCaching) { + domCaching->releaseDOM(); if (propagate) - domCachingParent->releaseChildrenDOM(propagate); + domCaching->releaseChildrenDOM(propagate); } } }; diff --git a/xmltooling/AbstractDOMCachingXMLObject.h b/xmltooling/AbstractDOMCachingXMLObject.h index 4c90d81..35bd2e1 100644 --- a/xmltooling/AbstractDOMCachingXMLObject.h +++ b/xmltooling/AbstractDOMCachingXMLObject.h @@ -44,7 +44,7 @@ namespace xmltooling { /** * @see DOMCachingXMLObject::getDOM() */ - const DOMElement* getDOM() const { + DOMElement* getDOM() const { return m_dom; } @@ -92,8 +92,8 @@ namespace xmltooling { */ void releaseThisAndChildrenDOM() { if (m_dom) { - releaseDOM(); releaseChildrenDOM(true); + releaseDOM(); } } diff --git a/xmltooling/DOMCachingXMLObject.h b/xmltooling/DOMCachingXMLObject.h index bf14c8c..dfbe84c 100644 --- a/xmltooling/DOMCachingXMLObject.h +++ b/xmltooling/DOMCachingXMLObject.h @@ -44,7 +44,7 @@ namespace xmltooling { * * @return the DOM representation of this XMLObject */ - virtual const DOMElement* getDOM() const=0; + virtual DOMElement* getDOM() const=0; /** * Sets the DOM representation of this XMLObject. diff --git a/xmltooling/XMLObject.h b/xmltooling/XMLObject.h index 5ed3363..83e003e 100644 --- a/xmltooling/XMLObject.h +++ b/xmltooling/XMLObject.h @@ -143,7 +143,7 @@ namespace xmltooling { * @param v vector in which to store pointers to child objects * @return the number of children */ - virtual size_t getOrderedChildren(std::vector& v)=0; + virtual size_t getOrderedChildren(std::vector& v) const=0; }; }; diff --git a/xmltooling/io/AbstractXMLObjectMarshaller.cpp b/xmltooling/io/AbstractXMLObjectMarshaller.cpp new file mode 100644 index 0000000..7fd4e50 --- /dev/null +++ b/xmltooling/io/AbstractXMLObjectMarshaller.cpp @@ -0,0 +1,224 @@ +/* +* Copyright 2001-2006 Internet2 + * +* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * AbstractXMLObjectMarshaller.cpp + * + * A thread-safe abstract marshaller. + */ + +#include "internal.h" +#include "DOMCachingXMLObject.h" +#include "exceptions.h" +#include "io/AbstractXMLObjectMarshaller.h" +#include "util/NDC.h" +#include "util/XMLConstants.h" +#include "util/XMLHelper.h" + +#include +#include +#include +#include + +using namespace xmltooling; +using namespace log4cpp; +using namespace std; + +#define XT_log (*static_cast(m_log)) + +AbstractXMLObjectMarshaller::AbstractXMLObjectMarshaller(const XMLCh* targetNamespaceURI, const XMLCh* targetLocalName) + : m_targetQName(targetNamespaceURI, targetLocalName), + m_log(&Category::getInstance(XMLTOOLING_LOGCAT".Marshaller")) { + if (!targetLocalName || !*targetLocalName) + throw MarshallingException("targetLocalName cannot be null or empty"); +} + +DOMElement* AbstractXMLObjectMarshaller::marshall(XMLObject* xmlObject, DOMDocument* document) const +{ +#ifdef _DEBUG + xmltooling::NDC ndc("marshall"); +#endif + + if (XT_log.isDebugEnabled()) { + XT_log.debug("marshalling %s", xmlObject->getElementQName().toString().c_str()); + } + + DOMCachingXMLObject* dc=dynamic_cast(xmlObject); + if (dc) { + DOMElement* cachedDOM=dc->getDOM(); + if (cachedDOM) { + if (!document || document==cachedDOM->getOwnerDocument()) { + XT_log.debug("XMLObject has a usable cached DOM, using it."); + return cachedDOM; + } + + // We have a DOM but it doesn't match the document we were given. This both sucks and blows. + // Without an adoptNode option to maintain the child pointers, we have to either import the + // DOM while somehow reassigning all the nested references (which amounts to a complete + // *unmarshall* operation), or we just release the existing DOM and hope that we can get + // it back. This depends on all objects being able to preserve their DOM at all costs. + dc->releaseChildrenDOM(true); + dc->releaseDOM(); + } + } + + // If we get here, we didn't have a usable DOM (and/or we flushed the one we had). + // We may need to create our own document. + bool bindDocument=false; + if (!document) { + document=DOMImplementationRegistry::getDOMImplementation(NULL)->createDocument(); + bindDocument=true; + } + + XT_log.debug("creating root element to marshall"); + DOMElement* domElement = document->createElementNS( + xmlObject->getElementQName().getNamespaceURI(), xmlObject->getElementQName().getLocalPart() + ); + domElement->setPrefix(xmlObject->getElementQName().getPrefix()); + + try { + marshallNamespaces(xmlObject, domElement); + marshallAttributes(xmlObject, domElement); + marshallChildElements(xmlObject, domElement); + marshallElementContent(xmlObject, domElement); + marshallElementType(xmlObject, domElement); + + /* TODO: signing + if (xmlObject instanceof SignableXMLObject) { + signElement(domElement, xmlObject); + } + */ + } + catch (...) { + // Delete the document if need be, and rethrow. + if (bindDocument) { + document->release(); + document=NULL; + } + throw; + } + + //Recache the DOM. + if (dc) { + XT_log.debug("caching DOM for XMLObject"); + dc->setDOM(domElement, bindDocument); + } + + return domElement; +} + +void AbstractXMLObjectMarshaller::marshallElementType(XMLObject* xmlObject, DOMElement* domElement) const +{ + const QName* type = xmlObject->getSchemaType(); + if (type) { + XT_log.debug("setting xsi:type attribute for XMLObject"); + + const XMLCh* typeLocalName = type->getLocalPart(); + if (!typeLocalName || !*typeLocalName) { + throw MarshallingException("Schema type of XMLObject may not have an empty local name."); + } + + static const XMLCh xsitype[] = { + chLatin_x, chLatin_s, chLatin_i, chColon, chLatin_t, chLatin_y, chLatin_p, chLatin_e, chNull + }; + + XMLCh* xsivalue=const_cast(typeLocalName); + const XMLCh* prefix=type->getPrefix(); + if (prefix && *prefix) { + xsivalue=new XMLCh[XMLString::stringLen(typeLocalName) + XMLString::stringLen(prefix) + 2*sizeof(XMLCh)]; + *xsivalue=chNull; + XMLString::catString(xsivalue,prefix); + static const XMLCh colon[] = {chColon, chNull}; + XMLString::catString(xsivalue,colon); + XMLString::catString(xsivalue,typeLocalName); + } + domElement->setAttributeNS(XMLConstants::XSI_NS, xsitype, xsivalue); + if (xsivalue != typeLocalName) + XMLString::release(&xsivalue); + + XT_log.debug("Adding XSI namespace to list of namespaces used by XMLObject"); + xmlObject->addNamespace(Namespace(XMLConstants::XSI_NS, XMLConstants::XSI_PREFIX)); + } +} + +class _addns : public binary_function { +public: + void operator()(DOMElement* domElement, const Namespace& ns) const { + const XMLCh* prefix=ns.getNamespacePrefix(); + const XMLCh* uri=ns.getNamespaceURI(); + if (prefix && *prefix) { + XMLCh* xmlns=new XMLCh[XMLString::stringLen(XMLConstants::XMLNS_PREFIX) + XMLString::stringLen(prefix) + 2*sizeof(XMLCh)]; + *xmlns=chNull; + XMLString::catString(xmlns,XMLConstants::XMLNS_PREFIX); + static const XMLCh colon[] = {chColon, chNull}; + XMLString::catString(xmlns,colon); + XMLString::catString(xmlns,prefix); + domElement->setAttributeNS(XMLConstants::XMLNS_NS, xmlns, uri); + } + else { + domElement->setAttributeNS(XMLConstants::XMLNS_NS, XMLConstants::XMLNS_PREFIX, uri); + } + } +}; + +void AbstractXMLObjectMarshaller::marshallNamespaces(const XMLObject* xmlObject, DOMElement* domElement) const +{ + XT_log.debug("marshalling namespace attributes for XMLObject"); + const set& namespaces = xmlObject->getNamespaces(); + for_each(namespaces.begin(),namespaces.end(),bind1st(_addns(),domElement)); +} + +class _marshallchild : public binary_function { + void* m_log; +public: + _marshallchild(void* log) : m_log(log) {} + void operator()(XMLObject* obj, DOMElement* element) const { + if (XT_log.isDebugEnabled()) { + XT_log.debug("getting marshaller for child XMLObject: %s", obj->getElementQName().toString().c_str()); + } + + const Marshaller* marshaller = Marshaller::getMarshaller(obj); + if (!marshaller) { + if (XMLToolingConfig::getConfig().ignoreUnknownElements) { + marshaller=Marshaller::getDefaultMarshaller(); + if (marshaller) + XT_log.debug("using default marshaller"); + else { + XT_log.error( + "no default unmarshaller installed, unknown child object: %s", + obj->getElementQName().toString().c_str() + ); + throw MarshallingException("Marshaller found unknown child element, but no default marshaller was found."); + } + } + else { + XT_log.error("unknown child object: %s", obj->getElementQName().toString().c_str()); + throw UnknownElementException("Marshaller found unknown child object."); + } + } + element->appendChild(marshaller->marshall(obj, element->getOwnerDocument())); + } +}; + +void AbstractXMLObjectMarshaller::marshallChildElements(const XMLObject* xmlObject, DOMElement* domElement) const +{ + XT_log.debug("marshalling child elements for XMLObject"); + + vector children; + if (xmlObject->getOrderedChildren(children)) { + for_each(children.begin(),children.end(),bind2nd(_marshallchild(m_log),domElement)); + } +} diff --git a/xmltooling/io/AbstractXMLObjectMarshaller.h b/xmltooling/io/AbstractXMLObjectMarshaller.h new file mode 100644 index 0000000..e893b80 --- /dev/null +++ b/xmltooling/io/AbstractXMLObjectMarshaller.h @@ -0,0 +1,111 @@ +/* +* Copyright 2001-2006 Internet2 + * +* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file AbstractXMLObjectMarshaller.h + * + * A thread-safe abstract marshaller. + */ + +#if !defined(__xmltooling_xmlmarshaller_h__) +#define __xmltooling_xmlmarshaller_h__ + +#include + +namespace xmltooling { + + /** + * A thread-safe abstract marshaller. + */ + class XMLTOOL_API AbstractXMLObjectMarshaller : public virtual Marshaller + { + public: + virtual ~AbstractXMLObjectMarshaller() {} + + /** + * @see Marshaller::marshall() + */ + DOMElement* marshall(XMLObject* xmlObject, DOMDocument* document=NULL) const; + + + protected: + /** + * Constructor. + * + * @param targetNamespaceURI the namespace URI of either the schema type QName or element QName of the elements this + * marshaller operates on + * @param targetLocalName the local name of either the schema type QName or element QName of the elements this + * marshaller operates on + */ + AbstractXMLObjectMarshaller(const XMLCh* targetNamespaceURI, const XMLCh* targetLocalName); + + /** + * Creates an xsi:type attribute, corresponding to the given type of the XMLObject, on the DOM element. + * + * @param xmlObject the XMLObject + * @param domElement the DOM element + * + * @throws MarshallingException thrown if the type on the XMLObject is doesn't contain + * a local name, prefix, and namespace URI + */ + void marshallElementType(XMLObject* xmlObject, DOMElement* domElement) const; + + /** + * Creates the xmlns attributes for any namespaces set on the given XMLObject. + * + * @param xmlObject the XMLObject + * @param domElement the DOM element the namespaces will be added to + */ + void marshallNamespaces(const XMLObject* xmlObject, DOMElement* domElement) const; + + /** + * Marshalls the child elements of the given XMLObject. + * + * @param xmlObject the XMLObject whose children will be marshalled + * @param domElement the DOM element that will recieved the marshalled children + * + * @throws MarshallingException thrown if there is a problem marshalling a child element + */ + void marshallChildElements(const XMLObject* xmlObject, DOMElement* domElement) const; + + /** + * Marshalls the attributes from the given XMLObject into the given DOM element. + * The XMLObject passed to this method is guaranteed to be of the target name + * specified during this marshaller's construction. + * + * @param xmlObject the XMLObject being marshalled + * @param domElement the DOM Element into which attributes will be marshalled + * + * @throws UnmarshallingException thrown if there is a problem unmarshalling an attribute + */ + virtual void marshallAttributes(const XMLObject* xmlObject, DOMElement* domElement) const=0; + + /** + * Marshalls data from the XMLObject into content of the DOM Element. + * + * @param xmlObject the XMLObject + * @param domElement the DOM element recieving the content + */ + virtual void marshallElementContent(const XMLObject* xmlObject, DOMElement* domElement) const=0; + + void* m_log; + private: + QName m_targetQName; + }; + +}; + +#endif /* __xmltooling_xmlmarshaller_h__ */ diff --git a/xmltooling/io/AbstractXMLObjectUnmarshaller.cpp b/xmltooling/io/AbstractXMLObjectUnmarshaller.cpp index 9aefabc..ce3f358 100644 --- a/xmltooling/io/AbstractXMLObjectUnmarshaller.cpp +++ b/xmltooling/io/AbstractXMLObjectUnmarshaller.cpp @@ -56,10 +56,6 @@ XMLObject* AbstractXMLObjectUnmarshaller::unmarshall(DOMElement* element, bool b XT_log.debug("unmarshalling DOM element %s", dname.get()); } -#ifdef _DEBUG - checkElementIsTarget(element); -#endif - XMLObject* xmlObject = buildXMLObject(element); if (element->hasAttributes()) { @@ -85,31 +81,6 @@ XMLObject* AbstractXMLObjectUnmarshaller::unmarshall(DOMElement* element, bool b return xmlObject; } -void AbstractXMLObjectUnmarshaller::checkElementIsTarget(const DOMElement* domElement) const -{ - auto_ptr elementName(XMLHelper::getNodeQName(domElement)); - - XT_log.debug("checking that root element meets target criteria"); - - auto_ptr type(XMLHelper::getXSIType(domElement)); - - if (type.get() && m_targetQName==*(type.get())) { - XT_log.debug("schema type of element matches target"); - return; - } - else { - if (m_targetQName==*(elementName.get())) { - XT_log.debug("element name matches target"); - return; - } - else { - XT_log.errorStream() << "unmarshaller for (" << m_targetQName.toString() - << ") passed (" << elementName->toString() << ")" << CategoryStream::ENDLINE; - throw UnmarshallingException("Incorrect element type passed to unmarshaller."); - } - } -} - XMLObject* AbstractXMLObjectUnmarshaller::buildXMLObject(const DOMElement* domElement) const { const XMLObjectBuilder* xmlObjectBuilder = XMLObjectBuilder::getBuilder(domElement); @@ -197,8 +168,13 @@ void AbstractXMLObjectUnmarshaller::unmarshallChildElements(const DOMElement* do unmarshaller=Unmarshaller::getDefaultUnmarshaller(); if (!unmarshaller) { auto_ptr cname(XMLHelper::getNodeQName(childNode)); - XT_log.error("no default unmarshaller installed, detected unknown child element %s", cname->toString().c_str()); - throw UnmarshallingException("Unmarshaller detected unknown child element, but no default unmarshaller was found."); + XT_log.error( + "no default unmarshaller installed, found unknown child element %s", + cname->toString().c_str() + ); + throw UnmarshallingException( + "Unmarshaller found unknown child element, but no default unmarshaller was found." + ); } else { XT_log.debug("using default unmarshaller"); @@ -207,7 +183,7 @@ void AbstractXMLObjectUnmarshaller::unmarshallChildElements(const DOMElement* do else { auto_ptr cname(XMLHelper::getNodeQName(childNode)); XT_log.error("detected unknown child element %s", cname->toString().c_str()); - throw UnknownElementException("Unmarshaller detected unknown child element."); + throw UnknownElementException("Unmarshaller found unknown child element."); } } diff --git a/xmltooling/io/AbstractXMLObjectUnmarshaller.h b/xmltooling/io/AbstractXMLObjectUnmarshaller.h index fb45191..a0ec6f4 100644 --- a/xmltooling/io/AbstractXMLObjectUnmarshaller.h +++ b/xmltooling/io/AbstractXMLObjectUnmarshaller.h @@ -23,7 +23,6 @@ #if !defined(__xmltooling_xmlunmarshaller_h__) #define __xmltooling_xmlunmarshaller_h__ -#include #include namespace xmltooling { @@ -54,16 +53,6 @@ namespace xmltooling { AbstractXMLObjectUnmarshaller(const XMLCh* targetNamespaceURI, const XMLCh* targetLocalName); /** - * Checks that the given DOM Element's XSI type or namespace qualified element name matches the target QName of this - * unmarshaller. - * - * @param domElement the DOM element to check - * - * @throws UnmarshallingException thrown if the DOM Element does not match the target of this unmarshaller - */ - void checkElementIsTarget(const DOMElement* domElement) const; - - /** * Constructs the XMLObject that the given DOM Element will be unmarshalled into. If the DOM element has an XML * Schema type defined this method will attempt to retrieve an XMLObjectBuilder using the schema type. If no * schema type is present or no builder is registered for the schema type, the element's QName is used. Once diff --git a/xmltooling/xmltooling.vcproj b/xmltooling/xmltooling.vcproj index 6f02046..230a8fc 100644 --- a/xmltooling/xmltooling.vcproj +++ b/xmltooling/xmltooling.vcproj @@ -229,6 +229,10 @@ Name="io" > + + @@ -331,6 +335,10 @@ Name="io" > + +