Add parametrized messaging and serialization to exceptions.
authorScott Cantor <cantor.2@osu.edu>
Sun, 26 Mar 2006 21:30:32 +0000 (21:30 +0000)
committerScott Cantor <cantor.2@osu.edu>
Sun, 26 Mar 2006 21:30:32 +0000 (21:30 +0000)
17 files changed:
schemas/Makefile.am
schemas/catalog.xml.in
schemas/xmltooling.xsd [new file with mode: 0644]
xmltooling/Makefile.am
xmltooling/XMLToolingConfig.cpp
xmltooling/exceptions.cpp [new file with mode: 0644]
xmltooling/exceptions.h
xmltooling/util/ParserPool.cpp
xmltooling/util/XMLConstants.cpp
xmltooling/util/XMLConstants.h
xmltooling/util/XMLHelper.cpp
xmltooling/util/XMLHelper.h
xmltooling/xmltooling.vcproj
xmltoolingtest/ExceptionTest.h [new file with mode: 0644]
xmltoolingtest/Makefile.am
xmltoolingtest/SignatureTest.h
xmltoolingtest/xmltoolingtest.vcproj

index 6ffa021..d399785 100644 (file)
@@ -8,7 +8,8 @@ pkgxml_DATA = \
        catalog.xml \
        xenc-schema.xsd \
        xmldsig-core-schema.xsd \
-       xml.xsd
+       xml.xsd \
+       xmltooling.xsd
 
 #
 # Some of these need building
@@ -26,8 +27,9 @@ catalog.xml: ${srcdir}/catalog.xml.in Makefile ${top_builddir}/config.status
 CLEANFILES = catalog.xml
 
 EXTRA_DIST = \
-       catalog.xml.in \
-        xenc-schema.xsd \
-        xmldsig-core-schema.xsd \
-        xml.xsd
+    catalog.xml.in \
+    xenc-schema.xsd \
+    xmldsig-core-schema.xsd \
+    xml.xsd \
+    xmltooling.xsd
 
index fa19a59..2521ae0 100644 (file)
@@ -4,4 +4,5 @@
     <uri name="http://www.w3.org/XML/1998/namespace" uri="@-PKGXMLDIR-@/xml.xsd"/>
     <uri name="http://www.w3.org/2001/04/xmlenc#" uri="@-PKGXMLDIR-@/xenc-schema.xsd"/>
     <uri name="http://www.w3.org/2000/09/xmldsig#" uri="@-PKGXMLDIR-@/xmldsig-core-schema.xsd"/>
+    <uri name="http://www.opensaml.org/xmltooling" uri="@-PKGXMLDIR-@/xmltooling.xsd"/>
 </catalog>
diff --git a/schemas/xmltooling.xsd b/schemas/xmltooling.xsd
new file mode 100644 (file)
index 0000000..a532d4a
--- /dev/null
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<schema xmlns="http://www.w3.org/2001/XMLSchema" \r
+    xmlns:xt="http://www.opensaml.org/xmltooling"\r
+    targetNamespace="http://www.opensaml.org/xmltooling"
+    elementFormDefault="qualified">\r
+\r
+    <element name="exception" type="xt:XMLToolingExceptionType"/>\r
+    <complexType name="XMLToolingExceptionType">\r
+        <sequence>\r
+            <element name="message" type="string" minOccurs="0"/>
+            <element name="param" minOccurs="0" maxOccurs="unbounded">
+                <complexType>
+                    <simpleContent>
+                        <extension base="string">
+                            <attribute name="name" type="string" use="required"/>
+                        </extension>
+                    </simpleContent>
+                </complexType>
+            </element>\r
+        </sequence>\r
+        <attribute name="type" type="string" use="required"/>\r
+    </complexType>\r
+\r
+</schema>
\ No newline at end of file
index ab0661b..4e89a59 100644 (file)
@@ -72,6 +72,7 @@ libxmltooling_la_SOURCES = \
     AbstractDOMCachingXMLObject.cpp \
     AbstractElementProxy.cpp \
     AbstractXMLObject.cpp \
+    exceptions.cpp \
     Namespace.cpp \
     QName.cpp \
     unicode.cpp \
index 23c4146..a8680be 100644 (file)
@@ -21,6 +21,7 @@
  */
 
 #include "internal.h"
+#include "exceptions.h"
 #include "XMLToolingConfig.h"
 #include "impl/UnknownElement.h"
 #include "signature/impl/XMLSecSignature.h"
@@ -44,6 +45,15 @@ using namespace log4cpp;
 using namespace xmltooling;
 using namespace std;
 
+DECL_EXCEPTION_FACTORY(XMLParserException);
+DECL_EXCEPTION_FACTORY(XMLObjectException);
+DECL_EXCEPTION_FACTORY(MarshallingException);
+DECL_EXCEPTION_FACTORY(UnmarshallingException);
+DECL_EXCEPTION_FACTORY(UnknownElementException);
+DECL_EXCEPTION_FACTORY(UnknownAttributeException);
+DECL_EXCEPTION_FACTORY(ValidationException);
+DECL_EXCEPTION_FACTORY(SignatureException);
+
 namespace {
    XMLToolingInternalConfig g_config;
 }
@@ -143,6 +153,14 @@ bool XMLToolingInternalConfig::init()
 #ifndef XMLTOOLING_NO_XMLSEC
         XMLObjectBuilder::registerBuilder(QName(XMLConstants::XMLSIG_NS,Signature::LOCAL_NAME),new XMLSecSignatureBuilder());
 #endif
