+/*\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
+ * AbstractXMLObjectUnmarshaller.cpp\r
+ * \r
+ * A thread-safe abstract unmarshaller.\r
+ */\r
+\r
+#include "internal.h"\r
+#include "DOMCachingXMLObject.h"\r
+#include "exceptions.h"\r
+#include "XMLObjectBuilder.h"\r
+#include "io/AbstractXMLObjectUnmarshaller.h"\r
+#include "util/NDC.h"\r
+#include "util/XMLConstants.h"\r
+#include "util/XMLHelper.h"\r
+\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
+AbstractXMLObjectUnmarshaller::AbstractXMLObjectUnmarshaller(const XMLCh* targetNamespaceURI, const XMLCh* targetLocalName)\r
+ : m_targetQName(targetNamespaceURI, targetLocalName),\r
+ m_log(&Category::getInstance(XMLTOOLING_LOGCAT".Unmarshaller")) {\r
+ if (!targetLocalName || !*targetLocalName)\r
+ throw UnmarshallingException("targetLocalName cannot be null or empty");\r
+}\r
+\r
+XMLObject* AbstractXMLObjectUnmarshaller::unmarshall(DOMElement* element, bool bindDocument) const\r
+{\r
+#ifdef _DEBUG\r
+ xmltooling::NDC ndc("unmarshall");\r
+#endif\r
+\r
+ if (XT_log.isDebugEnabled()) {\r
+ auto_ptr_char dname(element->getLocalName());\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
+ unmarshallAttributes(element, xmlObject);\r
+ }\r
+\r
+ if (element->getTextContent()) {\r
+ processElementContent(xmlObject, element->getTextContent());\r
+ }\r
+\r
+ unmarshallChildElements(element, xmlObject);\r
+\r
+ /* TODO: Signing\r
+ if (xmlObject instanceof SignableXMLObject) {\r
+ verifySignature(domElement, xmlObject);\r
+ }\r
+ */\r
+\r
+ DOMCachingXMLObject* dc=dynamic_cast<DOMCachingXMLObject*>(xmlObject);\r
+ if (dc)\r
+ dc->setDOM(element,bindDocument);\r
+ \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
+ if (xmlObjectBuilder)\r
+ return xmlObjectBuilder->buildObject();\r
+ throw UnmarshallingException("Failed to locate XMLObjectBuilder for element.");\r
+}\r
+\r
+void AbstractXMLObjectUnmarshaller::unmarshallAttributes(const DOMElement* domElement, XMLObject* xmlObject) const\r
+{\r
+#ifdef _DEBUG\r
+ xmltooling::NDC ndc("unmarshallAttributes");\r
+#endif\r
+ static const XMLCh type[]={chLatin_t, chLatin_y, chLatin_p, chLatin_e, chNull};\r
+\r
+ if (XT_log.isDebugEnabled()) {\r
+ auto_ptr_char dname(domElement->getLocalName());\r
+ XT_log.debug("unmarshalling attributes for DOM element %s", dname.get());\r
+ }\r
+\r
+ DOMNamedNodeMap* attributes = domElement->getAttributes();\r
+ if (!attributes) {\r
+ XT_log.debug("no attributes to unmarshall");\r
+ return;\r
+ }\r
+\r
+ DOMNode* childNode;\r
+ DOMAttr* attribute;\r
+ for (XMLSize_t i=0; i<attributes->getLength(); i++) {\r
+ childNode = attributes->item(i);\r
+\r
+ // The child node should always be an attribute, but just in case\r
+ if (childNode->getNodeType() != DOMNode::ATTRIBUTE_NODE) {\r
+ XT_log.debug("encountered child node of type %d in attribute list, ignoring it", childNode->getNodeType());\r
+ continue;\r
+ }\r
+\r
+ attribute = static_cast<DOMAttr*>(childNode);\r
+ \r
+ const XMLCh* nsuri=attribute->getNamespaceURI();\r
+ if (!XMLString::compareString(nsuri,XMLConstants::XMLNS_NS)) {\r
+ XT_log.debug("found namespace declaration, adding it to the list of namespaces on the XMLObject");\r
+ xmlObject->addNamespace(Namespace(attribute->getValue(), attribute->getLocalName()));\r
+ continue;\r
+ }\r
+ else if (!XMLString::compareString(nsuri,XMLConstants::XSI_NS) &&\r
+ !XMLString::compareString(attribute->getLocalName(),type)) {\r
+ XT_log.debug("found xsi:type declaration, setting the schema type of the XMLObject");\r
+ auto_ptr<QName> xsitype(XMLHelper::getAttributeValueAsQName(attribute));\r
+ xmlObject->setSchemaType(xsitype.get());\r
+ continue;\r
+ }\r
+\r
+ XT_log.debug("processing generic attribute");\r
+ processAttribute(xmlObject, attribute);\r
+ }\r
+}\r
+\r
+void AbstractXMLObjectUnmarshaller::unmarshallChildElements(const DOMElement* domElement, XMLObject* xmlObject) const\r
+{\r
+#ifdef _DEBUG\r
+ xmltooling::NDC ndc("unmarshallChildElements");\r
+#endif\r
+\r
+ if (XT_log.isDebugEnabled()) {\r
+ auto_ptr_char dname(domElement->getLocalName());\r
+ XT_log.debug("unmarshalling child elements of DOM element %s", dname.get());\r
+ }\r
+\r
+ DOMNodeList* childNodes = domElement->getChildNodes();\r
+ DOMNode* childNode;\r
+ const Unmarshaller* unmarshaller;\r
+ if (!childNodes || childNodes->getLength()==0) {\r
+ XT_log.debug("element had no children");\r
+ return;\r
+ }\r
+\r
+ XMLToolingConfig& config=XMLToolingConfig::getConfig();\r
+ for (XMLSize_t i = 0; i < childNodes->getLength(); i++) {\r
+ childNode = childNodes->item(i);\r
+ if (childNode->getNodeType() == DOMNode::ELEMENT_NODE) {\r
+ unmarshaller = Unmarshaller::getUnmarshaller(static_cast<DOMElement*>(childNode));\r
+ if (!unmarshaller) {\r
+ if (config.ignoreUnknownElements) {\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
+ }\r
+ else {\r
+ XT_log.debug("using default unmarshaller");\r
+ }\r
+ }\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
+ }\r
+ }\r
+\r
+ if (XT_log.isDebugEnabled()) {\r
+ auto_ptr<QName> cname(XMLHelper::getNodeQName(childNode));\r
+ XT_log.debug("unmarshalling child element %s", cname->toString().c_str());\r
+ }\r
+ processChildElement(xmlObject, unmarshaller->unmarshall(static_cast<DOMElement*>(childNode)));\r
+ }\r
+ }\r
+}\r