--- /dev/null
+/*\r
+* Copyright 2001-2006 Internet2\r
+ * \r
+* Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+/**\r
+ * AbstractXMLObjectMarshaller.cpp\r
+ * \r
+ * A thread-safe abstract marshaller.\r
+ */\r
+\r
+#include "internal.h"\r
+#include "DOMCachingXMLObject.h"\r
+#include "exceptions.h"\r
+#include "io/AbstractXMLObjectMarshaller.h"\r
+#include "util/NDC.h"\r
+#include "util/XMLConstants.h"\r
+#include "util/XMLHelper.h"\r
+\r
+#include <algorithm>\r
+#include <functional>\r
+#include <xercesc/util/XMLUniDefs.hpp>\r
+#include <log4cpp/Category.hh>\r
+\r
+using namespace xmltooling;\r
+using namespace log4cpp;\r
+using namespace std;\r
+\r
+#define XT_log (*static_cast<Category*>(m_log))\r
+\r
+AbstractXMLObjectMarshaller::AbstractXMLObjectMarshaller(const XMLCh* targetNamespaceURI, const XMLCh* targetLocalName)\r
+ : m_targetQName(targetNamespaceURI, targetLocalName),\r
+ m_log(&Category::getInstance(XMLTOOLING_LOGCAT".Marshaller")) {\r
+ if (!targetLocalName || !*targetLocalName)\r
+ throw MarshallingException("targetLocalName cannot be null or empty");\r
+}\r
+ \r
+DOMElement* AbstractXMLObjectMarshaller::marshall(XMLObject* xmlObject, DOMDocument* document) const\r
+{\r
+#ifdef _DEBUG\r
+ xmltooling::NDC ndc("marshall");\r
+#endif\r
+\r
+ if (XT_log.isDebugEnabled()) {\r
+ XT_log.debug("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 (!document || document==cachedDOM->getOwnerDocument()) {\r
+ XT_log.debug("XMLObject has a usable cached DOM, using it.");\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 flushed the one we had).\r
+ // We may need to create our own document.\r
+ bool bindDocument=false;\r
+ if (!document) {\r
+ document=DOMImplementationRegistry::getDOMImplementation(NULL)->createDocument();\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
+ }\r
+ */\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
+ //Recache the DOM.\r
+ if (dc) {\r
+ XT_log.debug("caching DOM for XMLObject");\r
+ dc->setDOM(domElement, bindDocument);\r
+ }\r
+\r
+ return domElement;\r
+}\r
+ \r
+void AbstractXMLObjectMarshaller::marshallElementType(XMLObject* xmlObject, DOMElement* domElement) const\r
+{\r
+ const QName* type = xmlObject->getSchemaType();\r
+ if (type) {\r
+ XT_log.debug("setting xsi:type attribute for XMLObject");\r
+ \r
+ const XMLCh* typeLocalName = type->getLocalPart();\r
+ if (!typeLocalName || !*typeLocalName) {\r
+ throw MarshallingException("Schema type of XMLObject may not have an empty local name.");\r
+ }\r
+\r
+ static const XMLCh xsitype[] = {\r
+ chLatin_x, chLatin_s, chLatin_i, chColon, chLatin_t, chLatin_y, chLatin_p, chLatin_e, chNull\r
+ };\r
+ \r
+ XMLCh* xsivalue=const_cast<XMLCh*>(typeLocalName);\r
+ const XMLCh* prefix=type->getPrefix();\r
+ if (prefix && *prefix) {\r
+ xsivalue=new XMLCh[XMLString::stringLen(typeLocalName) + XMLString::stringLen(prefix) + 2*sizeof(XMLCh)];\r
+ *xsivalue=chNull;\r
+ XMLString::catString(xsivalue,prefix);\r
+ static const XMLCh colon[] = {chColon, chNull};\r
+ XMLString::catString(xsivalue,colon);\r
+ XMLString::catString(xsivalue,typeLocalName);\r
+ } \r
+ domElement->setAttributeNS(XMLConstants::XSI_NS, xsitype, xsivalue);\r
+ if (xsivalue != typeLocalName)\r
+ XMLString::release(&xsivalue);\r
+\r
+ XT_log.debug("Adding XSI namespace to list of namespaces used by XMLObject");\r
+ xmlObject->addNamespace(Namespace(XMLConstants::XSI_NS, XMLConstants::XSI_PREFIX));\r
+ }\r
+}\r
+\r
+class _addns : public binary_function<DOMElement*,Namespace,void> {\r
+public:\r
+ void operator()(DOMElement* domElement, const Namespace& ns) const {\r
+ const XMLCh* prefix=ns.getNamespacePrefix();\r
+ const XMLCh* uri=ns.getNamespaceURI();\r
+ if (prefix && *prefix) {\r
+ XMLCh* xmlns=new XMLCh[XMLString::stringLen(XMLConstants::XMLNS_PREFIX) + XMLString::stringLen(prefix) + 2*sizeof(XMLCh)];\r
+ *xmlns=chNull;\r
+ XMLString::catString(xmlns,XMLConstants::XMLNS_PREFIX);\r
+ static const XMLCh colon[] = {chColon, chNull};\r
+ XMLString::catString(xmlns,colon);\r
+ XMLString::catString(xmlns,prefix);\r
+ domElement->setAttributeNS(XMLConstants::XMLNS_NS, xmlns, uri);\r
+ }\r
+ else {\r
+ domElement->setAttributeNS(XMLConstants::XMLNS_NS, XMLConstants::XMLNS_PREFIX, uri);\r
+ }\r
+ }\r
+};\r
+\r
+void AbstractXMLObjectMarshaller::marshallNamespaces(const XMLObject* xmlObject, DOMElement* domElement) const\r
+{\r
+ XT_log.debug("marshalling namespace attributes for XMLObject");\r
+ const set<Namespace>& namespaces = xmlObject->getNamespaces();\r
+ for_each(namespaces.begin(),namespaces.end(),bind1st(_addns(),domElement));\r
+}\r
+\r
+class _marshallchild : public binary_function<XMLObject*,DOMElement*,void> {\r
+ void* m_log;\r
+public:\r
+ _marshallchild(void* log) : m_log(log) {}\r
+ void operator()(XMLObject* obj, DOMElement* element) const {\r
+ if (XT_log.isDebugEnabled()) {\r
+ XT_log.debug("getting marshaller for child XMLObject: %s", obj->getElementQName().toString().c_str());\r
+ }\r
+\r
+ const Marshaller* marshaller = Marshaller::getMarshaller(obj);\r
+ if (!marshaller) {\r
+ if (XMLToolingConfig::getConfig().ignoreUnknownElements) {\r
+ marshaller=Marshaller::getDefaultMarshaller();\r
+ if (marshaller)\r
+ XT_log.debug("using default marshaller");\r
+ else {\r
+ XT_log.error(\r
+ "no default unmarshaller installed, unknown child object: %s",\r
+ obj->getElementQName().toString().c_str()\r
+ );\r
+ throw MarshallingException("Marshaller found unknown child element, but no default marshaller was found.");\r
+ }\r
+ }\r
+ else {\r
+ XT_log.error("unknown child object: %s", obj->getElementQName().toString().c_str());\r
+ throw UnknownElementException("Marshaller found unknown child object.");\r
+ }\r
+ }\r
+ element->appendChild(marshaller->marshall(obj, element->getOwnerDocument()));\r
+ }\r
+};\r
+\r
+void AbstractXMLObjectMarshaller::marshallChildElements(const XMLObject* xmlObject, DOMElement* domElement) const\r
+{\r
+ XT_log.debug("marshalling child elements for XMLObject");\r
+\r
+ vector<XMLObject*> children;\r
+ if (xmlObject->getOrderedChildren(children)) {\r
+ for_each(children.begin(),children.end(),bind2nd(_marshallchild(m_log),domElement));\r
+ }\r
+}\r
--- /dev/null
+/*\r
+* Copyright 2001-2006 Internet2\r
+ * \r
+* Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+/**\r
+ * @file AbstractXMLObjectMarshaller.h\r
+ * \r
+ * A thread-safe abstract marshaller.\r
+ */\r
+\r
+#if !defined(__xmltooling_xmlmarshaller_h__)\r
+#define __xmltooling_xmlmarshaller_h__\r
+\r
+#include <xmltooling/io/Marshaller.h>\r
+\r
+namespace xmltooling {\r
+\r
+ /**\r
+ * A thread-safe abstract marshaller.\r
+ */\r
+ class XMLTOOL_API AbstractXMLObjectMarshaller : public virtual Marshaller\r
+ {\r
+ public:\r
+ virtual ~AbstractXMLObjectMarshaller() {}\r
+\r
+ /**\r
+ * @see Marshaller::marshall()\r
+ */\r
+ DOMElement* marshall(XMLObject* xmlObject, DOMDocument* document=NULL) const;\r
+ \r
+ \r
+ protected:\r
+ /**\r
+ * Constructor.\r
+ * \r
+ * @param targetNamespaceURI the namespace URI of either the schema type QName or element QName of the elements this\r
+ * marshaller operates on\r
+ * @param targetLocalName the local name of either the schema type QName or element QName of the elements this\r
+ * marshaller operates on\r
+ */\r
+ AbstractXMLObjectMarshaller(const XMLCh* targetNamespaceURI, const XMLCh* targetLocalName);\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
+ * @param domElement the DOM element\r
+ * \r
+ * @throws MarshallingException thrown if the type on the XMLObject is doesn't contain\r
+ * a local name, prefix, and namespace URI\r
+ */\r
+ void marshallElementType(XMLObject* xmlObject, DOMElement* domElement) const;\r
+\r
+ /**\r
+ * Creates the xmlns attributes for any namespaces set on the given XMLObject.\r
+ * \r
+ * @param xmlObject the XMLObject\r
+ * @param domElement the DOM element the namespaces will be added to\r
+ */\r
+ void marshallNamespaces(const XMLObject* xmlObject, DOMElement* domElement) const;\r
+ \r
+ /**\r
+ * Marshalls the child elements of the given XMLObject.\r
+ * \r
+ * @param xmlObject the XMLObject whose children will be marshalled\r
+ * @param domElement the DOM element that will recieved the marshalled children\r
+ * \r
+ * @throws MarshallingException thrown if there is a problem marshalling a child element\r
+ */\r
+ void marshallChildElements(const XMLObject* xmlObject, DOMElement* domElement) const;\r
+\r
+ /**\r
+ * Marshalls the attributes from the given XMLObject into the given DOM element.\r
+ * The XMLObject passed to this method is guaranteed to be of the target name\r
+ * specified during this marshaller's construction.\r
+ * \r
+ * @param xmlObject the XMLObject being marshalled\r
+ * @param domElement the DOM Element into which attributes will be marshalled\r
+ * \r
+ * @throws UnmarshallingException thrown if there is a problem unmarshalling an attribute\r
+ */\r
+ virtual void marshallAttributes(const XMLObject* xmlObject, DOMElement* domElement) const=0;\r
+\r
+ /**\r
+ * Marshalls data from the XMLObject into content of the DOM Element.\r
+ * \r
+ * @param xmlObject the XMLObject\r
+ * @param domElement the DOM element recieving the content\r
+ */\r
+ virtual void marshallElementContent(const XMLObject* xmlObject, DOMElement* domElement) const=0;\r
+\r
+ void* m_log;\r
+ private:\r
+ QName m_targetQName;\r
+ };\r
+ \r
+};\r
+\r
+#endif /* __xmltooling_xmlmarshaller_h__ */\r
XT_log.debug("unmarshalling DOM element %s", dname.get());\r
}\r
\r
-#ifdef _DEBUG\r
- checkElementIsTarget(element);\r
-#endif\r
-\r
XMLObject* xmlObject = buildXMLObject(element);\r
\r
if (element->hasAttributes()) {\r
return xmlObject;\r
}\r
\r
-void AbstractXMLObjectUnmarshaller::checkElementIsTarget(const DOMElement* domElement) const\r
-{\r
- auto_ptr<QName> elementName(XMLHelper::getNodeQName(domElement));\r
-\r
- XT_log.debug("checking that root element meets target criteria");\r
-\r
- auto_ptr<QName> type(XMLHelper::getXSIType(domElement));\r
-\r
- if (type.get() && m_targetQName==*(type.get())) {\r
- XT_log.debug("schema type of element matches target");\r
- return;\r
- }\r
- else {\r
- if (m_targetQName==*(elementName.get())) {\r
- XT_log.debug("element name matches target");\r
- return;\r
- }\r
- else {\r
- XT_log.errorStream() << "unmarshaller for (" << m_targetQName.toString()\r
- << ") passed (" << elementName->toString() << ")" << CategoryStream::ENDLINE;\r
- throw UnmarshallingException("Incorrect element type passed to unmarshaller.");\r
- }\r
- }\r
-}\r
-\r
XMLObject* AbstractXMLObjectUnmarshaller::buildXMLObject(const DOMElement* domElement) const\r
{\r
const XMLObjectBuilder* xmlObjectBuilder = XMLObjectBuilder::getBuilder(domElement);\r
unmarshaller=Unmarshaller::getDefaultUnmarshaller();\r
if (!unmarshaller) {\r
auto_ptr<QName> cname(XMLHelper::getNodeQName(childNode));\r
- XT_log.error("no default unmarshaller installed, detected unknown child element %s", cname->toString().c_str());\r
- throw UnmarshallingException("Unmarshaller detected unknown child element, but no default unmarshaller was found.");\r
+ XT_log.error(\r
+ "no default unmarshaller installed, found unknown child element %s",\r
+ cname->toString().c_str()\r
+ );\r
+ throw UnmarshallingException(\r
+ "Unmarshaller found unknown child element, but no default unmarshaller was found."\r
+ );\r
}\r
else {\r
XT_log.debug("using default unmarshaller");\r
else {\r
auto_ptr<QName> cname(XMLHelper::getNodeQName(childNode));\r
XT_log.error("detected unknown child element %s", cname->toString().c_str());\r
- throw UnknownElementException("Unmarshaller detected unknown child element.");\r
+ throw UnknownElementException("Unmarshaller found unknown child element.");\r
}\r
}\r
\r