+        REGISTER_EXCEPTION_FACTORY(XMLParserException);
+        REGISTER_EXCEPTION_FACTORY(XMLObjectException);
+        REGISTER_EXCEPTION_FACTORY(MarshallingException);
+        REGISTER_EXCEPTION_FACTORY(UnmarshallingException);
+        REGISTER_EXCEPTION_FACTORY(UnknownElementException);
+        REGISTER_EXCEPTION_FACTORY(UnknownAttributeException);
+        REGISTER_EXCEPTION_FACTORY(ValidationException);
+        REGISTER_EXCEPTION_FACTORY(SignatureException);
     }
     catch (const xercesc::XMLException&) {
         log.fatal("caught exception while initializing Xerces");
diff --git a/xmltooling/exceptions.cpp b/xmltooling/exceptions.cpp
new file mode 100644 (file)
index 0000000..10e4e39
--- /dev/null
@@ -0,0 +1,274 @@
+/*\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
+ * exceptions.cpp\r
+ * \r
+ * Exception classes\r
+ */\r
\r
+#include "internal.h"\r
+#include "exceptions.h"\r
+#include "XMLToolingConfig.h"\r
+#include "util/XMLConstants.h"\r
+#include "util/XMLHelper.h"\r
+\r
+#include <sstream>\r
+#include <xercesc/util/XMLUniDefs.hpp>\r
+\r
+using namespace xmltooling;\r
+using namespace std;\r
+\r
+params::params(int count,...)\r
+{\r
+    va_list args;\r
+    va_start(args,count);\r
+    while (count--)\r
+        v.push_back(va_arg(args,char*));\r
+    va_end(args);\r
+}\r
+\r
+namedparams::namedparams(int count,...)\r
+{\r
+    count*=2;\r
+    va_list args;\r
+    va_start(args,count);\r
+    while (count--)\r
+        v.push_back(va_arg(args,char*));\r
+    va_end(args);\r
+}\r
+\r
+XMLToolingException::ExceptionFactoryMap XMLToolingException::m_factoryMap;\r
+\r
+XMLToolingException* XMLToolingException::getInstance(const char* exceptionClass)\r
+{\r
+    if (exceptionClass) {\r
+        ExceptionFactoryMap::const_iterator i=m_factoryMap.find(exceptionClass);\r
+        if (i!=m_factoryMap.end())\r
+            return (i->second)();\r
+    }\r
+    return new XMLToolingException();\r
+}\r
+\r
+XMLToolingException::XMLToolingException(const char* msg, const params& p)\r
+{\r
+    if (msg)\r
+        m_msg=msg;\r
+    addProperties(p);\r
+}\r
+\r
+XMLToolingException::XMLToolingException(const char* msg, const namedparams& p)\r
+{\r
+    if (msg)\r
+        m_msg=msg;\r
+    addProperties(p);\r
+}\r
+\r
+XMLToolingException::XMLToolingException(const std::string& msg, const params& p) : m_msg(msg)\r
+{\r
+    addProperties(p);\r
+}\r
+\r
+XMLToolingException::XMLToolingException(const std::string& msg, const namedparams& p) : m_msg(msg)\r
+{\r
+    addProperties(p);\r
+}\r
+\r
+void XMLToolingException::setMessage(const char* msg)\r
+{\r
+    if (msg)\r
+        m_msg=msg;\r
+    else\r
+        m_msg.erase();\r
+    m_processedmsg.erase();\r
+}\r
+\r
+inline const char* get_digit_character()\r
+{\r
+    static const char  s_characters[19] = \r
+    {\r
+            '9'\r
+        ,   '8'\r
+        ,   '7'\r
+        ,   '6'\r
+        ,   '5'\r
+        ,   '4'\r
+        ,   '3'\r
+        ,   '2'\r
+        ,   '1'\r
+        ,   '0'\r
+        ,   '1'\r
+        ,   '2'\r
+        ,   '3'\r
+        ,   '4'\r
+        ,   '5'\r
+        ,   '6'\r
+        ,   '7'\r
+        ,   '8'\r
+        ,   '9'\r
+    };\r
+    static const char  *s_mid  =   s_characters + 9;\r
+\r
+    return s_mid;\r
+}\r
+\r
+inline const char* unsigned_integer_to_string(char* buf, size_t cchBuf, int i)\r
+{\r
+    char* psz=buf + cchBuf - 1;     // Set psz to last char\r
+    *psz = 0;                       // Set terminating null\r
+\r
+    do {\r
+        unsigned int lsd = i % 10;  // Get least significant\r
+                                    // digit\r
+\r
+        i /= 10;                    // Prepare for next most\r
+                                    // significant digit\r
+\r
+        --psz;                      // Move back\r
+\r
+        *psz = get_digit_character()[lsd]; // Place the digit\r
+\r
+    } while(i!=0 && psz>buf);\r
+\r
+    return psz;\r
+}\r
+\r
+void XMLToolingException::addProperties(const params& p)\r
+{\r
+    m_processedmsg.erase();\r
+    int i=m_params.size()+1;\r
+    char buf[20];\r
+    const vector<const char*>& v=p.get();\r
+    for (vector<const char*>::const_iterator ci=v.begin(); ci!=v.end(); ci++) {\r
+        m_params[unsigned_integer_to_string(buf,sizeof(buf),i++)] = *ci;\r
+    }\r
+}\r
+        \r
+void XMLToolingException::addProperties(const namedparams& p)\r
+{\r
+    m_processedmsg.erase();\r
+    const vector<const char*>& v=p.get();\r
+    for (vector<const char*>::const_iterator ci=v.begin(); ci!=v.end(); ci++) {\r
+        m_params.erase(*ci);\r
+        m_params[*ci] = *(ci+1);\r
+        ci++;   // advance past name to value, then loop will advance it again\r
+    }\r
+}\r
+\r
+const char* XMLToolingException::getProperty(unsigned int index) const\r
+{\r
+    char buf[20];\r
+    map<string,string>::const_iterator i=m_params.find(unsigned_integer_to_string(buf,sizeof(buf),index));\r
+    return (i==m_params.end()) ? NULL : i->second.c_str();\r
+}\r
+\r
+const char* XMLToolingException::getProperty(const char* name) const\r
+{\r
+    map<string,string>::const_iterator i=m_params.find(name);\r
+    return (i==m_params.end()) ? NULL : i->second.c_str();\r
+}\r
+\r
+const char* XMLToolingException::getMessage() const\r
+{\r
+    if (!m_processedmsg.empty())\r
+        return m_processedmsg.c_str();\r
+    else if (m_params.empty())\r
+        return m_msg.c_str();\r
+\r
+    static const char* legal="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890_";\r
+\r
+    // Replace any parameters in the message.\r
+    string::size_type i=0,start=0;\r
+    while (start!=string::npos && start<m_msg.length() && (i=m_msg.find("$",start))!=string::npos) {\r
+        if (i>start)\r
+            m_processedmsg += m_msg.substr(start,i-start);  // append everything in between\r
+        start=i+1;                                  // move start to the beginning of the token name\r
+        i=m_msg.find_first_not_of(legal,start);     // find token delimiter\r
+        if (i==start) {                             // append a non legal character\r
+           m_processedmsg+=m_msg[start++];\r
+           continue;\r
+        }\r
+        \r
+        // search for token in map\r
+        map<string,string>::const_iterator param=m_params.find(m_msg.substr(start,(i==string::npos) ? i : i-start));\r
+        if (param!=m_params.end()) {\r
+            m_processedmsg+=param->second;\r
+            start=i;\r
+        }\r
+    }\r
+    if (start!=string::npos && start<m_msg.length())\r
+        m_processedmsg += m_msg.substr(start,i);    // append rest of string\r
+    return m_processedmsg.c_str();\r
+}\r
+\r
+string XMLToolingException::toString() const\r
+{\r
+    string xml=string("<exception xmlns=\"http://www.opensaml.org/xmltooling\" type=\"") + getClassName() + "\">";\r
+    const char* msg=getMessage();\r
+    if (msg)\r
+        xml=xml + "<message>" + msg + "</message>";\r
+    for (map<string,string>::const_iterator i=m_params.begin(); i!=m_params.end(); i++) {\r
+        xml=xml + "<param name=\"" + i->first + "\">" + i->second + "</param>";\r
+    }\r
+    xml+="</exception>";\r
+    return xml;\r
+}\r
+\r
+XMLToolingException* XMLToolingException::fromStream(std::istream& in)\r
+{\r
+    static const XMLCh exception[] = { chLatin_e, chLatin_x, chLatin_c, chLatin_e, chLatin_p, chLatin_t, chLatin_i, chLatin_o, chLatin_n, chNull };\r
+    static const XMLCh message[] = { chLatin_m, chLatin_e, chLatin_s, chLatin_s, chLatin_a, chLatin_g, chLatin_e, chNull };\r
+    static const XMLCh name[] = { chLatin_n, chLatin_a, chLatin_m, chLatin_e, chNull };\r
+    static const XMLCh param[] = { chLatin_p, chLatin_a, chLatin_r, chLatin_a, chLatin_m, chNull };\r
+    static const XMLCh type[] = { chLatin_t, chLatin_y, chLatin_p, chLatin_e, chNull };\r
+\r
+    DOMDocument* doc=XMLToolingInternalConfig::getInternalConfig().m_parserPool->parse(in);\r
+    \r
+    // Check root element.\r
+    const DOMElement* root=doc->getDocumentElement();\r
+    if (!XMLHelper::isNodeNamed(root,XMLConstants::XMLTOOLING_NS,exception)) {\r
+        doc->release();\r
+        throw XMLToolingException("Invalid root element on serialized exception.");\r
+    }\r
+    \r
+    auto_ptr_char classname(root->getAttributeNS(NULL,type));\r
+    auto_ptr<XMLToolingException> excep(XMLToolingException::getInstance(classname.get()));\r
+    \r
+    DOMElement* child=XMLHelper::getFirstChildElement(root,XMLConstants::XMLTOOLING_NS,message);\r
+    if (child && child->hasChildNodes()) {\r
+        auto_ptr_char m(child->getFirstChild()->getNodeValue());\r
+        excep->setMessage(m.get());\r
+    }\r
+    \r
+    child=XMLHelper::getFirstChildElement(root,XMLConstants::XMLTOOLING_NS,param);\r
+    while (child && child->hasChildNodes()) {\r
+        auto_ptr_char n(child->getAttributeNS(NULL,name));\r
+        char* v=toUTF8(child->getFirstChild()->getNodeValue());\r
+        if (n.get() && v)\r
+            excep->addProperty(n.get(), v);\r
+        XMLString::release(&v);\r
+        child=XMLHelper::getNextSiblingElement(root,XMLConstants::XMLTOOLING_NS,param);\r
+    }\r
+\r
+    doc->release();\r
+    return excep.release();\r
+}\r
+        \r
+XMLToolingException* XMLToolingException::fromString(const char* s)\r
+{\r
+    istringstream in(s);\r
+    return fromStream(in);\r
+}\r
index f527350..39ee359 100644 (file)
 #if !defined(__xmltooling_exceptions_h__)\r
 #define __xmltooling_exceptions_h__\r
 \r
+#include <map>\r
 #include <string>\r
+#include <vector>\r
+#include <iostream>\r
 #include <xmltooling/base.h>\r
 \r
-#define DECL_XMLTOOLING_EXCEPTION(type) \\r
-    class XMLTOOL_EXCEPTIONAPI(XMLTOOL_API) type : public XMLToolingException { \\r
+/**\r
+ * Declares a derived exception class\r
+ * \r
+ * @param name  the exception class\r
+ * @param base  the base class\r
+ */\r
+#define DECL_XMLTOOLING_EXCEPTION(name,base) \\r
+    class XMLTOOL_EXCEPTIONAPI(XMLTOOL_API) name : public xmltooling::base { \\r
     public: \\r
-        type(const char* const msg) : XMLToolingException(msg) {} \\r
-        type(const std::string& msg) : XMLToolingException(msg) {} \\r
-        virtual ~type() {} \\r
+        name(const char* msg=NULL, const xmltooling::params& p=xmltooling::params()) \\r
+            : xmltooling::base(msg,p) {} \\r
+        name(const char* msg, const xmltooling::namedparams& p) \\r
+            : xmltooling::base(msg,p) {} \\r
+        name(const std::string& msg, const xmltooling::params& p=xmltooling::params()) \\r
+            : xmltooling::base(msg,p) {} \\r
+        name(const std::string& msg, const xmltooling::namedparams& p) \\r
+            : xmltooling::base(msg,p) {} \\r
+        virtual ~name() {} \\r
+        virtual const char* getClassName() const { return "xmltooling::"#name; } \\r
+        void raise() const {throw *this;} \\r
+    }\r
+\r
+/**\r
+ * Declares a factory function for an exception class.\r
+ * \r
+ * @param name  the exception class name\r
+ */\r
+#define DECL_EXCEPTION_FACTORY(name) \\r
+    xmltooling::XMLToolingException* name##Factory() \\r
+    { \\r
+        return new xmltooling::name(); \\r
     }\r
 \r
+/**\r
+ * Registers a factory for an exception class.\r
+ * \r
+ * @param name  the exception class name\r
+ */\r
+#define REGISTER_EXCEPTION_FACTORY(name) XMLToolingException::registerFactory("xmltooling::"#name,name##Factory)\r
+\r
 namespace xmltooling {\r
     \r
     /**\r
-     * Base exception class.\r
-     * std::exception seems to be inconsistently defined, so this is just\r
-     * a substitute base class.\r
+     * Wrapper around a variable number of arguments.\r
      */\r
+    class XMLTOOL_API params\r
+    {\r
+    public:\r
+        /**\r
+         * Initializes with zero parameters.\r
+         */\r
+        params() {}\r
+        \r
+        /**\r
+         * Initializes the parameter set.\r
+         * \r
+         * @param count     the number of parameters that follow\r
+         */\r
+        params(int count,...);\r
+        \r
+        /**\r
+         * Returns an immutable reference to the set of parameters.\r
+         * \r
+         * @return the parameter set\r
+         */\r
+        const std::vector<const char*>& get() const {return v;}\r
+        \r
+    protected:\r
+        std::vector<const char*> v;\r
+    };\r
+    \r
+    /**\r
+     * Wrapper around a variable number of name/value pairs.\r
+     */\r
+    class XMLTOOL_API namedparams : public params\r
+    {\r
+    public:\r
+        /**\r
+         * Initializes with zero parameters.\r
+         */\r
+        namedparams() {}\r
+\r
+        /**\r
+         * Initializes the named parameter set.\r
+         * \r
+         * @param count     the number of name/value pairs that follow (must be even)\r
+         */\r
+        namedparams(int count,...);\r
+    };\r
+\r
+    /**\r
+     * Base exception class, supports parametrized messages and XML serialization.\r
+     * Parameters are prefixed with a dollar sign ($) and can be positional ($1)\r
+     * or named ($info).\r
+     */\r
+    class XMLTOOL_EXCEPTIONAPI(XMLTOOL_API) XMLToolingException;\r
+    typedef XMLToolingException* ExceptionFactory();\r
+    \r
     class XMLTOOL_EXCEPTIONAPI(XMLTOOL_API) XMLToolingException\r
     {\r
     public:\r
-        XMLToolingException() {}\r
         virtual ~XMLToolingException() {}\r
-        XMLToolingException(const char* const msg) : m_msg(msg) {}\r
-        XMLToolingException(const std::string& msg) : m_msg(msg) {}\r
-        virtual const char* what() const { return m_msg.c_str(); }\r
+\r
+        /**\r
+         * Constructs an exception using a message and positional parameters.\r
+         * \r
+         * @param msg   error message\r
+         * @param p     an ordered set of positional parameter strings\r
+         */\r
+        XMLToolingException(const char* msg=NULL, const params& p=params());\r
+\r
+        /**\r
+         * Constructs an exception using a message and named parameters.\r
+         * \r
+         * @param msg   error message\r
+         * @param p     a set of named parameter strings\r
+         */\r
+        XMLToolingException(const char* msg, const namedparams& p);\r
+\r
+        /**\r
+         * Constructs an exception using a message and positional parameters.\r
+         * \r
+         * @param msg   error message\r
+         * @param p     an ordered set of positional parameter strings\r
+         */\r
+        XMLToolingException(const std::string& msg, const params& p=params());\r
+\r
+        /**\r
+         * Constructs an exception using a message and named parameters.\r
+         * \r
+         * @param msg   error message\r
+         * @param p     a set of named parameter strings\r
+         */\r
+        XMLToolingException(const std::string& msg, const namedparams& p);\r
+\r
+        /**\r
+         * Returns the error message, after processing any parameter references.\r
+         * \r
+         * @return  the processed message\r
+         */\r
+        const char* getMessage() const;\r
+\r
+        /**\r
+         * Returns the error message, after processing any parameter references.\r
+         * \r
+         * @return  the processed message\r
+         */\r
+        const char* what() const {return getMessage();}\r
+\r
+        /**\r
+         * Sets the error message.\r
+         * \r
+         * @param msg   the error message\r
+         */\r
+        void setMessage(const char* msg);\r
+\r
+        /**\r
+         * Sets the error message.\r
+         * \r
+         * @param msg   the error message\r
+         */\r
+        void setMessage(const std::string& msg) {\r
+            setMessage(msg.c_str());\r
+        }\r
+\r
+        /**\r
+         * Attach a set of positional parameters to the exception.\r
+         * \r
+         * @param p     an ordered set of named parameter strings\r
+         */\r
+        void addProperties(const params& p);\r
+        \r
+        /**\r
+         * Attach a set of named parameters to the exception.\r
+         * \r
+         * @param p     a set of named parameter strings\r
+         */\r
+        void addProperties(const namedparams& p);\r
+\r
+        /**\r
+         * Attach a single positional parameter at the next available position.\r
+         * \r
+         * @param value the parameter value\r
+         */\r
+        void addProperty(const char* value) {\r
+            addProperties(params(1,value));\r
+        }\r
+\r
+        /**\r
+         * Attach a single named parameter.\r
+         * \r
+         * @param name  the parameter name\r
+         * @param value the parameter value\r
+         */\r
+        void addProperty(const char* name, const char* value) {\r
+            addProperties(namedparams(1,name,value));\r
+        }\r
+\r
+        /**\r
+         * Returns the parameter property with the designated position (based from one).\r
+         * \r
+         * @param index     position to access\r
+         * @return  the parameter property or NULL\r
+         */\r
+        const char* getProperty(unsigned int index) const;\r
+\r
+        /**\r
+         * Returns the parameter property with the designated name.\r
+         * \r
+         * @param name     named parameter to access\r
+         * @return  the parameter property or NULL\r
+         */\r
+        const char* getProperty(const char* name) const;\r
+\r
+        /**\r
+         * Raises an exception using itself.\r
+         * Used to raise an exception of a derived type.\r
+         */\r
+        virtual void raise() const {\r
+            throw *this;\r
+        }\r
+\r
+        /**\r
+         * Returns a unique name for the exception class.\r
+         * \r
+         * @return class name\r
+         */\r
+        virtual const char* getClassName() const {\r
+            return "xmltooling::XMLToolingException";\r
+        }\r
+        \r
+        /**\r
+         * Returns a string containing a serialized representation of the exception.\r
+         * \r
+         * @return  the serialization\r
+         */\r
+        std::string toString() const;\r
+\r
     private:\r
         std::string m_msg;\r
+        mutable std::string m_processedmsg;\r
+        std::map<std::string,std::string> m_params;\r
+\r
+    public:\r
+        /**\r
+         * Builds an empty exception of the given type.\r
+         * \r
+         * @param exceptionClass    the name of the exception type to build\r
+         * @return an empty exception object\r
+         */\r
+        static XMLToolingException* getInstance(const char* exceptionClass);\r
+\r
+        /**\r
+         * Builds an exception from a serialized input stream.\r
+         * \r
+         * @param in    input stream\r
+         * @return the exception object found in the stream\r
+         */\r
+        static XMLToolingException* fromStream(std::istream& in);\r
+        \r
+        /**\r
+         * Builds an exception from a serialized input buffer.\r
+         * \r
+         * @param s   input buffer\r
+         * @return the exception object found in the buffer\r
+         */\r
+        static XMLToolingException* fromString(const char* s);\r
+                \r
+        /**\r
+         * Registers a factory to create exceptions of a given class name.\r
+         * \r
+         * @param exceptionClass    name of exception type\r
+         * @param factory           factory function to build exceptions with\r
+         */\r
+        static void registerFactory(const char* exceptionClass, ExceptionFactory* factory) {\r
+            m_factoryMap[exceptionClass] = factory;\r
+        }\r
+        \r
+        /**\r
+         * Unregisters the factory for a given class name.\r
+         * \r
+         * @param exceptionClass    name of exception type\r
+         */\r
+        static void deregisterFactory(const char* exceptionClass) {\r
+            m_factoryMap.erase(exceptionClass);\r
+        }\r
+\r
+    private:\r
+        typedef std::map<std::string,ExceptionFactory*> ExceptionFactoryMap;\r
+        static ExceptionFactoryMap m_factoryMap;\r
     };\r
 \r
-    DECL_XMLTOOLING_EXCEPTION(XMLParserException);\r
-    DECL_XMLTOOLING_EXCEPTION(XMLObjectException);\r
-    DECL_XMLTOOLING_EXCEPTION(MarshallingException);\r
-    DECL_XMLTOOLING_EXCEPTION(UnmarshallingException);\r
-    DECL_XMLTOOLING_EXCEPTION(UnknownElementException);\r
-    DECL_XMLTOOLING_EXCEPTION(UnknownAttributeException);\r
-    DECL_XMLTOOLING_EXCEPTION(ValidationException);\r
-    DECL_XMLTOOLING_EXCEPTION(SignatureException);\r
+    DECL_XMLTOOLING_EXCEPTION(XMLParserException,XMLToolingException);\r
+    DECL_XMLTOOLING_EXCEPTION(XMLObjectException,XMLToolingException);\r
+    DECL_XMLTOOLING_EXCEPTION(MarshallingException,XMLToolingException);\r
+    DECL_XMLTOOLING_EXCEPTION(UnmarshallingException,XMLToolingException);\r
+    DECL_XMLTOOLING_EXCEPTION(UnknownElementException,XMLToolingException);\r
+    DECL_XMLTOOLING_EXCEPTION(UnknownAttributeException,XMLToolingException);\r
+    DECL_XMLTOOLING_EXCEPTION(ValidationException,XMLToolingException);\r
+    DECL_XMLTOOLING_EXCEPTION(SignatureException,XMLToolingException);\r
 \r
 };\r
 \r
index 3eb039d..5bec638 100644 (file)
@@ -169,6 +169,7 @@ bool ParserPool::loadCatalog(const XMLCh* pathname)
         if (!XMLHelper::isNodeNamed(root,CATALOG_NS,catalog)) {\r
             auto_ptr_char temp(pathname);\r
             log.error("unknown root element, failed to load XML catalog from %s", temp.get());\r
+            doc->release();\r
             return false;\r
         }\r
         \r
@@ -194,6 +195,7 @@ bool ParserPool::loadCatalog(const XMLCh* pathname)
         for_each(m_schemaLocMap.begin(),m_schemaLocMap.end(),doubleit<string>(m_schemaLocations,' '));\r
 #endif\r
         XMLPlatformUtils::unlockMutex(m_lock);\r
+        doc->release();\r
     }\r
     catch (XMLParserException& e) {\r
         log.error("catalog loader caught XMLParserException: %s", e.what());\r
index 72ab14f..a5974b9 100644 (file)
@@ -82,3 +82,11 @@ const XMLCh XMLConstants::XMLENC_NS[] = // http://www.w3.org/2001/04/xmlenc#
 };\r
 \r
 const XMLCh XMLConstants::XMLENC_PREFIX[] = { chLatin_x, chLatin_e, chLatin_n, chLatin_c, chNull };\r
+\r
+const XMLCh XMLConstants::XMLTOOLING_NS[] = // http://www.opensaml.org/xmltooling\r
+{ chLatin_h, chLatin_t, chLatin_t, chLatin_p, chColon, chForwardSlash, chForwardSlash,\r
+  chLatin_w, chLatin_w, chLatin_w, chPeriod,\r
+  chLatin_o, chLatin_p, chLatin_e, chLatin_n, chLatin_s, chLatin_a, chLatin_m, chLatin_l, chPeriod,\r
+  chLatin_o, chLatin_r, chLatin_g, chForwardSlash,\r
+  chLatin_x, chLatin_m, chLatin_l, chLatin_t, chLatin_o, chLatin_o, chLatin_l, chLatin_i, chLatin_n, chLatin_g, chNull\r
+};\r
index a503fb7..1faf640 100644 (file)
@@ -67,6 +67,9 @@ namespace xmltooling {
         \r
         /**  XML Encryption QName prefix ("xenc") */\r
         static const XMLCh XMLENC_PREFIX[];\r
+        \r
+        /**  XML Tooling namespace ("http://www.opensaml.org/xmltooling") */\r
+        static const XMLCh XMLTOOLING_NS[];\r
     };\r
 \r
 };\r
index 0c3691e..74e09b3 100644 (file)
@@ -128,6 +128,78 @@ const XMLCh* XMLHelper::getTextContent(const DOMElement* e)
     return NULL;\r
 }\r
 \r
