From 4d48e7c4f1f95891d4ef5004dda17311648e9724 Mon Sep 17 00:00:00 2001 From: Scott Cantor Date: Sun, 26 Mar 2006 21:30:32 +0000 Subject: [PATCH] Add parametrized messaging and serialization to exceptions. --- schemas/Makefile.am | 12 +- schemas/catalog.xml.in | 1 + schemas/xmltooling.xsd | 24 +++ xmltooling/Makefile.am | 1 + xmltooling/XMLToolingConfig.cpp | 18 ++ xmltooling/exceptions.cpp | 274 +++++++++++++++++++++++++++++++ xmltooling/exceptions.h | 308 ++++++++++++++++++++++++++++++++--- xmltooling/util/ParserPool.cpp | 2 + xmltooling/util/XMLConstants.cpp | 8 + xmltooling/util/XMLConstants.h | 3 + xmltooling/util/XMLHelper.cpp | 72 ++++++++ xmltooling/util/XMLHelper.h | 72 ++++++++ xmltooling/xmltooling.vcproj | 4 + xmltoolingtest/ExceptionTest.h | 52 ++++++ xmltoolingtest/Makefile.am | 1 + xmltoolingtest/SignatureTest.h | 2 +- xmltoolingtest/xmltoolingtest.vcproj | 34 +++- 17 files changed, 858 insertions(+), 30 deletions(-) create mode 100644 schemas/xmltooling.xsd create mode 100644 xmltooling/exceptions.cpp create mode 100644 xmltoolingtest/ExceptionTest.h diff --git a/schemas/Makefile.am b/schemas/Makefile.am index 6ffa021..d399785 100644 --- a/schemas/Makefile.am +++ b/schemas/Makefile.am @@ -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 diff --git a/schemas/catalog.xml.in b/schemas/catalog.xml.in index fa19a59..2521ae0 100644 --- a/schemas/catalog.xml.in +++ b/schemas/catalog.xml.in @@ -4,4 +4,5 @@ + diff --git a/schemas/xmltooling.xsd b/schemas/xmltooling.xsd new file mode 100644 index 0000000..a532d4a --- /dev/null +++ b/schemas/xmltooling.xsd @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/xmltooling/Makefile.am b/xmltooling/Makefile.am index ab0661b..4e89a59 100644 --- a/xmltooling/Makefile.am +++ b/xmltooling/Makefile.am @@ -72,6 +72,7 @@ libxmltooling_la_SOURCES = \ AbstractDOMCachingXMLObject.cpp \ AbstractElementProxy.cpp \ AbstractXMLObject.cpp \ + exceptions.cpp \ Namespace.cpp \ QName.cpp \ unicode.cpp \ diff --git a/xmltooling/XMLToolingConfig.cpp b/xmltooling/XMLToolingConfig.cpp index 23c4146..a8680be 100644 --- a/xmltooling/XMLToolingConfig.cpp +++ b/xmltooling/XMLToolingConfig.cpp @@ -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 index 0000000..10e4e39 --- /dev/null +++ b/xmltooling/exceptions.cpp @@ -0,0 +1,274 @@ +/* + * 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. + */ + +/** + * exceptions.cpp + * + * Exception classes + */ + +#include "internal.h" +#include "exceptions.h" +#include "XMLToolingConfig.h" +#include "util/XMLConstants.h" +#include "util/XMLHelper.h" + +#include +#include + +using namespace xmltooling; +using namespace std; + +params::params(int count,...) +{ + va_list args; + va_start(args,count); + while (count--) + v.push_back(va_arg(args,char*)); + va_end(args); +} + +namedparams::namedparams(int count,...) +{ + count*=2; + va_list args; + va_start(args,count); + while (count--) + v.push_back(va_arg(args,char*)); + va_end(args); +} + +XMLToolingException::ExceptionFactoryMap XMLToolingException::m_factoryMap; + +XMLToolingException* XMLToolingException::getInstance(const char* exceptionClass) +{ + if (exceptionClass) { + ExceptionFactoryMap::const_iterator i=m_factoryMap.find(exceptionClass); + if (i!=m_factoryMap.end()) + return (i->second)(); + } + return new XMLToolingException(); +} + +XMLToolingException::XMLToolingException(const char* msg, const params& p) +{ + if (msg) + m_msg=msg; + addProperties(p); +} + +XMLToolingException::XMLToolingException(const char* msg, const namedparams& p) +{ + if (msg) + m_msg=msg; + addProperties(p); +} + +XMLToolingException::XMLToolingException(const std::string& msg, const params& p) : m_msg(msg) +{ + addProperties(p); +} + +XMLToolingException::XMLToolingException(const std::string& msg, const namedparams& p) : m_msg(msg) +{ + addProperties(p); +} + +void XMLToolingException::setMessage(const char* msg) +{ + if (msg) + m_msg=msg; + else + m_msg.erase(); + m_processedmsg.erase(); +} + +inline const char* get_digit_character() +{ + static const char s_characters[19] = + { + '9' + , '8' + , '7' + , '6' + , '5' + , '4' + , '3' + , '2' + , '1' + , '0' + , '1' + , '2' + , '3' + , '4' + , '5' + , '6' + , '7' + , '8' + , '9' + }; + static const char *s_mid = s_characters + 9; + + return s_mid; +} + +inline const char* unsigned_integer_to_string(char* buf, size_t cchBuf, int i) +{ + char* psz=buf + cchBuf - 1; // Set psz to last char + *psz = 0; // Set terminating null + + do { + unsigned int lsd = i % 10; // Get least significant + // digit + + i /= 10; // Prepare for next most + // significant digit + + --psz; // Move back + + *psz = get_digit_character()[lsd]; // Place the digit + + } while(i!=0 && psz>buf); + + return psz; +} + +void XMLToolingException::addProperties(const params& p) +{ + m_processedmsg.erase(); + int i=m_params.size()+1; + char buf[20]; + const vector& v=p.get(); + for (vector::const_iterator ci=v.begin(); ci!=v.end(); ci++) { + m_params[unsigned_integer_to_string(buf,sizeof(buf),i++)] = *ci; + } +} + +void XMLToolingException::addProperties(const namedparams& p) +{ + m_processedmsg.erase(); + const vector& v=p.get(); + for (vector::const_iterator ci=v.begin(); ci!=v.end(); ci++) { + m_params.erase(*ci); + m_params[*ci] = *(ci+1); + ci++; // advance past name to value, then loop will advance it again + } +} + +const char* XMLToolingException::getProperty(unsigned int index) const +{ + char buf[20]; + map::const_iterator i=m_params.find(unsigned_integer_to_string(buf,sizeof(buf),index)); + return (i==m_params.end()) ? NULL : i->second.c_str(); +} + +const char* XMLToolingException::getProperty(const char* name) const +{ + map::const_iterator i=m_params.find(name); + return (i==m_params.end()) ? NULL : i->second.c_str(); +} + +const char* XMLToolingException::getMessage() const +{ + if (!m_processedmsg.empty()) + return m_processedmsg.c_str(); + else if (m_params.empty()) + return m_msg.c_str(); + + static const char* legal="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890_"; + + // Replace any parameters in the message. + string::size_type i=0,start=0; + while (start!=string::npos && startstart) + m_processedmsg += m_msg.substr(start,i-start); // append everything in between + start=i+1; // move start to the beginning of the token name + i=m_msg.find_first_not_of(legal,start); // find token delimiter + if (i==start) { // append a non legal character + m_processedmsg+=m_msg[start++]; + continue; + } + + // search for token in map + map::const_iterator param=m_params.find(m_msg.substr(start,(i==string::npos) ? i : i-start)); + if (param!=m_params.end()) { + m_processedmsg+=param->second; + start=i; + } + } + if (start!=string::npos && start"; + const char* msg=getMessage(); + if (msg) + xml=xml + "" + msg + ""; + for (map::const_iterator i=m_params.begin(); i!=m_params.end(); i++) { + xml=xml + "first + "\">" + i->second + ""; + } + xml+=""; + return xml; +} + +XMLToolingException* XMLToolingException::fromStream(std::istream& in) +{ + static const XMLCh exception[] = { chLatin_e, chLatin_x, chLatin_c, chLatin_e, chLatin_p, chLatin_t, chLatin_i, chLatin_o, chLatin_n, chNull }; + static const XMLCh message[] = { chLatin_m, chLatin_e, chLatin_s, chLatin_s, chLatin_a, chLatin_g, chLatin_e, chNull }; + static const XMLCh name[] = { chLatin_n, chLatin_a, chLatin_m, chLatin_e, chNull }; + static const XMLCh param[] = { chLatin_p, chLatin_a, chLatin_r, chLatin_a, chLatin_m, chNull }; + static const XMLCh type[] = { chLatin_t, chLatin_y, chLatin_p, chLatin_e, chNull }; + + DOMDocument* doc=XMLToolingInternalConfig::getInternalConfig().m_parserPool->parse(in); + + // Check root element. + const DOMElement* root=doc->getDocumentElement(); + if (!XMLHelper::isNodeNamed(root,XMLConstants::XMLTOOLING_NS,exception)) { + doc->release(); + throw XMLToolingException("Invalid root element on serialized exception."); + } + + auto_ptr_char classname(root->getAttributeNS(NULL,type)); + auto_ptr excep(XMLToolingException::getInstance(classname.get())); + + DOMElement* child=XMLHelper::getFirstChildElement(root,XMLConstants::XMLTOOLING_NS,message); + if (child && child->hasChildNodes()) { + auto_ptr_char m(child->getFirstChild()->getNodeValue()); + excep->setMessage(m.get()); + } + + child=XMLHelper::getFirstChildElement(root,XMLConstants::XMLTOOLING_NS,param); + while (child && child->hasChildNodes()) { + auto_ptr_char n(child->getAttributeNS(NULL,name)); + char* v=toUTF8(child->getFirstChild()->getNodeValue()); + if (n.get() && v) + excep->addProperty(n.get(), v); + XMLString::release(&v); + child=XMLHelper::getNextSiblingElement(root,XMLConstants::XMLTOOLING_NS,param); + } + + doc->release(); + return excep.release(); +} + +XMLToolingException* XMLToolingException::fromString(const char* s) +{ + istringstream in(s); + return fromStream(in); +} diff --git a/xmltooling/exceptions.h b/xmltooling/exceptions.h index f527350..39ee359 100644 --- a/xmltooling/exceptions.h +++ b/xmltooling/exceptions.h @@ -23,44 +23,312 @@ #if !defined(__xmltooling_exceptions_h__) #define __xmltooling_exceptions_h__ +#include #include +#include +#include #include -#define DECL_XMLTOOLING_EXCEPTION(type) \ - class XMLTOOL_EXCEPTIONAPI(XMLTOOL_API) type : public XMLToolingException { \ +/** + * Declares a derived exception class + * + * @param name the exception class + * @param base the base class + */ +#define DECL_XMLTOOLING_EXCEPTION(name,base) \ + class XMLTOOL_EXCEPTIONAPI(XMLTOOL_API) name : public xmltooling::base { \ public: \ - type(const char* const msg) : XMLToolingException(msg) {} \ - type(const std::string& msg) : XMLToolingException(msg) {} \ - virtual ~type() {} \ + name(const char* msg=NULL, const xmltooling::params& p=xmltooling::params()) \ + : xmltooling::base(msg,p) {} \ + name(const char* msg, const xmltooling::namedparams& p) \ + : xmltooling::base(msg,p) {} \ + name(const std::string& msg, const xmltooling::params& p=xmltooling::params()) \ + : xmltooling::base(msg,p) {} \ + name(const std::string& msg, const xmltooling::namedparams& p) \ + : xmltooling::base(msg,p) {} \ + virtual ~name() {} \ + virtual const char* getClassName() const { return "xmltooling::"#name; } \ + void raise() const {throw *this;} \ + } + +/** + * Declares a factory function for an exception class. + * + * @param name the exception class name + */ +#define DECL_EXCEPTION_FACTORY(name) \ + xmltooling::XMLToolingException* name##Factory() \ + { \ + return new xmltooling::name(); \ } +/** + * Registers a factory for an exception class. + * + * @param name the exception class name + */ +#define REGISTER_EXCEPTION_FACTORY(name) XMLToolingException::registerFactory("xmltooling::"#name,name##Factory) + namespace xmltooling { /** - * Base exception class. - * std::exception seems to be inconsistently defined, so this is just - * a substitute base class. + * Wrapper around a variable number of arguments. */ + class XMLTOOL_API params + { + public: + /** + * Initializes with zero parameters. + */ + params() {} + + /** + * Initializes the parameter set. + * + * @param count the number of parameters that follow + */ + params(int count,...); + + /** + * Returns an immutable reference to the set of parameters. + * + * @return the parameter set + */ + const std::vector& get() const {return v;} + + protected: + std::vector v; + }; + + /** + * Wrapper around a variable number of name/value pairs. + */ + class XMLTOOL_API namedparams : public params + { + public: + /** + * Initializes with zero parameters. + */ + namedparams() {} + + /** + * Initializes the named parameter set. + * + * @param count the number of name/value pairs that follow (must be even) + */ + namedparams(int count,...); + }; + + /** + * Base exception class, supports parametrized messages and XML serialization. + * Parameters are prefixed with a dollar sign ($) and can be positional ($1) + * or named ($info). + */ + class XMLTOOL_EXCEPTIONAPI(XMLTOOL_API) XMLToolingException; + typedef XMLToolingException* ExceptionFactory(); + class XMLTOOL_EXCEPTIONAPI(XMLTOOL_API) XMLToolingException { public: - XMLToolingException() {} virtual ~XMLToolingException() {} - XMLToolingException(const char* const msg) : m_msg(msg) {} - XMLToolingException(const std::string& msg) : m_msg(msg) {} - virtual const char* what() const { return m_msg.c_str(); } + + /** + * Constructs an exception using a message and positional parameters. + * + * @param msg error message + * @param p an ordered set of positional parameter strings + */ + XMLToolingException(const char* msg=NULL, const params& p=params()); + + /** + * Constructs an exception using a message and named parameters. + * + * @param msg error message + * @param p a set of named parameter strings + */ + XMLToolingException(const char* msg, const namedparams& p); + + /** + * Constructs an exception using a message and positional parameters. + * + * @param msg error message + * @param p an ordered set of positional parameter strings + */ + XMLToolingException(const std::string& msg, const params& p=params()); + + /** + * Constructs an exception using a message and named parameters. + * + * @param msg error message + * @param p a set of named parameter strings + */ + XMLToolingException(const std::string& msg, const namedparams& p); + + /** + * Returns the error message, after processing any parameter references. + * + * @return the processed message + */ + const char* getMessage() const; + + /** + * Returns the error message, after processing any parameter references. + * + * @return the processed message + */ + const char* what() const {return getMessage();} + + /** + * Sets the error message. + * + * @param msg the error message + */ + void setMessage(const char* msg); + + /** + * Sets the error message. + * + * @param msg the error message + */ + void setMessage(const std::string& msg) { + setMessage(msg.c_str()); + } + + /** + * Attach a set of positional parameters to the exception. + * + * @param p an ordered set of named parameter strings + */ + void addProperties(const params& p); + + /** + * Attach a set of named parameters to the exception. + * + * @param p a set of named parameter strings + */ + void addProperties(const namedparams& p); + + /** + * Attach a single positional parameter at the next available position. + * + * @param value the parameter value + */ + void addProperty(const char* value) { + addProperties(params(1,value)); + } + + /** + * Attach a single named parameter. + * + * @param name the parameter name + * @param value the parameter value + */ + void addProperty(const char* name, const char* value) { + addProperties(namedparams(1,name,value)); + } + + /** + * Returns the parameter property with the designated position (based from one). + * + * @param index position to access + * @return the parameter property or NULL + */ + const char* getProperty(unsigned int index) const; + + /** + * Returns the parameter property with the designated name. + * + * @param name named parameter to access + * @return the parameter property or NULL + */ + const char* getProperty(const char* name) const; + + /** + * Raises an exception using itself. + * Used to raise an exception of a derived type. + */ + virtual void raise() const { + throw *this; + } + + /** + * Returns a unique name for the exception class. + * + * @return class name + */ + virtual const char* getClassName() const { + return "xmltooling::XMLToolingException"; + } + + /** + * Returns a string containing a serialized representation of the exception. + * + * @return the serialization + */ + std::string toString() const; + private: std::string m_msg; + mutable std::string m_processedmsg; + std::map m_params; + + public: + /** + * Builds an empty exception of the given type. + * + * @param exceptionClass the name of the exception type to build + * @return an empty exception object + */ + static XMLToolingException* getInstance(const char* exceptionClass); + + /** + * Builds an exception from a serialized input stream. + * + * @param in input stream + * @return the exception object found in the stream + */ + static XMLToolingException* fromStream(std::istream& in); + + /** + * Builds an exception from a serialized input buffer. + * + * @param s input buffer + * @return the exception object found in the buffer + */ + static XMLToolingException* fromString(const char* s); + + /** + * Registers a factory to create exceptions of a given class name. + * + * @param exceptionClass name of exception type + * @param factory factory function to build exceptions with + */ + static void registerFactory(const char* exceptionClass, ExceptionFactory* factory) { + m_factoryMap[exceptionClass] = factory; + } + + /** + * Unregisters the factory for a given class name. + * + * @param exceptionClass name of exception type + */ + static void deregisterFactory(const char* exceptionClass) { + m_factoryMap.erase(exceptionClass); + } + + private: + typedef std::map ExceptionFactoryMap; + static ExceptionFactoryMap m_factoryMap; }; - DECL_XMLTOOLING_EXCEPTION(XMLParserException); - DECL_XMLTOOLING_EXCEPTION(XMLObjectException); - DECL_XMLTOOLING_EXCEPTION(MarshallingException); - DECL_XMLTOOLING_EXCEPTION(UnmarshallingException); - DECL_XMLTOOLING_EXCEPTION(UnknownElementException); - DECL_XMLTOOLING_EXCEPTION(UnknownAttributeException); - DECL_XMLTOOLING_EXCEPTION(ValidationException); - DECL_XMLTOOLING_EXCEPTION(SignatureException); + DECL_XMLTOOLING_EXCEPTION(XMLParserException,XMLToolingException); + DECL_XMLTOOLING_EXCEPTION(XMLObjectException,XMLToolingException); + DECL_XMLTOOLING_EXCEPTION(MarshallingException,XMLToolingException); + DECL_XMLTOOLING_EXCEPTION(UnmarshallingException,XMLToolingException); + DECL_XMLTOOLING_EXCEPTION(UnknownElementException,XMLToolingException); + DECL_XMLTOOLING_EXCEPTION(UnknownAttributeException,XMLToolingException); + DECL_XMLTOOLING_EXCEPTION(ValidationException,XMLToolingException); + DECL_XMLTOOLING_EXCEPTION(SignatureException,XMLToolingException); }; diff --git a/xmltooling/util/ParserPool.cpp b/xmltooling/util/ParserPool.cpp index 3eb039d..5bec638 100644 --- a/xmltooling/util/ParserPool.cpp +++ b/xmltooling/util/ParserPool.cpp @@ -169,6 +169,7 @@ bool ParserPool::loadCatalog(const XMLCh* pathname) if (!XMLHelper::isNodeNamed(root,CATALOG_NS,catalog)) { auto_ptr_char temp(pathname); log.error("unknown root element, failed to load XML catalog from %s", temp.get()); + doc->release(); return false; } @@ -194,6 +195,7 @@ bool ParserPool::loadCatalog(const XMLCh* pathname) for_each(m_schemaLocMap.begin(),m_schemaLocMap.end(),doubleit(m_schemaLocations,' ')); #endif XMLPlatformUtils::unlockMutex(m_lock); + doc->release(); } catch (XMLParserException& e) { log.error("catalog loader caught XMLParserException: %s", e.what()); diff --git a/xmltooling/util/XMLConstants.cpp b/xmltooling/util/XMLConstants.cpp index 72ab14f..a5974b9 100644 --- a/xmltooling/util/XMLConstants.cpp +++ b/xmltooling/util/XMLConstants.cpp @@ -82,3 +82,11 @@ const XMLCh XMLConstants::XMLENC_NS[] = // http://www.w3.org/2001/04/xmlenc# }; const XMLCh XMLConstants::XMLENC_PREFIX[] = { chLatin_x, chLatin_e, chLatin_n, chLatin_c, chNull }; + +const XMLCh XMLConstants::XMLTOOLING_NS[] = // http://www.opensaml.org/xmltooling +{ chLatin_h, chLatin_t, chLatin_t, chLatin_p, chColon, chForwardSlash, chForwardSlash, + chLatin_w, chLatin_w, chLatin_w, chPeriod, + chLatin_o, chLatin_p, chLatin_e, chLatin_n, chLatin_s, chLatin_a, chLatin_m, chLatin_l, chPeriod, + chLatin_o, chLatin_r, chLatin_g, chForwardSlash, + chLatin_x, chLatin_m, chLatin_l, chLatin_t, chLatin_o, chLatin_o, chLatin_l, chLatin_i, chLatin_n, chLatin_g, chNull +}; diff --git a/xmltooling/util/XMLConstants.h b/xmltooling/util/XMLConstants.h index a503fb7..1faf640 100644 --- a/xmltooling/util/XMLConstants.h +++ b/xmltooling/util/XMLConstants.h @@ -67,6 +67,9 @@ namespace xmltooling { /** XML Encryption QName prefix ("xenc") */ static const XMLCh XMLENC_PREFIX[]; + + /** XML Tooling namespace ("http://www.opensaml.org/xmltooling") */ + static const XMLCh XMLTOOLING_NS[]; }; }; diff --git a/xmltooling/util/XMLHelper.cpp b/xmltooling/util/XMLHelper.cpp index 0c3691e..74e09b3 100644 --- a/xmltooling/util/XMLHelper.cpp +++ b/xmltooling/util/XMLHelper.cpp @@ -128,6 +128,78 @@ const XMLCh* XMLHelper::getTextContent(const DOMElement* e) return NULL; } +DOMElement* XMLHelper::getFirstChildElement(const DOMNode* n) +{ + DOMNode* child = n->getFirstChild(); + while (child && child->getNodeType() != DOMNode::ELEMENT_NODE) + child = child->getNextSibling(); + if (child) + return static_cast(child); + return NULL; +} + +DOMElement* XMLHelper::getLastChildElement(const DOMNode* n) +{ + DOMNode* child = n->getLastChild(); + while (child && child->getNodeType() != DOMNode::ELEMENT_NODE) + child = child->getPreviousSibling(); + if (child) + return static_cast(child); + return NULL; +} + +DOMElement* XMLHelper::getFirstChildElement(const DOMNode* n, const XMLCh* ns, const XMLCh* localName) +{ + DOMElement* e = getFirstChildElement(n); + while (e && !isNodeNamed(e, ns, localName)) + e = getNextSiblingElement(e); + return e; +} + +DOMElement* XMLHelper::getLastChildElement(const DOMNode* n, const XMLCh* ns, const XMLCh* localName) +{ + DOMElement* e = getLastChildElement(n); + while (e && !isNodeNamed(e, ns, localName)) + e = getPreviousSiblingElement(e); + return e; +} + +DOMElement* XMLHelper::getNextSiblingElement(const DOMNode* n) +{ + DOMNode* sib = n->getNextSibling(); + while (sib && sib->getNodeType() != DOMNode::ELEMENT_NODE) + sib = sib->getNextSibling(); + if (sib) + return static_cast(sib); + return NULL; +} + +DOMElement* XMLHelper::getPreviousSiblingElement(const DOMNode* n) +{ + DOMNode* sib = n->getPreviousSibling(); + while (sib && sib->getNodeType() != DOMNode::ELEMENT_NODE) + sib = sib->getPreviousSibling(); + if (sib) + return static_cast(sib); + return NULL; +} + +DOMElement* XMLHelper::getNextSiblingElement(const DOMNode* n, const XMLCh* ns, const XMLCh* localName) +{ + DOMElement* e = getNextSiblingElement(n); + while (e && !isNodeNamed(e, ns, localName)) + e = getNextSiblingElement(e); + return e; +} + +DOMElement* XMLHelper::getPreviousSiblingElement(const DOMNode* n, const XMLCh* ns, const XMLCh* localName) +{ + DOMElement* e = getPreviousSiblingElement(n); + while (e && !isNodeNamed(e, ns, localName)) + e = getPreviousSiblingElement(e); + return e; +} + void XMLHelper::serialize(const DOMElement* e, std::string& buf) { static const XMLCh impltype[] = { chLatin_L, chLatin_S, chNull }; diff --git a/xmltooling/util/XMLHelper.h b/xmltooling/util/XMLHelper.h index 99697e6..ac6f240 100644 --- a/xmltooling/util/XMLHelper.h +++ b/xmltooling/util/XMLHelper.h @@ -99,6 +99,78 @@ namespace xmltooling { } /** + * Returns the first child element of the node if any. + * + * @param n node to check + * @return the first child node of type Element, or NULL + */ + static DOMElement* getFirstChildElement(const DOMNode* n); + + /** + * Returns the last child element of the node if any. + * + * @param n node to check + * @return the last child node of type Element, or NULL + */ + static DOMElement* getLastChildElement(const DOMNode* n); + + /** + * Returns the next sibling element of the node if any. + * + * @param n node to check + * @return the next sibling node of type Element, or NULL + */ + static DOMElement* getNextSiblingElement(const DOMNode* n); + + /** + * Returns the previous sibling element of the node if any. + * + * @param n node to check + * @return the previous sibling node of type Element, or NULL + */ + static DOMElement* getPreviousSiblingElement(const DOMNode* n); + + /** + * Returns the first matching child element of the node if any. + * + * @param n node to check + * @param ns namespace to compare with + * @param localName local name to compare with + * @return the first matching child node of type Element, or NULL + */ + static DOMElement* getFirstChildElement(const DOMNode* n, const XMLCh* ns, const XMLCh* localName); + + /** + * Returns the last matching child element of the node if any. + * + * @param n node to check + * @param ns namespace to compare with + * @param localName local name to compare with + * @return the last matching child node of type Element, or NULL + */ + static DOMElement* getLastChildElement(const DOMNode* n, const XMLCh* ns, const XMLCh* localName); + + /** + * Returns the next matching sibling element of the node if any. + * + * @param n node to check + * @param ns namespace to compare with + * @param localName local name to compare with + * @return the next matching sibling node of type Element, or NULL + */ + static DOMElement* getNextSiblingElement(const DOMNode* n, const XMLCh* ns, const XMLCh* localName); + + /** + * Returns the previous matching sibling element of the node if any. + * + * @param n node to check + * @param ns namespace to compare with + * @param localName local name to compare with + * @return the previous matching sibling node of type Element, or NULL + */ + static DOMElement* getPreviousSiblingElement(const DOMNode* n, const XMLCh* ns, const XMLCh* localName); + + /** * Returns the content of the first Text node found in the element, if any. * This is roughly similar to the DOM getTextContent function, but only * examples the immediate children of the element. diff --git a/xmltooling/xmltooling.vcproj b/xmltooling/xmltooling.vcproj index 5c94f40..d095368 100644 --- a/xmltooling/xmltooling.vcproj +++ b/xmltooling/xmltooling.vcproj @@ -198,6 +198,10 @@ > + + diff --git a/xmltoolingtest/ExceptionTest.h b/xmltoolingtest/ExceptionTest.h new file mode 100644 index 0000000..d681577 --- /dev/null +++ b/xmltoolingtest/ExceptionTest.h @@ -0,0 +1,52 @@ +/* + * Copyright 2001-2005 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. + */ + +#include "XMLObjectBaseTestCase.h" + +#include + + +class ExceptionTest : public CxxTest::TestSuite { +public: + + void testException(void) { + TS_TRACE("testException"); + +#define TEST(n,b,a) XMLToolingException e##n(b); \ + TS_ASSERT(!strcmp(a,e##n.what())) + +#define TESTP(n,b,a,p) MarshallingException e##n(b,p); \ + TS_ASSERT(!strcmp(a,e##n.what())) + + + TESTP(1,"This is a test.", "This is a test.", params(2,"Foo","bar")); + TESTP(2,"This is a test.$", "This is a test.", params(2,"Foo","bar")); + TESTP(3,"This is a $ test.", "This is a test.", params(2,"Foo","bar")); + TESTP(4,"$$This is a test.$", "$This is a test.", params(2,"Foo","bar")); + TESTP(5,"$This is a $test.", "This is a test.", params(2,"Foo","bar")); + TESTP(6,"$1 is a $2", "Foo is a bar", params(2,"Foo","bar")); + TESTP(7,"$This is a $test.", "Foo is a bar.", namedparams(2,"This","Foo","test","bar")); + TESTP(8,"Unable to generate random data: $1", + "Unable to generate random data: OpenSSLCryptoProvider::getRandom - OpenSSL random not properly initialised", + params(1,"OpenSSLCryptoProvider::getRandom - OpenSSL random not properly initialised")); + + string buf=e7.toString(); + TS_TRACE(buf.c_str()); + auto_ptr ptr(XMLToolingException::fromString(buf.c_str())); + TS_ASSERT(typeid(*ptr)==typeid(MarshallingException)); + TS_ASSERT(!strcmp(ptr->what(),"Foo is a bar.")); + } +}; diff --git a/xmltoolingtest/Makefile.am b/xmltoolingtest/Makefile.am index 448e160..65044a3 100644 --- a/xmltoolingtest/Makefile.am +++ b/xmltoolingtest/Makefile.am @@ -16,6 +16,7 @@ endif xmltoolingtest_h = \ ComplexXMLObjectTest.h \ + ExceptionText.h \ MarshallingTest.h \ UnmarshallingTest.h \ xmltoolingtest.h \ diff --git a/xmltoolingtest/SignatureTest.h b/xmltoolingtest/SignatureTest.h index cfd8e46..d626291 100644 --- a/xmltoolingtest/SignatureTest.h +++ b/xmltoolingtest/SignatureTest.h @@ -129,7 +129,7 @@ public: string buf; XMLHelper::serialize(rootElement, buf); - TS_TRACE(buf.c_str()); + //TS_TRACE(buf.c_str()); istringstream in(buf); DOMDocument* doc=nonvalidatingPool->parse(in); diff --git a/xmltoolingtest/xmltoolingtest.vcproj b/xmltoolingtest/xmltoolingtest.vcproj index 294fe85..7a95632 100644 --- a/xmltoolingtest/xmltoolingtest.vcproj +++ b/xmltoolingtest/xmltoolingtest.vcproj @@ -184,6 +184,10 @@ > + + @@ -213,7 +217,7 @@ > @@ -222,7 +226,29 @@ > + + + + + + + + @@ -257,7 +283,7 @@ > @@ -266,7 +292,7 @@ > -- 2.1.4