+DOMElement* XMLHelper::getFirstChildElement(const DOMNode* n)\r
+{\r
+    DOMNode* child = n->getFirstChild();\r
+    while (child && child->getNodeType() != DOMNode::ELEMENT_NODE)\r
+        child = child->getNextSibling();\r
+    if (child)\r
+        return static_cast<DOMElement*>(child);\r
+    return NULL;\r
+}    \r
+\r
+DOMElement* XMLHelper::getLastChildElement(const DOMNode* n)\r
+{\r
+    DOMNode* child = n->getLastChild();\r
+    while (child && child->getNodeType() != DOMNode::ELEMENT_NODE)\r
+        child = child->getPreviousSibling();\r
+    if (child)\r
+        return static_cast<DOMElement*>(child);\r
+    return NULL;\r
+}    \r
+\r
+DOMElement* XMLHelper::getFirstChildElement(const DOMNode* n, const XMLCh* ns, const XMLCh* localName)\r
+{\r
+    DOMElement* e = getFirstChildElement(n);\r
+    while (e && !isNodeNamed(e, ns, localName))\r
+        e = getNextSiblingElement(e);\r
+    return e;\r
+}\r
+\r
+DOMElement* XMLHelper::getLastChildElement(const DOMNode* n, const XMLCh* ns, const XMLCh* localName)\r
+{\r
+    DOMElement* e = getLastChildElement(n);\r
+    while (e && !isNodeNamed(e, ns, localName))\r
+        e = getPreviousSiblingElement(e);\r
+    return e;\r
+}\r
+\r
+DOMElement* XMLHelper::getNextSiblingElement(const DOMNode* n)\r
+{\r
+    DOMNode* sib = n->getNextSibling();\r
+    while (sib && sib->getNodeType() != DOMNode::ELEMENT_NODE)\r
+        sib = sib->getNextSibling();\r
+    if (sib)\r
+        return static_cast<DOMElement*>(sib);\r
+    return NULL;\r
+}\r
+\r
+DOMElement* XMLHelper::getPreviousSiblingElement(const DOMNode* n)\r
+{\r
+    DOMNode* sib = n->getPreviousSibling();\r
+    while (sib && sib->getNodeType() != DOMNode::ELEMENT_NODE)\r
+        sib = sib->getPreviousSibling();\r
+    if (sib)\r
+        return static_cast<DOMElement*>(sib);\r
+    return NULL;\r
+}\r
+\r
+DOMElement* XMLHelper::getNextSiblingElement(const DOMNode* n, const XMLCh* ns, const XMLCh* localName)\r
+{\r
+    DOMElement* e = getNextSiblingElement(n);\r
+    while (e && !isNodeNamed(e, ns, localName))\r
+        e = getNextSiblingElement(e);\r
+    return e;\r
+}\r
+\r
+DOMElement* XMLHelper::getPreviousSiblingElement(const DOMNode* n, const XMLCh* ns, const XMLCh* localName)\r
+{\r
+    DOMElement* e = getPreviousSiblingElement(n);\r
+    while (e && !isNodeNamed(e, ns, localName))\r
+        e = getPreviousSiblingElement(e);\r
+    return e;\r
+}\r
+\r
 void XMLHelper::serialize(const DOMElement* e, std::string& buf)\r
 {\r
     static const XMLCh impltype[] = { chLatin_L, chLatin_S, chNull };\r
index 99697e6..ac6f240 100644 (file)
@@ -99,6 +99,78 @@ namespace xmltooling {
         }\r
 \r
         /**\r
+         * Returns the first child element of the node if any.\r
+         * \r
+         * @param n     node to check\r
+         * @return  the first child node of type Element, or NULL\r
+         */\r
+        static DOMElement* getFirstChildElement(const DOMNode* n);\r
+        \r
+        /**\r
+         * Returns the last child element of the node if any.\r
+         * \r
+         * @param n     node to check\r
+         * @return  the last child node of type Element, or NULL\r
+         */\r
+        static DOMElement* getLastChildElement(const DOMNode* n);\r
+        \r
+        /**\r
+         * Returns the next sibling element of the node if any.\r
+         * \r
+         * @param n     node to check\r
+         * @return  the next sibling node of type Element, or NULL\r
+         */\r
+        static DOMElement* getNextSiblingElement(const DOMNode* n);\r
+        \r
+        /**\r
+         * Returns the previous sibling element of the node if any.\r
+         * \r
+         * @param n     node to check\r
+         * @return  the previous sibling node of type Element, or NULL\r
+         */\r
+        static DOMElement* getPreviousSiblingElement(const DOMNode* n);\r
+        \r
+        /**\r
+         * Returns the first matching child element of the node if any.\r
+         * \r
+         * @param n         node to check\r
+         * @param ns        namespace to compare with\r
+         * @param localName local name to compare with\r
+         * @return  the first matching child node of type Element, or NULL\r
+         */\r
+        static DOMElement* getFirstChildElement(const DOMNode* n, const XMLCh* ns, const XMLCh* localName);\r
+        \r
+        /**\r
+         * Returns the last matching child element of the node if any.\r
+         * \r
+         * @param n         node to check\r
+         * @param ns        namespace to compare with\r
+         * @param localName local name to compare with\r
+         * @return  the last matching child node of type Element, or NULL\r
+         */\r
+        static DOMElement* getLastChildElement(const DOMNode* n, const XMLCh* ns, const XMLCh* localName);\r
+        \r
+        /**\r
+         * Returns the next matching sibling element of the node if any.\r
+         * \r
+         * @param n         node to check\r
+         * @param ns        namespace to compare with\r
+         * @param localName local name to compare with\r
+         * @return  the next matching sibling node of type Element, or NULL\r
+         */\r
+        static DOMElement* getNextSiblingElement(const DOMNode* n, const XMLCh* ns, const XMLCh* localName);\r
+        \r
+        /**\r
+         * Returns the previous matching sibling element of the node if any.\r
+         * \r
+         * @param n         node to check\r
+         * @param ns        namespace to compare with\r
+         * @param localName local name to compare with\r
+         * @return  the previous matching sibling node of type Element, or NULL\r
+         */\r
+        static DOMElement* getPreviousSiblingElement(const DOMNode* n, const XMLCh* ns, const XMLCh* localName);\r
+\r
+        /**\r
          * Returns the content of the first Text node found in the element, if any.\r
          * This is roughly similar to the DOM getTextContent function, but only\r
          * examples the immediate children of the element.\r
index 5c94f40..d095368 100644 (file)
                                >\r
                        </File>\r
                        <File\r
+                               RelativePath=".\exceptions.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
                                RelativePath=".\Namespace.cpp"\r
                                >\r
                        </File>\r
diff --git a/xmltoolingtest/ExceptionTest.h b/xmltoolingtest/ExceptionTest.h
new file mode 100644 (file)
index 0000000..d681577
--- /dev/null
@@ -0,0 +1,52 @@
+/*\r
+ *  Copyright 2001-2005 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
+#include "XMLObjectBaseTestCase.h"\r
+\r
+#include <xmltooling/exceptions.h>\r
+\r
+\r
+class ExceptionTest : public CxxTest::TestSuite {\r
+public:\r
+\r
+    void testException(void) {\r
+        TS_TRACE("testException");\r
+\r
+#define TEST(n,b,a) XMLToolingException e##n(b); \\r
+                TS_ASSERT(!strcmp(a,e##n.what()))\r
+\r
+#define TESTP(n,b,a,p) MarshallingException e##n(b,p); \\r
+                TS_ASSERT(!strcmp(a,e##n.what()))\r
+\r
+\r
+        TESTP(1,"This is a test.",          "This is a test.",      params(2,"Foo","bar"));\r
+        TESTP(2,"This is a test.$",         "This is a test.",      params(2,"Foo","bar"));\r
+        TESTP(3,"This is a $ test.",        "This is a  test.",     params(2,"Foo","bar"));\r
+        TESTP(4,"$$This is a test.$",       "$This is a test.",     params(2,"Foo","bar"));\r
+        TESTP(5,"$This is a $test.",        "This is a test.",      params(2,"Foo","bar"));\r
+        TESTP(6,"$1 is a $2",               "Foo is a bar",         params(2,"Foo","bar"));\r
+        TESTP(7,"$This is a $test.",        "Foo is a bar.",        namedparams(2,"This","Foo","test","bar"));\r
+        TESTP(8,"Unable to generate random data: $1",\r
+                "Unable to generate random data: OpenSSLCryptoProvider::getRandom - OpenSSL random not properly initialised",\r
+                params(1,"OpenSSLCryptoProvider::getRandom - OpenSSL random not properly initialised"));\r
+\r
+        string buf=e7.toString();\r
+        TS_TRACE(buf.c_str());\r
+        auto_ptr<XMLToolingException> ptr(XMLToolingException::fromString(buf.c_str()));\r
+        TS_ASSERT(typeid(*ptr)==typeid(MarshallingException));\r
+        TS_ASSERT(!strcmp(ptr->what(),"Foo is a bar."));\r
+    }\r
+};\r
index 448e160..65044a3 100644 (file)
@@ -16,6 +16,7 @@ endif
 
 xmltoolingtest_h = \
     ComplexXMLObjectTest.h \
+    ExceptionText.h \
     MarshallingTest.h \
     UnmarshallingTest.h \
     xmltoolingtest.h \
index cfd8e46..d626291 100644 (file)
@@ -129,7 +129,7 @@ public:
         \r
         string buf;\r
         XMLHelper::serialize(rootElement, buf);\r
-        TS_TRACE(buf.c_str());\r
+        //TS_TRACE(buf.c_str());\r
 \r
         istringstream in(buf);\r
         DOMDocument* doc=nonvalidatingPool->parse(in);\r
index 294fe85..7a95632 100644 (file)
                                >\r
                        </File>\r
                        <File\r
+                               RelativePath=".\ExceptionTest.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
                                RelativePath=".\MarshallingTest.cpp"\r
                                >\r
                        </File>\r
                                        >\r
                                        <Tool\r
                                                Name="VCCustomBuildTool"\r
-                                               CommandLine="\perl\bin\perl.exe -w \cxxtest\cxxtestgen.pl --part --have-eh --have-std --abort-on-fail -o &quot;$(InputName)&quot;.cpp &quot;$(InputPath)&quot;&#x0D;&#x0A;"\r
+                                               CommandLine="\perl\bin\perl.exe -w \cxxtest\cxxtestgen.pl --part --have-eh --have-std --abort-on-fail -o &quot;$(InputName)&quot;.cpp &quot;$(InputPath)&quot;"\r
                                                Outputs="&quot;$(InputName)&quot;.cpp"\r
                                        />\r
                                </FileConfiguration>\r
                                        >\r
                                        <Tool\r
                                                Name="VCCustomBuildTool"\r
-                                               CommandLine="\perl\bin\perl.exe -w \cxxtest\cxxtestgen.pl --part --have-eh --have-std --abort-on-fail -o &quot;$(InputName)&quot;.cpp &quot;$(InputPath)&quot;&#x0D;&#x0A;"\r
+                                               CommandLine="\perl\bin\perl.exe -w \cxxtest\cxxtestgen.pl --part --have-eh --have-std --abort-on-fail -o &quot;$(InputName)&quot;.cpp &quot;$(InputPath)&quot;"\r
+                                               Outputs="&quot;$(InputName)&quot;.cpp"\r
+                                       />\r
+                               </FileConfiguration>\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\ExceptionTest.h"\r
+                               >\r
+                               <FileConfiguration\r
+                                       Name="Debug|Win32"\r
+                                       >\r
+                                       <Tool\r
+                                               Name="VCCustomBuildTool"\r
+                                               CommandLine="\perl\bin\perl.exe -w \cxxtest\cxxtestgen.pl --part --have-eh --have-std --abort-on-fail -o &quot;$(InputName)&quot;.cpp &quot;$(InputPath)&quot;"\r
+                                               Outputs="&quot;$(InputName)&quot;.cpp"\r
+                                       />\r
+                               </FileConfiguration>\r
+                               <FileConfiguration\r
+                                       Name="Release|Win32"\r
+                                       >\r
+                                       <Tool\r
+                                               Name="VCCustomBuildTool"\r
+                                               CommandLine="\perl\bin\perl.exe -w \cxxtest\cxxtestgen.pl --part --have-eh --have-std --abort-on-fail -o &quot;$(InputName)&quot;.cpp &quot;$(InputPath)&quot;"\r
                                                Outputs="&quot;$(InputName)&quot;.cpp"\r
                                        />\r
                                </FileConfiguration>\r
                                        >\r
                                        <Tool\r
                                                Name="VCCustomBuildTool"\r
-                                               CommandLine="\perl\bin\perl.exe -w \cxxtest\cxxtestgen.pl --part --have-eh --have-std --abort-on-fail -o &quot;$(InputName)&quot;.cpp &quot;$(InputPath)&quot;"\r
+                                               CommandLine="\perl\bin\perl.exe -w \cxxtest\cxxtestgen.pl --part --have-eh --have-std --abort-on-fail -o &quot;$(InputName)&quot;.cpp &quot;$(InputPath)&quot;&#x0D;&#x0A;"\r
                                                Outputs="&quot;$(InputName)&quot;.cpp"\r
                                        />\r
                                </FileConfiguration>\r
                                        >\r
                                        <Tool\r
                                                Name="VCCustomBuildTool"\r
-                                               CommandLine="\perl\bin\perl.exe -w \cxxtest\cxxtestgen.pl --part --have-eh --have-std --abort-on-fail -o &quot;$(InputName)&quot;.cpp &quot;$(InputPath)&quot;"\r
+                                               CommandLine="\perl\bin\perl.exe -w \cxxtest\cxxtestgen.pl --part --have-eh --have-std --abort-on-fail -o &quot;$(InputName)&quot;.cpp &quot;$(InputPath)&quot;&#x0D;&#x0A;"\r
                                                Outputs="&quot;$(InputName)&quot;.cpp"\r
                                        />\r
                                </FileConfiguration>\r