From e7eab2260f79e0fe854841f273dafa006f4b4cf9 Mon Sep 17 00:00:00 2001 From: cantor Date: Mon, 8 May 2006 03:54:41 +0000 Subject: [PATCH] Macro adjustments, date/time class, typed XML attributes. git-svn-id: https://svn.middleware.georgetown.edu/cpp-xmltooling/trunk@91 de75baf8-a10c-0410-a50a-987c0e22f00f --- xmltooling/AbstractXMLObject.cpp | 55 + xmltooling/AbstractXMLObject.h | 54 +- xmltooling/Makefile.am | 2 + xmltooling/PluginManager.h | 12 +- xmltooling/base.h | 318 +++- xmltooling/io/AbstractXMLObjectUnmarshaller.cpp | 2 +- xmltooling/signature/KeyInfo.h | 76 +- xmltooling/signature/Signature.h | 10 +- xmltooling/signature/impl/KeyInfoImpl.cpp | 226 +-- .../signature/impl/KeyInfoSchemaValidators.cpp | 8 +- xmltooling/signature/impl/XMLSecSignatureImpl.cpp | 6 + xmltooling/util/DateTime.cpp | 1621 ++++++++++++++++++++ xmltooling/util/DateTime.h | 233 +++ xmltooling/xmltooling.vcproj | 8 + 14 files changed, 2441 insertions(+), 190 deletions(-) create mode 100644 xmltooling/util/DateTime.cpp create mode 100644 xmltooling/util/DateTime.h diff --git a/xmltooling/AbstractXMLObject.cpp b/xmltooling/AbstractXMLObject.cpp index 23e563b..3111c8d 100644 --- a/xmltooling/AbstractXMLObject.cpp +++ b/xmltooling/AbstractXMLObject.cpp @@ -60,6 +60,61 @@ XMLCh* AbstractXMLObject::prepareForAssignment(XMLCh* oldValue, const XMLCh* new return oldValue; } +QName* AbstractXMLObject::prepareForAssignment(QName* oldValue, const QName* newValue) +{ + if (!oldValue) { + if (newValue) { + releaseThisandParentDOM(); + Namespace newNamespace(newValue->getNamespaceURI(), newValue->getPrefix()); + addNamespace(newNamespace); + return new QName(*newValue); + } + return NULL; + } + + delete oldValue; + releaseThisandParentDOM(); + if (newValue) { + Namespace newNamespace(newValue->getNamespaceURI(), newValue->getPrefix()); + addNamespace(newNamespace); + return new QName(*newValue); + } + return NULL; +} + +DateTime* AbstractXMLObject::prepareForAssignment(DateTime* oldValue, const DateTime* newValue) +{ + if (!oldValue) { + if (newValue) { + releaseThisandParentDOM(); + return new DateTime(*newValue); + } + return NULL; + } + + delete oldValue; + releaseThisandParentDOM(); + return newValue ? new DateTime(*newValue) : NULL; +} + +DateTime* AbstractXMLObject::prepareForAssignment(DateTime* oldValue, time_t newValue) +{ + delete oldValue; + releaseThisandParentDOM(); + DateTime* ret = new DateTime(newValue); + ret->parseDateTime(); + return ret; +} + +DateTime* AbstractXMLObject::prepareForAssignment(DateTime* oldValue, const XMLCh* newValue) +{ + delete oldValue; + releaseThisandParentDOM(); + DateTime* ret = new DateTime(newValue); + ret->parseDateTime(); + return ret; +} + XMLObject* AbstractXMLObject::prepareForAssignment(XMLObject* oldValue, XMLObject* newValue) { if (newValue && newValue->hasParent()) diff --git a/xmltooling/AbstractXMLObject.h b/xmltooling/AbstractXMLObject.h index 5ead42f..ba34176 100644 --- a/xmltooling/AbstractXMLObject.h +++ b/xmltooling/AbstractXMLObject.h @@ -24,6 +24,7 @@ #define __xmltooling_abstractxmlobj_h__ #include +#include #if defined (_MSC_VER) #pragma warning( push ) @@ -97,9 +98,10 @@ namespace xmltooling { AbstractXMLObject(const AbstractXMLObject& src); /** - * A helper function for derived classes. + * A helper function for derived classes, for assignment of strings. + * * This 'normalizes' newString, and then if it is different from oldString, - * it invalidates the DOM, frees the old string, and return the new. + * it invalidates the DOM, frees the old string, and returns the new. * If not different, it frees the new string and just returns the old value. * * @param oldValue - the current value @@ -110,6 +112,54 @@ namespace xmltooling { XMLCh* prepareForAssignment(XMLCh* oldValue, const XMLCh* newValue); /** + * A helper function for derived classes, for assignment of date/time data. + * + * It invalidates the DOM, frees the old object, and returns the new. + * + * @param oldValue - the current value + * @param newValue - the new value + * + * @return the value that should be assigned + */ + DateTime* prepareForAssignment(DateTime* oldValue, const DateTime* newValue); + + /** + * A helper function for derived classes, for assignment of date/time data. + * + * It invalidates the DOM, frees the old object, and returns the new. + * + * @param oldValue - the current value + * @param newValue - the epoch to assign as the new value + * + * @return the value that should be assigned + */ + DateTime* prepareForAssignment(DateTime* oldValue, time_t newValue); + + /** + * A helper function for derived classes, for assignment of date/time data. + * + * It invalidates the DOM, frees the old object, and returns the new. + * + * @param oldValue - the current value + * @param newValue - the new value in string form + * + * @return the value that should be assigned + */ + DateTime* prepareForAssignment(DateTime* oldValue, const XMLCh* newValue); + + /** + * A helper function for derived classes, for assignment of QName data. + * + * It invalidates the DOM, frees the old object, and returns the new. + * + * @param oldValue - the current value + * @param newValue - the new value + * + * @return the value that should be assigned + */ + QName* prepareForAssignment(QName* oldValue, const QName* newValue); + + /** * A helper function for derived classes, for assignment of (singleton) XML objects. * * It is indifferent to whether either the old or the new version of the value is null. diff --git a/xmltooling/Makefile.am b/xmltooling/Makefile.am index 17b0d43..044e60b 100644 --- a/xmltooling/Makefile.am +++ b/xmltooling/Makefile.am @@ -53,6 +53,7 @@ siginclude_HEADERS = \ signature/VerifyingContext.h utilinclude_HEADERS = \ + util/DateTime.h \ util/NDC.h \ util/ParserPool.h \ util/XMLConstants.h \ @@ -92,6 +93,7 @@ libxmltooling_la_SOURCES = \ io/AbstractXMLObjectUnmarshaller.cpp \ signature/impl/KeyInfoImpl.cpp \ signature/impl/KeyInfoSchemaValidators.cpp \ + util/DateTime.cpp \ util/NDC.cpp \ util/ParserPool.cpp \ util/XMLConstants.cpp \ diff --git a/xmltooling/PluginManager.h b/xmltooling/PluginManager.h index d876cce..3c76be4 100644 --- a/xmltooling/PluginManager.h +++ b/xmltooling/PluginManager.h @@ -27,9 +27,11 @@ #include #include -#include -using namespace xercesc; +#if defined (_MSC_VER) + #pragma warning( push ) + #pragma warning( disable : 4250 4251 ) +#endif namespace xmltooling { @@ -73,7 +75,7 @@ namespace xmltooling { /** * Builds a new instance of a plugin of a given type, configuring it - * with the supplied element, if any. + * with the supplied parameters. * * @param type the name of the plugin type * @param p parameters to configure plugin @@ -92,4 +94,8 @@ namespace xmltooling { }; +#if defined (_MSC_VER) + #pragma warning( pop ) +#endif + #endif /* __xmltooling_plugin_h__ */ diff --git a/xmltooling/base.h b/xmltooling/base.h index f9e81db..89320f2 100644 --- a/xmltooling/base.h +++ b/xmltooling/base.h @@ -235,42 +235,135 @@ #define END_XMLOBJECT } /** - * Declares abstract get/set methods for a named XML attribute. + * Declares abstract get/set methods for a typed XML attribute. * * @param proper the proper name of the attribute * @param upcased the upcased name of the attribute + * @param type the attribute's data type */ -#define DECL_XMLOBJECT_ATTRIB(proper,upcased) \ +#define DECL_XMLOBJECT_ATTRIB(proper,upcased,type) \ public: \ XMLTOOLING_DOXYGEN(proper attribute name) \ static const XMLCh upcased##_ATTRIB_NAME[]; \ XMLTOOLING_DOXYGEN(Returns the proper attribute.) \ - virtual const XMLCh* get##proper() const=0; \ + virtual const type* get##proper() const=0; \ XMLTOOLING_DOXYGEN(Sets the proper attribute.) \ - virtual void set##proper(const XMLCh* proper)=0 + virtual void set##proper(const type* proper)=0 /** - * Implements get/set methods and a private member for a named XML attribute. + * Declares abstract get/set methods for a string XML attribute. * * @param proper the proper name of the attribute + * @param upcased the upcased name of the attribute */ -#define IMPL_XMLOBJECT_ATTRIB(proper) \ - private: \ - XMLCh* m_##proper; \ +#define DECL_STRING_ATTRIB(proper,upcased) \ + DECL_XMLOBJECT_ATTRIB(proper,upcased,XMLCh) + +/** + * Declares abstract get/set methods for a string XML attribute. + * + * @param proper the proper name of the attribute + * @param upcased the upcased name of the attribute + */ +#define DECL_DATETIME_ATTRIB(proper,upcased) \ + DECL_XMLOBJECT_ATTRIB(proper,upcased,xmltooling::DateTime); \ + XMLTOOLING_DOXYGEN(Sets the proper attribute.) \ + virtual void set##proper(time_t proper)=0; \ + XMLTOOLING_DOXYGEN(Sets the proper attribute.) \ + virtual void set##proper(const XMLCh* proper)=0 + +/** + * Declares abstract get/set methods for an integer XML attribute. + * + * @param proper the proper name of the attribute + * @param upcased the upcased name of the attribute + */ +#define DECL_INTEGER_ATTRIB(proper,upcased) \ + public: \ + XMLTOOLING_DOXYGEN(proper attribute name) \ + static const XMLCh upcased##_ATTRIB_NAME[]; \ + XMLTOOLING_DOXYGEN(Returns the proper attribute.) \ + virtual int get##proper() const=0; \ + XMLTOOLING_DOXYGEN(Sets the proper attribute.) \ + virtual void set##proper(int proper)=0 + +/** + * Implements get/set methods and a private member for a typed XML attribute. + * + * @param proper the proper name of the attribute + * @param type the attribute's data type + */ +#define IMPL_XMLOBJECT_ATTRIB(proper,type) \ + protected: \ + type* m_##proper; \ public: \ - const XMLCh* get##proper() const { \ + const type* get##proper() const { \ return m_##proper; \ } \ - void set##proper(const XMLCh* proper) { \ + void set##proper(const type* proper) { \ m_##proper = prepareForAssignment(m_##proper,proper); \ } /** + * Implements get/set methods and a private member for a string XML attribute. + * + * @param proper the proper name of the attribute + */ +#define IMPL_STRING_ATTRIB(proper) \ + IMPL_XMLOBJECT_ATTRIB(proper,XMLCh) + +/** + * Implements get/set methods and a private member for a DateTime XML attribute. + * + * @param proper the proper name of the attribute + */ +#define IMPL_DATETIME_ATTRIB(proper) \ + IMPL_XMLOBJECT_ATTRIB(proper,xmltooling::DateTime); \ + void set##proper(time_t proper) { \ + m_##proper = prepareForAssignment(m_##proper,proper); \ + } \ + void set##proper(const XMLCh* proper) { \ + m_##proper = prepareForAssignment(m_##proper,proper); \ + } + +/** + * Implements get/set methods and a private member for an integer XML attribute. + * + * @param proper the proper name of the attribute + */ +#define IMPL_INTEGER_ATTRIB(proper) \ + protected: \ + int m_##proper; \ + public: \ + int get##proper() const { \ + return m_##proper; \ + } \ + void set##proper(int proper) { \ + if (m_##proper != proper) { \ + releaseThisandParentDOM(); \ + m_##proper = proper; \ + } \ + } + +/** + * Declares abstract get/set methods for a typed XML child object in a foreign namespace. + * + * @param proper the proper name of the child type + * @param ns the C++ namespace for the type + */ +#define DECL_TYPED_FOREIGN_CHILD(proper,ns) \ + public: \ + XMLTOOLING_DOXYGEN(Returns the proper child.) \ + virtual ns::proper* get##proper() const=0; \ + XMLTOOLING_DOXYGEN(Sets the proper child.) \ + virtual void set##proper(ns::proper* child)=0 + +/** * Declares abstract get/set methods for a typed XML child object. * * @param proper the proper name of the child type */ -#define DECL_XMLOBJECT_CHILD(proper) \ +#define DECL_TYPED_CHILD(proper) \ public: \ XMLTOOLING_DOXYGEN(Returns the proper child.) \ virtual proper* get##proper() const=0; \ @@ -278,14 +371,27 @@ virtual void set##proper(proper* child)=0 /** + * Declares abstract get/set methods for a generic XML child object. + * + * @param proper the proper name of the child + */ +#define DECL_XMLOBJECT_CHILD(proper) \ + public: \ + XMLTOOLING_DOXYGEN(Returns the proper child.) \ + virtual xmltooling::XMLObject* get##proper() const=0; \ + XMLTOOLING_DOXYGEN(Sets the proper child.) \ + virtual void set##proper(xmltooling::XMLObject* child)=0 + + +/** * Implements get/set methods and a private list iterator member for a typed XML child object. * * @param proper the proper name of the child type */ -#define IMPL_XMLOBJECT_CHILD(proper) \ - private: \ +#define IMPL_TYPED_CHILD(proper) \ + protected: \ proper* m_##proper; \ - std::list::iterator m_pos_##proper; \ + std::list::iterator m_pos_##proper; \ public: \ proper* get##proper() const { \ return m_##proper; \ @@ -296,11 +402,29 @@ } /** + * Implements get/set methods and a private list iterator member for a generic XML child object. + * + * @param proper the proper name of the child + */ +#define IMPL_XMLOBJECT_CHILD(proper) \ + protected: \ + xmltooling::XMLObject* m_##proper; \ + std::list::iterator m_pos_##proper; \ + public: \ + xmltooling::XMLObject* get##proper() const { \ + return m_##proper; \ + } \ + void set##proper(xmltooling::XMLObject* child) { \ + prepareForAssignment(m_##proper,child); \ + *m_pos_##proper = m_##proper = child; \ + } + +/** * Declares abstract get/set methods for a typed XML child collection. * * @param proper the proper name of the child type */ -#define DECL_XMLOBJECT_CHILDREN(proper) \ +#define DECL_TYPED_CHILDREN(proper) \ public: \ XMLTOOLING_DOXYGEN(Returns modifiable proper collection.) \ virtual VectorOf(proper) get##proper##s()=0; \ @@ -308,13 +432,25 @@ virtual const std::vector& get##proper##s() const=0 /** + * Declares abstract get/set methods for a generic XML child collection. + * + * @param proper the proper name of the child + */ +#define DECL_XMLOBJECT_CHILDREN(proper) \ + public: \ + XMLTOOLING_DOXYGEN(Returns modifiable proper collection.) \ + virtual VectorOf(xmltooling::XMLObject) get##proper##s()=0; \ + XMLTOOLING_DOXYGEN(Returns reference to immutable proper collection.) \ + virtual const std::vector& get##proper##s() const=0 + +/** * Implements get method and a private vector member for a typed XML child collection. * * @param proper the proper name of the child type * @param fence insertion fence for new objects of the child collection in backing list */ -#define IMPL_XMLOBJECT_CHILDREN(proper,fence) \ - private: \ +#define IMPL_TYPED_CHILDREN(proper,fence) \ + protected: \ std::vector m_##proper##s; \ public: \ VectorOf(proper) get##proper##s() { \ @@ -325,38 +461,93 @@ } /** - * Implements marshalling for an attribute + * Implements get method and a private vector member for a generic XML child collection. + * + * @param proper the proper name of the child + * @param fence insertion fence for new objects of the child collection in backing list + */ +#define IMPL_XMLOBJECT_CHILDREN(proper,fence) \ + protected: \ + std::vector m_##proper##s; \ + public: \ + VectorOf(xmltooling::XMLObject) get##proper##s() { \ + return VectorOf(xmltooling::XMLObject)(this, m_##proper##s, &m_children, fence); \ + } \ + const std::vector& get##proper##s() const { \ + return m_##proper##s; \ + } + +/** + * Implements marshalling for a string attribute * * @param proper the proper name of the attribute * @param ucase the upcased name of the attribute * @param namespaceURI the XML namespace of the attribute */ -#define MARSHALL_XMLOBJECT_ATTRIB(proper,ucase,namespaceURI) \ +#define MARSHALL_STRING_ATTRIB(proper,ucase,namespaceURI) \ if(get##proper()) { \ domElement->setAttributeNS(namespaceURI, ucase##_ATTRIB_NAME, get##proper()); \ } /** + * Implements marshalling for a DateTime attribute + * + * @param proper the proper name of the attribute + * @param ucase the upcased name of the attribute + * @param namespaceURI the XML namespace of the attribute + */ +#define MARSHALL_DATETIME_ATTRIB(proper,ucase,namespaceURI) \ + if(get##proper()) { \ + domElement->setAttributeNS(namespaceURI, ucase##_ATTRIB_NAME, get##proper()->getRawData()); \ + } + +/** + * Implements marshalling for an integer attribute + * + * @param proper the proper name of the attribute + * @param ucase the upcased name of the attribute + * @param namespaceURI the XML namespace of the attribute + */ +#define MARSHALL_INTEGER_ATTRIB(proper,ucase,namespaceURI) \ + char buf##proper[64]; \ + sprintf(buf##proper,"%d",get##proper()); \ + auto_ptr_XMLCh wide##proper(buf##proper); \ + domElement->setAttributeNS(namespaceURI, ucase##_ATTRIB_NAME, wide##proper.get()) + +/** + * Implements marshalling for a QName attribute + * + * @param proper the proper name of the attribute + * @param ucase the upcased name of the attribute + * @param namespaceURI the XML namespace of the attribute + */ +#define MARSHALL_QNAME_ATTRIB(proper,ucase,namespaceURI) \ + if(get##proper()) { \ + auto_ptr_XMLCh qstr(get##proper()->toString().c_str()); \ + domElement->setAttributeNS(namespaceURI, ucase##_ATTRIB_NAME, qstr.get()); \ + } + +/** * Implements marshalling for an ID attribute * * @param proper the proper name of the attribute * @param ucase the upcased name of the attribute * @param namespaceURI the XML namespace of the attribute */ -#define MARSHALL_XMLOBJECT_ID_ATTRIB(proper,ucase,namespaceURI) \ +#define MARSHALL_ID_ATTRIB(proper,ucase,namespaceURI) \ if(get##proper()) { \ domElement->setAttributeNS(namespaceURI, ucase##_ATTRIB_NAME, get##proper()); \ domElement->setIdAttributeNS(namespaceURI, ucase##_ATTRIB_NAME); \ } /** - * Implements unmarshalling process branch for an attribute + * Implements unmarshalling process branch for a string attribute * * @param proper the proper name of the attribute * @param ucase the upcased name of the attribute * @param namespaceURI the XML namespace of the attribute */ -#define PROC_XMLOBJECT_ATTRIB(proper,ucase,namespaceURI) \ +#define PROC_STRING_ATTRIB(proper,ucase,namespaceURI) \ if (xmltooling::XMLHelper::isNodeNamed(attribute, namespaceURI, ucase##_ATTRIB_NAME)) { \ set##proper(attribute->getValue()); \ return; \ @@ -369,7 +560,7 @@ * @param ucase the upcased name of the attribute * @param namespaceURI the XML namespace of the attribute */ -#define PROC_XMLOBJECT_ID_ATTRIB(proper,ucase,namespaceURI) \ +#define PROC_ID_ATTRIB(proper,ucase,namespaceURI) \ if (xmltooling::XMLHelper::isNodeNamed(attribute, namespaceURI, ucase##_ATTRIB_NAME)) { \ set##proper(attribute->getValue()); \ static_cast(attribute->getParentNode())->setIdAttributeNode(attribute); \ @@ -377,13 +568,51 @@ } /** + * Implements unmarshalling process branch for a DateTime attribute + * + * @param proper the proper name of the attribute + * @param ucase the upcased name of the attribute + * @param namespaceURI the XML namespace of the attribute + */ +#define PROC_DATETIME_ATTRIB(proper,ucase,namespaceURI) \ + PROC_STRING_ATTRIB(proper,ucase,namespaceURI) + +/** + * Implements unmarshalling process branch for a DateTime attribute + * + * @param proper the proper name of the attribute + * @param ucase the upcased name of the attribute + * @param namespaceURI the XML namespace of the attribute + */ +#define PROC_QNAME_ATTRIB(proper,ucase,namespaceURI) \ + if (xmltooling::XMLHelper::isNodeNamed(attribute, namespaceURI, ucase##_ATTRIB_NAME)) { \ + set##proper(XMLHelper::getAttributeValueAsQName(attribute)); \ + return; \ + } + +/** + * Implements unmarshalling process branch for an integer attribute + * + * @param proper the proper name of the attribute + * @param ucase the upcased name of the attribute + * @param namespaceURI the XML namespace of the attribute + */ +#define PROC_INTEGER_ATTRIB(proper,ucase,namespaceURI) \ + if (xmltooling::XMLHelper::isNodeNamed(attribute, namespaceURI, ucase##_ATTRIB_NAME)) { \ + set##proper(XMLString::parseInt(attribute->getValue())); \ + return; \ + } + + +/** * Implements unmarshalling process branch for typed child collection element * * @param proper the proper name of the child type * @param namespaceURI the XML namespace of the child element + * @param force bypass use of hint and just cast down to check child */ -#define PROC_XMLOBJECT_CHILDREN(proper,namespaceURI) \ - if (xmltooling::XMLHelper::isNodeNamed(root,namespaceURI,proper::LOCAL_NAME)) { \ +#define PROC_TYPED_CHILDREN(proper,namespaceURI,force) \ + if (force || xmltooling::XMLHelper::isNodeNamed(root,namespaceURI,proper::LOCAL_NAME)) { \ proper* typesafe=dynamic_cast(childXMLObject); \ if (typesafe) { \ get##proper##s().push_back(typesafe); \ @@ -396,9 +625,10 @@ * * @param proper the proper name of the child type * @param namespaceURI the XML namespace of the child element + * @param force bypass use of hint and just cast down to check child */ -#define PROC_XMLOBJECT_CHILD(proper,namespaceURI) \ - if (xmltooling::XMLHelper::isNodeNamed(root,namespaceURI,proper::LOCAL_NAME)) { \ +#define PROC_TYPED_CHILD(proper,namespaceURI,force) \ + if (force || xmltooling::XMLHelper::isNodeNamed(root,namespaceURI,proper::LOCAL_NAME)) { \ proper* typesafe=dynamic_cast(childXMLObject); \ if (typesafe) { \ set##proper(typesafe); \ @@ -478,7 +708,7 @@ */ #define DECL_XMLOBJECTIMPL_SIMPLE(linkage,cname) \ class linkage cname##Impl \ - : public cname, \ + : public virtual cname, \ public xmltooling::AbstractSimpleElement, \ public xmltooling::AbstractChildlessElement, \ public xmltooling::AbstractDOMCachingXMLObject, \ @@ -619,6 +849,18 @@ throw xmltooling::ValidationException(#cname" must have "#proper1" or "#proper2".") /** + * Validator code that checks for one of a pair of + * required attributes, content, or singletons, but disallows both. + * + * @param cname the name of the XMLObject specialization + * @param proper1 the proper name of the first attribute, content, or singleton member + * @param proper2 the proper name of the second attribute, content, or singleton member + */ +#define XMLOBJECTVALIDATOR_ONLYONEOF(cname,proper1,proper2) \ + if ((!ptr->get##proper1() && !ptr->get##proper2()) || (ptr->get##proper1() && ptr->get##proper2())) \ + throw xmltooling::ValidationException(#cname" must have "#proper1" or "#proper2" but not both.") + +/** * Validator code that checks for one of a set of three * required attributes, content, or singletons. * @@ -632,6 +874,26 @@ throw xmltooling::ValidationException(#cname" must have "#proper1", "#proper2", or "#proper3".") /** + * Validator code that checks for one of a set of three + * required attributes, content, or singletons but disallows more than one. + * + * @param cname the name of the XMLObject specialization + * @param proper1 the proper name of the first attribute, content, or singleton member + * @param proper2 the proper name of the second attribute, content, or singleton member + * @param proper3 the proper name of the third attribute, content, or singleton member + */ +#define XMLOBJECTVALIDATOR_ONLYONEOF3(cname,proper1,proper2,proper3) \ + int c##proper1##proper2##proper3=0; \ + if (ptr->get##proper1()!=NULL) \ + c##proper1##proper2##proper3++; \ + if (ptr->get##proper2()!=NULL) \ + c##proper1##proper2##proper3++; \ + if (ptr->get##proper3()!=NULL) \ + c##proper1##proper2##proper3++; \ + if (c##proper1##proper2##proper3 != 1) \ + throw xmltooling::ValidationException(#cname" must have only one of "#proper1", "#proper2", or "#proper3".") + +/** * Validator code that checks a co-constraint (if one present, the other must be) * between a pair of attributes, content, or singletons. * diff --git a/xmltooling/io/AbstractXMLObjectUnmarshaller.cpp b/xmltooling/io/AbstractXMLObjectUnmarshaller.cpp index 3afab8c..e44e37d 100644 --- a/xmltooling/io/AbstractXMLObjectUnmarshaller.cpp +++ b/xmltooling/io/AbstractXMLObjectUnmarshaller.cpp @@ -177,5 +177,5 @@ void AbstractXMLObjectUnmarshaller::unmarshallChildElements(const DOMElement* do void AbstractXMLObjectUnmarshaller::processChildElement(XMLObject* child, const DOMElement* childRoot) { - throw UnmarshallingException("Child elements are not permitted on this object."); + throw UnmarshallingException("Invalid child element: $1",params(1,child->getElementQName().toString().c_str())); } diff --git a/xmltooling/signature/KeyInfo.h b/xmltooling/signature/KeyInfo.h index 00cc9d6..3981b3d 100644 --- a/xmltooling/signature/KeyInfo.h +++ b/xmltooling/signature/KeyInfo.h @@ -59,67 +59,67 @@ namespace xmlsignature { DECL_XMLOBJECT_SIMPLE(XMLTOOL_API,PGPKeyPacket,Packet,XML Digital Signature version 20020212 PGPKeyPacket element); BEGIN_XMLOBJECT(XMLTOOL_API,DSAKeyValue,xmltooling::XMLObject,XML Digital Signature version 20020212 DSAKeyValue element); - DECL_XMLOBJECT_CHILD(P); - DECL_XMLOBJECT_CHILD(Q); - DECL_XMLOBJECT_CHILD(G); - DECL_XMLOBJECT_CHILD(Y); - DECL_XMLOBJECT_CHILD(J); - DECL_XMLOBJECT_CHILD(Seed); - DECL_XMLOBJECT_CHILD(PgenCounter); + DECL_TYPED_CHILD(P); + DECL_TYPED_CHILD(Q); + DECL_TYPED_CHILD(G); + DECL_TYPED_CHILD(Y); + DECL_TYPED_CHILD(J); + DECL_TYPED_CHILD(Seed); + DECL_TYPED_CHILD(PgenCounter); /** DSAKeyValueType local name */ static const XMLCh TYPE_NAME[]; END_XMLOBJECT; BEGIN_XMLOBJECT(XMLTOOL_API,RSAKeyValue,xmltooling::XMLObject,XML Digital Signature version 20020212 RSAKeyValue element); - DECL_XMLOBJECT_CHILD(Modulus); - DECL_XMLOBJECT_CHILD(Exponent); + DECL_TYPED_CHILD(Modulus); + DECL_TYPED_CHILD(Exponent); /** RSAKeyValueType local name */ static const XMLCh TYPE_NAME[]; END_XMLOBJECT; BEGIN_XMLOBJECT(XMLTOOL_API,KeyValue,xmltooling::SimpleElement,XML Digital Signature version 20020212 KeyValue element); - DECL_XMLOBJECT_CHILD(DSAKeyValue); - DECL_XMLOBJECT_CHILD(RSAKeyValue); - DECL_XMLOBJECT_CHILD(XMLObject); + DECL_TYPED_CHILD(DSAKeyValue); + DECL_TYPED_CHILD(RSAKeyValue); + DECL_XMLOBJECT_CHILD(OtherKeyValue); /** KeyValueType local name */ static const XMLCh TYPE_NAME[]; END_XMLOBJECT; BEGIN_XMLOBJECT(XMLTOOL_API,Transform,xmltooling::ElementProxy,XML Digital Signature version 20020212 Transform element); - DECL_XMLOBJECT_ATTRIB(Algorithm,ALGORITHM); - DECL_XMLOBJECT_CHILDREN(XPath); + DECL_STRING_ATTRIB(Algorithm,ALGORITHM); + DECL_TYPED_CHILDREN(XPath); /** TransformType local name */ static const XMLCh TYPE_NAME[]; END_XMLOBJECT; BEGIN_XMLOBJECT(XMLTOOL_API,Transforms,xmltooling::XMLObject,XML Digital Signature version 20020212 Transforms element); - DECL_XMLOBJECT_CHILDREN(Transform); + DECL_TYPED_CHILDREN(Transform); /** TransformsType local name */ static const XMLCh TYPE_NAME[]; END_XMLOBJECT; BEGIN_XMLOBJECT(XMLTOOL_API,RetrievalMethod,xmltooling::XMLObject,XML Digital Signature version 20020212 RetrievalMethod element); - DECL_XMLOBJECT_ATTRIB(URI,URI); - DECL_XMLOBJECT_ATTRIB(Type,TYPE); - DECL_XMLOBJECT_CHILD(Transforms); + DECL_STRING_ATTRIB(URI,URI); + DECL_STRING_ATTRIB(Type,TYPE); + DECL_TYPED_CHILD(Transforms); /** RetrievalMethodType local name */ static const XMLCh TYPE_NAME[]; END_XMLOBJECT; BEGIN_XMLOBJECT(XMLTOOL_API,X509IssuerSerial,xmltooling::XMLObject,XML Digital Signature version 20020212 X509IssuerSerial element); - DECL_XMLOBJECT_CHILD(X509IssuerName); - DECL_XMLOBJECT_CHILD(X509SerialNumber); + DECL_TYPED_CHILD(X509IssuerName); + DECL_TYPED_CHILD(X509SerialNumber); /** X509IssuerSerialType local name */ static const XMLCh TYPE_NAME[]; END_XMLOBJECT; BEGIN_XMLOBJECT(XMLTOOL_API,X509Data,xmltooling::XMLObject,XML Digital Signature version 20020212 X509Data element); - DECL_XMLOBJECT_CHILDREN(X509IssuerSerial); - DECL_XMLOBJECT_CHILDREN(X509SKI); - DECL_XMLOBJECT_CHILDREN(X509SubjectName); - DECL_XMLOBJECT_CHILDREN(X509Certificate); - DECL_XMLOBJECT_CHILDREN(X509CRL); - DECL_XMLOBJECT_CHILDREN(XMLObject); + DECL_TYPED_CHILDREN(X509IssuerSerial); + DECL_TYPED_CHILDREN(X509SKI); + DECL_TYPED_CHILDREN(X509SubjectName); + DECL_TYPED_CHILDREN(X509Certificate); + DECL_TYPED_CHILDREN(X509CRL); + DECL_XMLOBJECT_CHILDREN(OtherX509Data); /** X509DataType local name */ static const XMLCh TYPE_NAME[]; END_XMLOBJECT; @@ -136,23 +136,23 @@ namespace xmlsignature { END_XMLOBJECT; BEGIN_XMLOBJECT(XMLTOOL_API,PGPData,xmltooling::XMLObject,XML Digital Signature version 20020212 PGPData element); - DECL_XMLOBJECT_CHILD(PGPKeyID); - DECL_XMLOBJECT_CHILD(PGPKeyPacket); - DECL_XMLOBJECT_CHILDREN(XMLObject); + DECL_TYPED_CHILD(PGPKeyID); + DECL_TYPED_CHILD(PGPKeyPacket); + DECL_XMLOBJECT_CHILDREN(PGPDataExtension); /** PGPDataType local name */ static const XMLCh TYPE_NAME[]; END_XMLOBJECT; BEGIN_XMLOBJECT(XMLTOOL_API,KeyInfo,xmltooling::XMLObject,XML Digital Signature version 20020212 KeyInfo element); - DECL_XMLOBJECT_ATTRIB(Id,ID); - DECL_XMLOBJECT_CHILDREN(X509Data); - DECL_XMLOBJECT_CHILDREN(KeyName); - DECL_XMLOBJECT_CHILDREN(KeyValue); - DECL_XMLOBJECT_CHILDREN(RetrievalMethod); - DECL_XMLOBJECT_CHILDREN(MgmtData); - DECL_XMLOBJECT_CHILDREN(PGPData); - DECL_XMLOBJECT_CHILDREN(SPKIData); - DECL_XMLOBJECT_CHILDREN(XMLObject); + DECL_STRING_ATTRIB(Id,ID); + DECL_TYPED_CHILDREN(X509Data); + DECL_TYPED_CHILDREN(KeyName); + DECL_TYPED_CHILDREN(KeyValue); + DECL_TYPED_CHILDREN(RetrievalMethod); + DECL_TYPED_CHILDREN(MgmtData); + DECL_TYPED_CHILDREN(PGPData); + DECL_TYPED_CHILDREN(SPKIData); + DECL_XMLOBJECT_CHILDREN(Other); /** KeyInfoType local name */ static const XMLCh TYPE_NAME[]; END_XMLOBJECT; diff --git a/xmltooling/signature/Signature.h b/xmltooling/signature/Signature.h index 82b0c5b..8717c6f 100644 --- a/xmltooling/signature/Signature.h +++ b/xmltooling/signature/Signature.h @@ -77,6 +77,13 @@ namespace xmlsignature { * @throws SignatureException thrown if the verifying operation fails */ virtual void verify(const VerifyingContext& ctx) const=0; + + /** + * Type-safe clone operation. + * + * @return copy of object + */ + virtual Signature* cloneSignature() const=0; protected: Signature() {} @@ -111,7 +118,8 @@ namespace xmlsignature { } }; - DECL_XMLTOOLING_EXCEPTION(SignatureException,XMLTOOL_EXCEPTIONAPI(XMLTOOL_API),xmlsignature,xmltooling::XMLToolingException,Exceptions in signature processing); + DECL_XMLTOOLING_EXCEPTION(XMLSecurityException,XMLTOOL_EXCEPTIONAPI(XMLTOOL_API),xmlsignature,xmltooling::XMLToolingException,Exceptions in XML Security processing); + DECL_XMLTOOLING_EXCEPTION(SignatureException,XMLTOOL_EXCEPTIONAPI(XMLTOOL_API),xmlsignature,xmlsignature::XMLSecurityException,Exceptions in signature processing); }; diff --git a/xmltooling/signature/impl/KeyInfoImpl.cpp b/xmltooling/signature/impl/KeyInfoImpl.cpp index f86ebd9..4643bf7 100644 --- a/xmltooling/signature/impl/KeyInfoImpl.cpp +++ b/xmltooling/signature/impl/KeyInfoImpl.cpp @@ -1,7 +1,7 @@ /* -* Copyright 2001-2006 Internet2 + * Copyright 2001-2006 Internet2 * -* Licensed under the Apache License, Version 2.0 (the "License"); + * 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 * @@ -45,7 +45,7 @@ using namespace std; namespace xmlsignature { - class XMLTOOL_DLLLOCAL DSAKeyValueImpl : public DSAKeyValue, + class XMLTOOL_DLLLOCAL DSAKeyValueImpl : public virtual DSAKeyValue, public AbstractComplexElement, public AbstractDOMCachingXMLObject, public AbstractValidatingXMLObject, @@ -110,28 +110,28 @@ namespace xmlsignature { } IMPL_XMLOBJECT_CLONE(DSAKeyValue); - IMPL_XMLOBJECT_CHILD(P); - IMPL_XMLOBJECT_CHILD(Q); - IMPL_XMLOBJECT_CHILD(G); - IMPL_XMLOBJECT_CHILD(Y); - IMPL_XMLOBJECT_CHILD(J); - IMPL_XMLOBJECT_CHILD(Seed); - IMPL_XMLOBJECT_CHILD(PgenCounter); + IMPL_TYPED_CHILD(P); + IMPL_TYPED_CHILD(Q); + IMPL_TYPED_CHILD(G); + IMPL_TYPED_CHILD(Y); + IMPL_TYPED_CHILD(J); + IMPL_TYPED_CHILD(Seed); + IMPL_TYPED_CHILD(PgenCounter); protected: void processChildElement(XMLObject* childXMLObject, const DOMElement* root) { - PROC_XMLOBJECT_CHILD(P,XMLConstants::XMLSIG_NS); - PROC_XMLOBJECT_CHILD(Q,XMLConstants::XMLSIG_NS); - PROC_XMLOBJECT_CHILD(G,XMLConstants::XMLSIG_NS); - PROC_XMLOBJECT_CHILD(Y,XMLConstants::XMLSIG_NS); - PROC_XMLOBJECT_CHILD(J,XMLConstants::XMLSIG_NS); - PROC_XMLOBJECT_CHILD(Seed,XMLConstants::XMLSIG_NS); - PROC_XMLOBJECT_CHILD(PgenCounter,XMLConstants::XMLSIG_NS); - throw UnmarshallingException("Invalid child element: $1",params(1,childXMLObject->getElementQName().toString().c_str())); + PROC_TYPED_CHILD(P,XMLConstants::XMLSIG_NS,false); + PROC_TYPED_CHILD(Q,XMLConstants::XMLSIG_NS,false); + PROC_TYPED_CHILD(G,XMLConstants::XMLSIG_NS,false); + PROC_TYPED_CHILD(Y,XMLConstants::XMLSIG_NS,false); + PROC_TYPED_CHILD(J,XMLConstants::XMLSIG_NS,false); + PROC_TYPED_CHILD(Seed,XMLConstants::XMLSIG_NS,false); + PROC_TYPED_CHILD(PgenCounter,XMLConstants::XMLSIG_NS,false); + AbstractXMLObjectUnmarshaller::processChildElement(childXMLObject,root); } }; - class XMLTOOL_DLLLOCAL RSAKeyValueImpl : public RSAKeyValue, + class XMLTOOL_DLLLOCAL RSAKeyValueImpl : public virtual RSAKeyValue, public AbstractComplexElement, public AbstractDOMCachingXMLObject, public AbstractValidatingXMLObject, @@ -166,18 +166,18 @@ namespace xmlsignature { } IMPL_XMLOBJECT_CLONE(RSAKeyValue); - IMPL_XMLOBJECT_CHILD(Modulus); - IMPL_XMLOBJECT_CHILD(Exponent); + IMPL_TYPED_CHILD(Modulus); + IMPL_TYPED_CHILD(Exponent); protected: void processChildElement(XMLObject* childXMLObject, const DOMElement* root) { - PROC_XMLOBJECT_CHILD(Modulus,XMLConstants::XMLSIG_NS); - PROC_XMLOBJECT_CHILD(Exponent,XMLConstants::XMLSIG_NS); - throw UnmarshallingException("Invalid child element: $1",params(1,childXMLObject->getElementQName().toString().c_str())); + PROC_TYPED_CHILD(Modulus,XMLConstants::XMLSIG_NS,false); + PROC_TYPED_CHILD(Exponent,XMLConstants::XMLSIG_NS,false); + AbstractXMLObjectUnmarshaller::processChildElement(childXMLObject,root); } }; - class XMLTOOL_DLLLOCAL KeyValueImpl : public KeyValue, + class XMLTOOL_DLLLOCAL KeyValueImpl : public virtual KeyValue, public AbstractSimpleElement, public AbstractComplexElement, public AbstractDOMCachingXMLObject, @@ -201,45 +201,45 @@ namespace xmlsignature { setDSAKeyValue(src.getDSAKeyValue()->cloneDSAKeyValue()); if (src.getRSAKeyValue()) setRSAKeyValue(src.getRSAKeyValue()->cloneRSAKeyValue()); - if (src.getXMLObject()) - setXMLObject(src.getXMLObject()->clone()); + if (src.getOtherKeyValue()) + setOtherKeyValue(src.getOtherKeyValue()->clone()); } void init() { m_DSAKeyValue=NULL; m_RSAKeyValue=NULL; - m_XMLObject=NULL; + m_OtherKeyValue=NULL; m_children.push_back(NULL); m_children.push_back(NULL); m_children.push_back(NULL); m_pos_DSAKeyValue=m_children.begin(); m_pos_RSAKeyValue=m_pos_DSAKeyValue; ++m_pos_RSAKeyValue; - m_pos_XMLObject=m_pos_RSAKeyValue; - ++m_pos_XMLObject; + m_pos_OtherKeyValue=m_pos_RSAKeyValue; + ++m_pos_OtherKeyValue; } IMPL_XMLOBJECT_CLONE(KeyValue); - IMPL_XMLOBJECT_CHILD(DSAKeyValue); - IMPL_XMLOBJECT_CHILD(RSAKeyValue); - IMPL_XMLOBJECT_CHILD(XMLObject); + IMPL_TYPED_CHILD(DSAKeyValue); + IMPL_TYPED_CHILD(RSAKeyValue); + IMPL_XMLOBJECT_CHILD(OtherKeyValue); IMPL_XMLOBJECT_CONTENT; protected: void processChildElement(XMLObject* childXMLObject, const DOMElement* root) { - PROC_XMLOBJECT_CHILD(DSAKeyValue,XMLConstants::XMLSIG_NS); - PROC_XMLOBJECT_CHILD(RSAKeyValue,XMLConstants::XMLSIG_NS); + PROC_TYPED_CHILD(DSAKeyValue,XMLConstants::XMLSIG_NS,false); + PROC_TYPED_CHILD(RSAKeyValue,XMLConstants::XMLSIG_NS,false); // Unknown child. const XMLCh* nsURI=root->getNamespaceURI(); if (!XMLString::equals(nsURI,XMLConstants::XMLSIG_NS) && nsURI && *nsURI) - setXMLObject(childXMLObject); + setOtherKeyValue(childXMLObject); - throw UnmarshallingException("Invalid child element: $1",params(1,childXMLObject->getElementQName().toString().c_str())); + AbstractXMLObjectUnmarshaller::processChildElement(childXMLObject,root); } }; - class XMLTOOL_DLLLOCAL TransformImpl : public Transform, + class XMLTOOL_DLLLOCAL TransformImpl : public virtual Transform, public AbstractDOMCachingXMLObject, public AbstractElementProxy, public AbstractValidatingXMLObject, @@ -271,32 +271,32 @@ namespace xmlsignature { } IMPL_XMLOBJECT_CLONE(Transform); - IMPL_XMLOBJECT_ATTRIB(Algorithm); - IMPL_XMLOBJECT_CHILDREN(XPath,m_children.end()); + IMPL_STRING_ATTRIB(Algorithm); + IMPL_TYPED_CHILDREN(XPath,m_children.end()); IMPL_XMLOBJECT_CONTENT; protected: void marshallAttributes(DOMElement* domElement) const { - MARSHALL_XMLOBJECT_ATTRIB(Algorithm,ALGORITHM,NULL); + MARSHALL_STRING_ATTRIB(Algorithm,ALGORITHM,NULL); } void processChildElement(XMLObject* childXMLObject, const DOMElement* root) { - PROC_XMLOBJECT_CHILDREN(XPath,XMLConstants::XMLSIG_NS); + PROC_TYPED_CHILDREN(XPath,XMLConstants::XMLSIG_NS,false); // Unknown child. const XMLCh* nsURI=root->getNamespaceURI(); if (!XMLString::equals(nsURI,XMLConstants::XMLSIG_NS) && nsURI && *nsURI) getXMLObjects().push_back(childXMLObject); - throw UnmarshallingException("Invalid child element: $1",params(1,childXMLObject->getElementQName().toString().c_str())); + AbstractXMLObjectUnmarshaller::processChildElement(childXMLObject,root); } void processAttribute(const DOMAttr* attribute) { - PROC_XMLOBJECT_ATTRIB(Algorithm,ALGORITHM,NULL); + PROC_STRING_ATTRIB(Algorithm,ALGORITHM,NULL); } }; - class XMLTOOL_DLLLOCAL TransformsImpl : public Transforms, + class XMLTOOL_DLLLOCAL TransformsImpl : public virtual Transforms, public AbstractComplexElement, public AbstractDOMCachingXMLObject, public AbstractValidatingXMLObject, @@ -321,16 +321,16 @@ namespace xmlsignature { } IMPL_XMLOBJECT_CLONE(Transforms); - IMPL_XMLOBJECT_CHILDREN(Transform,m_children.end()); + IMPL_TYPED_CHILDREN(Transform,m_children.end()); protected: void processChildElement(XMLObject* childXMLObject, const DOMElement* root) { - PROC_XMLOBJECT_CHILDREN(Transform,XMLConstants::XMLSIG_NS); - throw UnmarshallingException("Invalid child element: $1",params(1,childXMLObject->getElementQName().toString().c_str())); + PROC_TYPED_CHILDREN(Transform,XMLConstants::XMLSIG_NS,false); + AbstractXMLObjectUnmarshaller::processChildElement(childXMLObject,root); } }; - class XMLTOOL_DLLLOCAL RetrievalMethodImpl : public RetrievalMethod, + class XMLTOOL_DLLLOCAL RetrievalMethodImpl : public virtual RetrievalMethod, public AbstractComplexElement, public AbstractDOMCachingXMLObject, public AbstractValidatingXMLObject, @@ -365,28 +365,28 @@ namespace xmlsignature { } IMPL_XMLOBJECT_CLONE(RetrievalMethod); - IMPL_XMLOBJECT_ATTRIB(URI); - IMPL_XMLOBJECT_ATTRIB(Type); - IMPL_XMLOBJECT_CHILD(Transforms); + IMPL_STRING_ATTRIB(URI); + IMPL_STRING_ATTRIB(Type); + IMPL_TYPED_CHILD(Transforms); protected: void marshallAttributes(DOMElement* domElement) const { - MARSHALL_XMLOBJECT_ATTRIB(URI,URI,NULL); - MARSHALL_XMLOBJECT_ATTRIB(Type,TYPE,NULL); + MARSHALL_STRING_ATTRIB(URI,URI,NULL); + MARSHALL_STRING_ATTRIB(Type,TYPE,NULL); } void processChildElement(XMLObject* childXMLObject, const DOMElement* root) { - PROC_XMLOBJECT_CHILD(Transforms,XMLConstants::XMLSIG_NS); - throw UnmarshallingException("Invalid child element: $1",params(1,childXMLObject->getElementQName().toString().c_str())); + PROC_TYPED_CHILD(Transforms,XMLConstants::XMLSIG_NS,false); + AbstractXMLObjectUnmarshaller::processChildElement(childXMLObject,root); } void processAttribute(const DOMAttr* attribute) { - PROC_XMLOBJECT_ATTRIB(URI,URI,NULL); - PROC_XMLOBJECT_ATTRIB(Type,TYPE,NULL); + PROC_STRING_ATTRIB(URI,URI,NULL); + PROC_STRING_ATTRIB(Type,TYPE,NULL); } }; - class XMLTOOL_DLLLOCAL X509IssuerSerialImpl : public X509IssuerSerial, + class XMLTOOL_DLLLOCAL X509IssuerSerialImpl : public virtual X509IssuerSerial, public AbstractComplexElement, public AbstractDOMCachingXMLObject, public AbstractValidatingXMLObject, @@ -421,18 +421,18 @@ namespace xmlsignature { } IMPL_XMLOBJECT_CLONE(X509IssuerSerial); - IMPL_XMLOBJECT_CHILD(X509IssuerName); - IMPL_XMLOBJECT_CHILD(X509SerialNumber); + IMPL_TYPED_CHILD(X509IssuerName); + IMPL_TYPED_CHILD(X509SerialNumber); protected: void processChildElement(XMLObject* childXMLObject, const DOMElement* root) { - PROC_XMLOBJECT_CHILD(X509IssuerName,XMLConstants::XMLSIG_NS); - PROC_XMLOBJECT_CHILD(X509SerialNumber,XMLConstants::XMLSIG_NS); - throw UnmarshallingException("Invalid child element: $1",params(1,childXMLObject->getElementQName().toString().c_str())); + PROC_TYPED_CHILD(X509IssuerName,XMLConstants::XMLSIG_NS,false); + PROC_TYPED_CHILD(X509SerialNumber,XMLConstants::XMLSIG_NS,false); + AbstractXMLObjectUnmarshaller::processChildElement(childXMLObject,root); } }; - class XMLTOOL_DLLLOCAL X509DataImpl : public X509Data, + class XMLTOOL_DLLLOCAL X509DataImpl : public virtual X509Data, public AbstractComplexElement, public AbstractDOMCachingXMLObject, public AbstractValidatingXMLObject, @@ -480,37 +480,37 @@ namespace xmlsignature { continue; } - getXMLObjects().push_back((*i)->clone()); + getOtherX509Datas().push_back((*i)->clone()); } } } IMPL_XMLOBJECT_CLONE(X509Data); - IMPL_XMLOBJECT_CHILDREN(X509IssuerSerial,m_children.end()); - IMPL_XMLOBJECT_CHILDREN(X509SKI,m_children.end()); - IMPL_XMLOBJECT_CHILDREN(X509SubjectName,m_children.end()); - IMPL_XMLOBJECT_CHILDREN(X509Certificate,m_children.end()); - IMPL_XMLOBJECT_CHILDREN(X509CRL,m_children.end()); - IMPL_XMLOBJECT_CHILDREN(XMLObject,m_children.end()); + IMPL_TYPED_CHILDREN(X509IssuerSerial,m_children.end()); + IMPL_TYPED_CHILDREN(X509SKI,m_children.end()); + IMPL_TYPED_CHILDREN(X509SubjectName,m_children.end()); + IMPL_TYPED_CHILDREN(X509Certificate,m_children.end()); + IMPL_TYPED_CHILDREN(X509CRL,m_children.end()); + IMPL_XMLOBJECT_CHILDREN(OtherX509Data,m_children.end()); protected: void processChildElement(XMLObject* childXMLObject, const DOMElement* root) { - PROC_XMLOBJECT_CHILDREN(X509IssuerSerial,XMLConstants::XMLSIG_NS); - PROC_XMLOBJECT_CHILDREN(X509SKI,XMLConstants::XMLSIG_NS); - PROC_XMLOBJECT_CHILDREN(X509SubjectName,XMLConstants::XMLSIG_NS); - PROC_XMLOBJECT_CHILDREN(X509Certificate,XMLConstants::XMLSIG_NS); - PROC_XMLOBJECT_CHILDREN(X509CRL,XMLConstants::XMLSIG_NS); + PROC_TYPED_CHILDREN(X509IssuerSerial,XMLConstants::XMLSIG_NS,false); + PROC_TYPED_CHILDREN(X509SKI,XMLConstants::XMLSIG_NS,false); + PROC_TYPED_CHILDREN(X509SubjectName,XMLConstants::XMLSIG_NS,false); + PROC_TYPED_CHILDREN(X509Certificate,XMLConstants::XMLSIG_NS,false); + PROC_TYPED_CHILDREN(X509CRL,XMLConstants::XMLSIG_NS,false); // Unknown child. const XMLCh* nsURI=root->getNamespaceURI(); if (!XMLString::equals(nsURI,XMLConstants::XMLSIG_NS) && nsURI && *nsURI) - getXMLObjects().push_back(childXMLObject); + getOtherX509Datas().push_back(childXMLObject); - throw UnmarshallingException("Invalid child element: $1",params(1,childXMLObject->getElementQName().toString().c_str())); + AbstractXMLObjectUnmarshaller::processChildElement(childXMLObject,root); } }; - class XMLTOOL_DLLLOCAL SPKIDataImpl : public SPKIData, + class XMLTOOL_DLLLOCAL SPKIDataImpl : public virtual SPKIData, public AbstractComplexElement, public AbstractDOMCachingXMLObject, public AbstractValidatingXMLObject, @@ -571,11 +571,11 @@ namespace xmlsignature { throw UnmarshallingException("Extension element must follow ds:SPKISexp element."); } - throw UnmarshallingException("Invalid child element: $1",params(1,childXMLObject->getElementQName().toString().c_str())); + AbstractXMLObjectUnmarshaller::processChildElement(childXMLObject,root); } }; - class XMLTOOL_DLLLOCAL PGPDataImpl : public PGPData, + class XMLTOOL_DLLLOCAL PGPDataImpl : public virtual PGPData, public AbstractComplexElement, public AbstractDOMCachingXMLObject, public AbstractValidatingXMLObject, @@ -597,8 +597,8 @@ namespace xmlsignature { setPGPKeyID(src.getPGPKeyID()->clonePGPKeyID()); if (src.getPGPKeyPacket()) setPGPKeyPacket(src.getPGPKeyPacket()->clonePGPKeyPacket()); - VectorOf(XMLObject) v=getXMLObjects(); - for (vector::const_iterator i=src.m_XMLObjects.begin(); i!=src.m_XMLObjects.end(); i++) { + VectorOf(XMLObject) v=getPGPDataExtensions(); + for (vector::const_iterator i=src.m_PGPDataExtensions.begin(); i!=src.m_PGPDataExtensions.end(); i++) { if (*i) { v.push_back((*i)->clone()); } @@ -616,25 +616,25 @@ namespace xmlsignature { } IMPL_XMLOBJECT_CLONE(PGPData); - IMPL_XMLOBJECT_CHILD(PGPKeyID); - IMPL_XMLOBJECT_CHILD(PGPKeyPacket); - IMPL_XMLOBJECT_CHILDREN(XMLObject,m_children.end()); + IMPL_TYPED_CHILD(PGPKeyID); + IMPL_TYPED_CHILD(PGPKeyPacket); + IMPL_XMLOBJECT_CHILDREN(PGPDataExtension,m_children.end()); protected: void processChildElement(XMLObject* childXMLObject, const DOMElement* root) { - PROC_XMLOBJECT_CHILD(PGPKeyID,XMLConstants::XMLSIG_NS); - PROC_XMLOBJECT_CHILD(PGPKeyPacket,XMLConstants::XMLSIG_NS); + PROC_TYPED_CHILD(PGPKeyID,XMLConstants::XMLSIG_NS,false); + PROC_TYPED_CHILD(PGPKeyPacket,XMLConstants::XMLSIG_NS,false); // Unknown child. const XMLCh* nsURI=root->getNamespaceURI(); if (!XMLString::equals(nsURI,XMLConstants::XMLSIG_NS) && nsURI && *nsURI) - getXMLObjects().push_back(childXMLObject); + getPGPDataExtensions().push_back(childXMLObject); - throw UnmarshallingException("Invalid child element: $1",params(1,childXMLObject->getElementQName().toString().c_str())); + AbstractXMLObjectUnmarshaller::processChildElement(childXMLObject,root); } }; - class XMLTOOL_DLLLOCAL KeyInfoImpl : public KeyInfo, + class XMLTOOL_DLLLOCAL KeyInfoImpl : public virtual KeyInfo, public AbstractComplexElement, public AbstractSimpleElement, public AbstractDOMCachingXMLObject, @@ -702,47 +702,47 @@ namespace xmlsignature { continue; } - getXMLObjects().push_back((*i)->clone()); + getOthers().push_back((*i)->clone()); } } } IMPL_XMLOBJECT_CLONE(KeyInfo); - IMPL_XMLOBJECT_ATTRIB(Id); - IMPL_XMLOBJECT_CHILDREN(KeyName,m_children.end()); - IMPL_XMLOBJECT_CHILDREN(KeyValue,m_children.end()); - IMPL_XMLOBJECT_CHILDREN(RetrievalMethod,m_children.end()); - IMPL_XMLOBJECT_CHILDREN(X509Data,m_children.end()); - IMPL_XMLOBJECT_CHILDREN(MgmtData,m_children.end()); - IMPL_XMLOBJECT_CHILDREN(SPKIData,m_children.end()); - IMPL_XMLOBJECT_CHILDREN(PGPData,m_children.end()); - IMPL_XMLOBJECT_CHILDREN(XMLObject,m_children.end()); + IMPL_STRING_ATTRIB(Id); + IMPL_TYPED_CHILDREN(KeyName,m_children.end()); + IMPL_TYPED_CHILDREN(KeyValue,m_children.end()); + IMPL_TYPED_CHILDREN(RetrievalMethod,m_children.end()); + IMPL_TYPED_CHILDREN(X509Data,m_children.end()); + IMPL_TYPED_CHILDREN(MgmtData,m_children.end()); + IMPL_TYPED_CHILDREN(SPKIData,m_children.end()); + IMPL_TYPED_CHILDREN(PGPData,m_children.end()); + IMPL_XMLOBJECT_CHILDREN(Other,m_children.end()); IMPL_XMLOBJECT_CONTENT; protected: void marshallAttributes(DOMElement* domElement) const { - MARSHALL_XMLOBJECT_ID_ATTRIB(Id,ID,NULL); + MARSHALL_ID_ATTRIB(Id,ID,NULL); } void processChildElement(XMLObject* childXMLObject, const DOMElement* root) { - PROC_XMLOBJECT_CHILDREN(X509Data,XMLConstants::XMLSIG_NS); - PROC_XMLOBJECT_CHILDREN(KeyName,XMLConstants::XMLSIG_NS); - PROC_XMLOBJECT_CHILDREN(KeyValue,XMLConstants::XMLSIG_NS); - PROC_XMLOBJECT_CHILDREN(RetrievalMethod,XMLConstants::XMLSIG_NS); - PROC_XMLOBJECT_CHILDREN(MgmtData,XMLConstants::XMLSIG_NS); - PROC_XMLOBJECT_CHILDREN(SPKIData,XMLConstants::XMLSIG_NS); - PROC_XMLOBJECT_CHILDREN(PGPData,XMLConstants::XMLSIG_NS); + PROC_TYPED_CHILDREN(X509Data,XMLConstants::XMLSIG_NS,false); + PROC_TYPED_CHILDREN(KeyName,XMLConstants::XMLSIG_NS,false); + PROC_TYPED_CHILDREN(KeyValue,XMLConstants::XMLSIG_NS,false); + PROC_TYPED_CHILDREN(RetrievalMethod,XMLConstants::XMLSIG_NS,false); + PROC_TYPED_CHILDREN(MgmtData,XMLConstants::XMLSIG_NS,false); + PROC_TYPED_CHILDREN(SPKIData,XMLConstants::XMLSIG_NS,false); + PROC_TYPED_CHILDREN(PGPData,XMLConstants::XMLSIG_NS,false); // Unknown child. const XMLCh* nsURI=root->getNamespaceURI(); if (!XMLString::equals(nsURI,XMLConstants::XMLSIG_NS) && nsURI && *nsURI) - getXMLObjects().push_back(childXMLObject); + getOthers().push_back(childXMLObject); - throw UnmarshallingException("Invalid child element: $1",params(1,childXMLObject->getElementQName().toString().c_str())); + AbstractXMLObjectUnmarshaller::processChildElement(childXMLObject,root); } void processAttribute(const DOMAttr* attribute) { - PROC_XMLOBJECT_ID_ATTRIB(Id,ID,NULL); + PROC_ID_ATTRIB(Id,ID,NULL); } }; diff --git a/xmltooling/signature/impl/KeyInfoSchemaValidators.cpp b/xmltooling/signature/impl/KeyInfoSchemaValidators.cpp index e2dc57d..ec4dbaa 100644 --- a/xmltooling/signature/impl/KeyInfoSchemaValidators.cpp +++ b/xmltooling/signature/impl/KeyInfoSchemaValidators.cpp @@ -64,7 +64,7 @@ namespace xmlsignature { END_XMLOBJECTVALIDATOR; BEGIN_XMLOBJECTVALIDATOR(XMLTOOL_DLLLOCAL,KeyValue); - XMLOBJECTVALIDATOR_ONEOF3(KeyValue,DSAKeyValue,RSAKeyValue,XMLObject); + XMLOBJECTVALIDATOR_ONLYONEOF3(KeyValue,DSAKeyValue,RSAKeyValue,OtherKeyValue); END_XMLOBJECTVALIDATOR; BEGIN_XMLOBJECTVALIDATOR(XMLTOOL_DLLLOCAL,Transform); @@ -90,7 +90,7 @@ namespace xmlsignature { const XMLCh* ns=xmlObject->getElementQName().getNamespaceURI(); if (XMLString::equals(ns,XMLConstants::XMLSIG_NS) || !ns || !*ns) { throw ValidationException( - "X509Data contains an illegal extension element ($1).", + "Object contains an illegal extension child element ($1).", params(1,xmlObject->getElementQName().toString().c_str()) ); } @@ -100,7 +100,7 @@ namespace xmlsignature { BEGIN_XMLOBJECTVALIDATOR(XMLTOOL_DLLLOCAL,X509Data); if (!ptr->hasChildren()) throw ValidationException("X509Data must have at least one child element."); - vector anys=ptr->getXMLObjects(); + const vector& anys=ptr->getOtherX509Datas(); for_each(anys.begin(),anys.end(),checkWildcardNS()); END_XMLOBJECTVALIDATOR; @@ -115,7 +115,7 @@ namespace xmlsignature { BEGIN_XMLOBJECTVALIDATOR(XMLTOOL_DLLLOCAL,KeyInfo); if (!ptr->hasChildren()) throw ValidationException("KeyInfo must have at least one child element."); - vector anys=ptr->getXMLObjects(); + const vector& anys=ptr->getOthers(); for_each(anys.begin(),anys.end(),checkWildcardNS()); END_XMLOBJECTVALIDATOR; diff --git a/xmltooling/signature/impl/XMLSecSignatureImpl.cpp b/xmltooling/signature/impl/XMLSecSignatureImpl.cpp index bf169bb..4bd7653 100644 --- a/xmltooling/signature/impl/XMLSecSignatureImpl.cpp +++ b/xmltooling/signature/impl/XMLSecSignatureImpl.cpp @@ -57,6 +57,7 @@ namespace xmlsignature { void releaseDOM(); XMLObject* clone() const; + Signature* cloneSignature() const; DOMElement* marshall(DOMDocument* document=NULL, MarshallingContext* ctx=NULL) const; DOMElement* marshall(DOMElement* parentElement, MarshallingContext* ctx=NULL) const; @@ -109,6 +110,11 @@ void XMLSecSignatureImpl::releaseDOM() XMLObject* XMLSecSignatureImpl::clone() const { + return cloneSignature(); +} + +Signature* XMLSecSignatureImpl::cloneSignature() const +{ XMLSecSignatureImpl* ret=new XMLSecSignatureImpl(); ret->m_c14n=XMLString::replicate(m_c14n); diff --git a/xmltooling/util/DateTime.cpp b/xmltooling/util/DateTime.cpp new file mode 100644 index 0000000..7877c50 --- /dev/null +++ b/xmltooling/util/DateTime.cpp @@ -0,0 +1,1621 @@ +/* + * 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. + */ + +/** + * DateTime.cpp + * + * Manipulation of XML date/time data. + */ + +/* + * This is mostly copied from Xerces-C, but they don't seem inclined to produce a usable + * class, so I had to incorporate my own version of it for now. I can't inherit it + * since the fields I need are private. + */ + +#include "internal.h" +#include "util/DateTime.h" + +#ifndef WIN32 +# include +#endif + +#include +#include +#include + +using namespace xmltooling; +using namespace std; + +// +// constants used to process raw data (fBuffer) +// +// [-]{CCYY-MM-DD}'T'{HH:MM:SS.MS}['Z'] +// [{+|-}hh:mm'] +// + +static const XMLCh DURATION_STARTER = chLatin_P; // 'P' +static const XMLCh DURATION_Y = chLatin_Y; // 'Y' +static const XMLCh DURATION_M = chLatin_M; // 'M' +static const XMLCh DURATION_D = chLatin_D; // 'D' +static const XMLCh DURATION_H = chLatin_H; // 'H' +static const XMLCh DURATION_S = chLatin_S; // 'S' + +static const XMLCh DATE_SEPARATOR = chDash; // '-' +static const XMLCh TIME_SEPARATOR = chColon; // ':' +static const XMLCh TIMEZONE_SEPARATOR = chColon; // ':' +static const XMLCh DATETIME_SEPARATOR = chLatin_T; // 'T' +static const XMLCh MILISECOND_SEPARATOR = chPeriod; // '.' + +static const XMLCh UTC_STD_CHAR = chLatin_Z; // 'Z' +static const XMLCh UTC_POS_CHAR = chPlus; // '+' +static const XMLCh UTC_NEG_CHAR = chDash; // '-' + +static const XMLCh UTC_SET[] = {UTC_STD_CHAR //"Z+-" + , UTC_POS_CHAR + , UTC_NEG_CHAR + , chNull}; + +static const int YMD_MIN_SIZE = 10; // CCYY-MM-DD +static const int YMONTH_MIN_SIZE = 7; // CCYY_MM +static const int TIME_MIN_SIZE = 8; // hh:mm:ss +static const int TIMEZONE_SIZE = 5; // hh:mm +static const int DAY_SIZE = 5; // ---DD +//static const int MONTH_SIZE = 6; // --MM-- +static const int MONTHDAY_SIZE = 7; // --MM-DD +static const int NOT_FOUND = -1; + +//define constants to be used in assigning default values for +//all date/time excluding duration +static const int YEAR_DEFAULT = 2000; +static const int MONTH_DEFAULT = 01; +static const int DAY_DEFAULT = 15; + +// order-relation on duration is a partial order. The dates below are used to +// for comparison of 2 durations, based on the fact that +// duration x and y is x<=y iff s+x<=s+y +// see 3.2.6 duration W3C schema datatype specs +// +// the dates are in format: {CCYY,MM,DD, H, S, M, MS, timezone} +const int DateTime::DATETIMES[][TOTAL_SIZE] = +{ + {1696, 9, 1, 0, 0, 0, 0, UTC_STD}, + {1697, 2, 1, 0, 0, 0, 0, UTC_STD}, + {1903, 3, 1, 0, 0, 0, 0, UTC_STD}, + {1903, 7, 1, 0, 0, 0, 0, UTC_STD} +}; + +// --------------------------------------------------------------------------- +// local methods +// --------------------------------------------------------------------------- +static inline int fQuotient(int a, int b) +{ + div_t div_result = div(a, b); + return div_result.quot; +} + +static inline int fQuotient(int temp, int low, int high) +{ + return fQuotient(temp - low, high - low); +} + +static inline int mod(int a, int b, int quotient) +{ + return (a - quotient*b) ; +} + +static inline int modulo (int temp, int low, int high) +{ + //modulo(a - low, high - low) + low + int a = temp - low; + int b = high - low; + return (mod (a, b, fQuotient(a, b)) + low) ; +} + +static inline bool isLeapYear(int year) +{ + return((year%4 == 0) && ((year%100 != 0) || (year%400 == 0))); +} + +static int maxDayInMonthFor(int year, int month) +{ + + if ( month == 4 || month == 6 || month == 9 || month == 11 ) + { + return 30; + } + else if ( month==2 ) + { + if ( isLeapYear(year) ) + return 29; + else + return 28; + } + else + { + return 31; + } + +} + +// --------------------------------------------------------------------------- +// static methods : for duration +// --------------------------------------------------------------------------- +/** + * Compares 2 given durations. (refer to W3C Schema Datatypes "3.2.6 duration") + * + * 3.2.6.2 Order relation on duration + * + * In general, the order-relation on duration is a partial order since there is no + * determinate relationship between certain durations such as one month (P1M) and 30 days (P30D). + * The order-relation of two duration values x and y is x < y iff s+x < s+y for each qualified + * dateTime s in the list below. + * + * These values for s cause the greatest deviations in the addition of dateTimes and durations + * + **/ +int DateTime::compare(const DateTime* const pDate1 + , const DateTime* const pDate2 + , bool strict) +{ + //REVISIT: this is unoptimazed vs of comparing 2 durations + // Algorithm is described in 3.2.6.2 W3C Schema Datatype specs + // + + int resultA, resultB = XMLDateTime::INDETERMINATE; + + //try and see if the objects are equal + if ( (resultA = compareOrder(pDate1, pDate2)) == XMLDateTime::EQUAL) + return XMLDateTime::EQUAL; + + //long comparison algorithm is required + DateTime tempA, *pTempA = &tempA; + DateTime tempB, *pTempB = &tempB; + + addDuration(pTempA, pDate1, 0); + addDuration(pTempB, pDate2, 0); + resultA = compareOrder(pTempA, pTempB); + if ( resultA == XMLDateTime::INDETERMINATE ) + return XMLDateTime::INDETERMINATE; + + addDuration(pTempA, pDate1, 1); + addDuration(pTempB, pDate2, 1); + resultB = compareOrder(pTempA, pTempB); + resultA = compareResult(resultA, resultB, strict); + if ( resultA == XMLDateTime::INDETERMINATE ) + return XMLDateTime::INDETERMINATE; + + addDuration(pTempA, pDate1, 2); + addDuration(pTempB, pDate2, 2); + resultB = compareOrder(pTempA, pTempB); + resultA = compareResult(resultA, resultB, strict); + if ( resultA == XMLDateTime::INDETERMINATE ) + return XMLDateTime::INDETERMINATE; + + addDuration(pTempA, pDate1, 3); + addDuration(pTempB, pDate2, 3); + resultB = compareOrder(pTempA, pTempB); + resultA = compareResult(resultA, resultB, strict); + + return resultA; + +} + +// +// Form a new DateTime with duration and baseDate array +// Note: C++ Java +// fNewDate duration +// fDuration date +// + +void DateTime::addDuration(DateTime* fNewDate + , const DateTime* const fDuration + , int index) + +{ + + //REVISIT: some code could be shared between normalize() and this method, + // however is it worth moving it? The structures are different... + // + + fNewDate->reset(); + //add months (may be modified additionaly below) + int temp = DATETIMES[index][Month] + fDuration->fValue[Month]; + fNewDate->fValue[Month] = modulo(temp, 1, 13); + int carry = fQuotient(temp, 1, 13); + + //add years (may be modified additionaly below) + fNewDate->fValue[CentYear] = + DATETIMES[index][CentYear] + fDuration->fValue[CentYear] + carry; + + //add seconds + temp = DATETIMES[index][Second] + fDuration->fValue[Second]; + carry = fQuotient (temp, 60); + fNewDate->fValue[Second] = mod(temp, 60, carry); + + //add minutes + temp = DATETIMES[index][Minute] + fDuration->fValue[Minute] + carry; + carry = fQuotient(temp, 60); + fNewDate->fValue[Minute] = mod(temp, 60, carry); + + //add hours + temp = DATETIMES[index][Hour] + fDuration->fValue[Hour] + carry; + carry = fQuotient(temp, 24); + fNewDate->fValue[Hour] = mod(temp, 24, carry); + + fNewDate->fValue[Day] = + DATETIMES[index][Day] + fDuration->fValue[Day] + carry; + + while ( true ) + { + temp = maxDayInMonthFor(fNewDate->fValue[CentYear], fNewDate->fValue[Month]); + if ( fNewDate->fValue[Day] < 1 ) + { //original fNewDate was negative + fNewDate->fValue[Day] += + maxDayInMonthFor(fNewDate->fValue[CentYear], fNewDate->fValue[Month]-1); + carry = -1; + } + else if ( fNewDate->fValue[Day] > temp ) + { + fNewDate->fValue[Day] -= temp; + carry = 1; + } + else + { + break; + } + + temp = fNewDate->fValue[Month] + carry; + fNewDate->fValue[Month] = modulo(temp, 1, 13); + fNewDate->fValue[CentYear] += fQuotient(temp, 1, 13); + } + + //fNewDate->fValue[utc] = UTC_STD_CHAR; + fNewDate->fValue[utc] = UTC_STD; +} + +int DateTime::compareResult(int resultA + , int resultB + , bool strict) +{ + + if ( resultB == XMLDateTime::INDETERMINATE ) + { + return XMLDateTime::INDETERMINATE; + } + else if ( (resultA != resultB) && + strict ) + { + return XMLDateTime::INDETERMINATE; + } + else if ( (resultA != resultB) && + !strict ) + { + if ( (resultA != XMLDateTime::EQUAL) && + (resultB != XMLDateTime::EQUAL) ) + { + return XMLDateTime::INDETERMINATE; + } + else + { + return (resultA != XMLDateTime::EQUAL)? resultA : resultB; + } + } + + return resultA; + +} + +// --------------------------------------------------------------------------- +// static methods : for others +// --------------------------------------------------------------------------- +int DateTime::compare(const DateTime* const pDate1 + , const DateTime* const pDate2) +{ + + if (pDate1->fValue[utc] == pDate2->fValue[utc]) + { + return DateTime::compareOrder(pDate1, pDate2); + } + + int c1, c2; + + if ( pDate1->isNormalized()) + { + c1 = compareResult(pDate1, pDate2, false, UTC_POS); + c2 = compareResult(pDate1, pDate2, false, UTC_NEG); + return getRetVal(c1, c2); + } + else if ( pDate2->isNormalized()) + { + c1 = compareResult(pDate1, pDate2, true, UTC_POS); + c2 = compareResult(pDate1, pDate2, true, UTC_NEG); + return getRetVal(c1, c2); + } + + return XMLDateTime::INDETERMINATE; +} + +int DateTime::compareResult(const DateTime* const pDate1 + , const DateTime* const pDate2 + , bool set2Left + , int utc_type) +{ + DateTime tmpDate = (set2Left ? *pDate1 : *pDate2); + + tmpDate.fTimeZone[hh] = 14; + tmpDate.fTimeZone[mm] = 0; + tmpDate.fValue[utc] = utc_type; + tmpDate.normalize(); + + return (set2Left? DateTime::compareOrder(&tmpDate, pDate2) : + DateTime::compareOrder(pDate1, &tmpDate)); +} + +int DateTime::compareOrder(const DateTime* const lValue + , const DateTime* const rValue) + //, MemoryManager* const memMgr) +{ + // + // If any of the them is not normalized() yet, + // we need to do something here. + // + DateTime lTemp = *lValue; + DateTime rTemp = *rValue; + + lTemp.normalize(); + rTemp.normalize(); + + for ( int i = 0 ; i < TOTAL_SIZE; i++ ) + { + if ( lTemp.fValue[i] < rTemp.fValue[i] ) + { + return XMLDateTime::LESS_THAN; + } + else if ( lTemp.fValue[i] > rTemp.fValue[i] ) + { + return XMLDateTime::GREATER_THAN; + } + } + + if ( lTemp.fHasTime) + { + if ( lTemp.fMiliSecond < rTemp.fMiliSecond ) + { + return XMLDateTime::LESS_THAN; + } + else if ( lTemp.fMiliSecond > rTemp.fMiliSecond ) + { + return XMLDateTime::GREATER_THAN; + } + } + + return XMLDateTime::EQUAL; +} + +// --------------------------------------------------------------------------- +// ctor and dtor +// --------------------------------------------------------------------------- +DateTime::~DateTime() +{ + delete[] fBuffer; +} + +DateTime::DateTime() +: fStart(0) +, fEnd(0) +, fBufferMaxLen(0) +, fBuffer(0) +, fMiliSecond(0) +, fHasTime(false) +{ + reset(); +} + +DateTime::DateTime(const XMLCh* const aString) +: fStart(0) +, fEnd(0) +, fBufferMaxLen(0) +, fBuffer(0) +, fMiliSecond(0) +, fHasTime(false) +{ + setBuffer(aString); +} + +DateTime::DateTime(time_t epoch) +: fStart(0) +, fEnd(0) +, fBufferMaxLen(0) +, fBuffer(0) +, fMiliSecond(0) +, fHasTime(false) +{ +#ifndef HAVE_GMTIME_R + struct tm* ptime=gmtime(&epoch); +#else + struct tm res; + struct tm* ptime=gmtime_r(&epoch,&res); +#endif + char timebuf[32]; + strftime(timebuf,32,"%Y-%m-%dT%H:%M:%SZ",ptime); + auto_ptr_XMLCh timeptr(timebuf); + setBuffer(timeptr.get()); +} + +// ----------------------------------------------------------------------- +// Copy ctor and Assignment operators +// ----------------------------------------------------------------------- + +DateTime::DateTime(const DateTime &toCopy) +: fBufferMaxLen(0) +, fBuffer(0) +{ + copy(toCopy); +} + +DateTime& DateTime::operator=(const DateTime& rhs) +{ + if (this == &rhs) + return *this; + + copy(rhs); + return *this; +} + +// ----------------------------------------------------------------------- +// Implementation of Abstract Interface +// ----------------------------------------------------------------------- + +// +// We may simply return the handle to fBuffer +// +const XMLCh* DateTime::getRawData() const +{ + //assertBuffer(); + return fBuffer; +} + + +const XMLCh* DateTime::getFormattedString() const +{ + return getRawData(); +} + +int DateTime::getSign() const +{ + return 0; +} + +time_t DateTime::getEpoch() const +{ + struct tm t; + t.tm_sec=getSecond(); + t.tm_min=getMinute(); + t.tm_hour=getHour(); + t.tm_mday=getDay(); + t.tm_mon=getMonth()-1; + t.tm_year=getYear()-1900; + t.tm_isdst=0; +#if defined(HAVE_TIMEGM) + return timegm(&t); +#else + // Windows, and hopefully most others...? + return mktime(&t) - timezone; +#endif +} + +// --------------------------------------------------------------------------- +// Parsers +// --------------------------------------------------------------------------- + +// +// [-]{CCYY-MM-DD}'T'{HH:MM:SS.MS}[TimeZone] +// +void DateTime::parseDateTime() +{ + initParser(); + getDate(); + + //fStart is supposed to point to 'T' + if (fBuffer[fStart++] != DATETIME_SEPARATOR) + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_gDay_invalid + , fBuffer); + + getTime(); + validateDateTime(); + normalize(); + fHasTime = true; +} + +// +// [-]{CCYY-MM-DD}[TimeZone] +// +void DateTime::parseDate() +{ + initParser(); + getDate(); + parseTimeZone(); + validateDateTime(); + normalize(); +} + +void DateTime::parseTime() +{ + initParser(); + + // time initialize to default values + fValue[CentYear]= YEAR_DEFAULT; + fValue[Month] = MONTH_DEFAULT; + fValue[Day] = DAY_DEFAULT; + + getTime(); + + validateDateTime(); + normalize(); + fHasTime = true; +} + +// +// {---DD}[TimeZone] +// 01234 +// +void DateTime::parseDay() +{ + initParser(); + + if (fBuffer[0] != DATE_SEPARATOR || + fBuffer[1] != DATE_SEPARATOR || + fBuffer[2] != DATE_SEPARATOR ) + { + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_gDay_invalid + , fBuffer); + } + + //initialize values + fValue[CentYear] = YEAR_DEFAULT; + fValue[Month] = MONTH_DEFAULT; + fValue[Day] = parseInt(fStart+3, fStart+5); + + if ( DAY_SIZE < fEnd ) + { + int sign = findUTCSign(DAY_SIZE); + if ( sign < 0 ) + { + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_gDay_invalid + , fBuffer); + } + else + { + getTimeZone(sign); + } + } + + validateDateTime(); + normalize(); +} + +// +// {--MM--}[TimeZone] +// {--MM}[TimeZone] +// 012345 +// +void DateTime::parseMonth() +{ + initParser(); + + if (fBuffer[0] != DATE_SEPARATOR || + fBuffer[1] != DATE_SEPARATOR ) + { + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_gMth_invalid + , fBuffer); + } + + //set constants + fValue[CentYear] = YEAR_DEFAULT; + fValue[Day] = DAY_DEFAULT; + fValue[Month] = parseInt(2, 4); + + // REVISIT: allow both --MM and --MM-- now. + // need to remove the following lines to disallow --MM-- + // when the errata is officially in the rec. + fStart = 4; + if ( fEnd >= fStart+2 && fBuffer[fStart] == DATE_SEPARATOR && fBuffer[fStart+1] == DATE_SEPARATOR ) + { + fStart += 2; + } + + // + // parse TimeZone if any + // + if ( fStart < fEnd ) + { + int sign = findUTCSign(fStart); + if ( sign < 0 ) + { + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_gMth_invalid + , fBuffer); + } + else + { + getTimeZone(sign); + } + } + + validateDateTime(); + normalize(); +} + +// +//[-]{CCYY}[TimeZone] +// 0 1234 +// +void DateTime::parseYear() +{ + initParser(); + + // skip the first '-' and search for timezone + // + int sign = findUTCSign((fBuffer[0] == chDash) ? 1 : 0); + + if (sign == NOT_FOUND) + { + fValue[CentYear] = parseIntYear(fEnd); + } + else + { + fValue[CentYear] = parseIntYear(sign); + getTimeZone(sign); + } + + //initialize values + fValue[Month] = MONTH_DEFAULT; + fValue[Day] = DAY_DEFAULT; //java is 1 + + validateDateTime(); + normalize(); +} + +// +//{--MM-DD}[TimeZone] +// 0123456 +// +void DateTime::parseMonthDay() +{ + initParser(); + + if (fBuffer[0] != DATE_SEPARATOR || + fBuffer[1] != DATE_SEPARATOR || + fBuffer[4] != DATE_SEPARATOR ) + { + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_gMthDay_invalid + , fBuffer); + } + + + //initialize + fValue[CentYear] = YEAR_DEFAULT; + fValue[Month] = parseInt(2, 4); + fValue[Day] = parseInt(5, 7); + + if ( MONTHDAY_SIZE < fEnd ) + { + int sign = findUTCSign(MONTHDAY_SIZE); + if ( sign<0 ) + { + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_gMthDay_invalid + , fBuffer); + } + else + { + getTimeZone(sign); + } + } + + validateDateTime(); + normalize(); +} + +void DateTime::parseYearMonth() +{ + initParser(); + + // get date + getYearMonth(); + fValue[Day] = DAY_DEFAULT; + parseTimeZone(); + + validateDateTime(); + normalize(); +} + +// +//PnYn MnDTnH nMnS: -P1Y2M3DT10H30M +// +// [-]{'P'{[n'Y'][n'M'][n'D']['T'][n'H'][n'M'][n'S']}} +// +// Note: the n above shall be >= 0 +// if no time element found, 'T' shall be absent +// +void DateTime::parseDuration() +{ + initParser(); + + // must start with '-' or 'P' + // + XMLCh c = fBuffer[fStart++]; + if ( (c != DURATION_STARTER) && + (c != chDash) ) + { + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_dur_Start_dashP + , fBuffer); + } + + // 'P' must ALWAYS be present in either case + if ( (c == chDash) && + (fBuffer[fStart++]!= DURATION_STARTER )) + { + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_dur_noP + , fBuffer); + } + + // java code + //date[utc]=(c=='-')?'-':0; + //fValue[utc] = UTC_STD; + fValue[utc] = (fBuffer[0] == chDash? UTC_NEG : UTC_STD); + + int negate = ( fBuffer[0] == chDash ? -1 : 1); + + // + // No negative value is allowed after 'P' + // + // eg P-1234, invalid + // + if (indexOf(fStart, fEnd, chDash) != NOT_FOUND) + { + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_dur_DashNotFirst + , fBuffer); + } + + //at least one number and designator must be seen after P + bool designator = false; + + int endDate = indexOf(fStart, fEnd, DATETIME_SEPARATOR); + if ( endDate == NOT_FOUND ) + { + endDate = fEnd; // 'T' absent + } + + //find 'Y' + int end = indexOf(fStart, endDate, DURATION_Y); + if ( end != NOT_FOUND ) + { + //scan year + fValue[CentYear] = negate * parseInt(fStart, end); + fStart = end+1; + designator = true; + } + + end = indexOf(fStart, endDate, DURATION_M); + if ( end != NOT_FOUND ) + { + //scan month + fValue[Month] = negate * parseInt(fStart, end); + fStart = end+1; + designator = true; + } + + end = indexOf(fStart, endDate, DURATION_D); + if ( end != NOT_FOUND ) + { + //scan day + fValue[Day] = negate * parseInt(fStart,end); + fStart = end+1; + designator = true; + } + + if ( (fEnd == endDate) && // 'T' absent + (fStart != fEnd) ) // something after Day + { + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_dur_inv_b4T + , fBuffer); + } + + if ( fEnd != endDate ) // 'T' present + { + //scan hours, minutes, seconds + // + + // skip 'T' first + end = indexOf(++fStart, fEnd, DURATION_H); + if ( end != NOT_FOUND ) + { + //scan hours + fValue[Hour] = negate * parseInt(fStart, end); + fStart = end+1; + designator = true; + } + + end = indexOf(fStart, fEnd, DURATION_M); + if ( end != NOT_FOUND ) + { + //scan min + fValue[Minute] = negate * parseInt(fStart, end); + fStart = end+1; + designator = true; + } + + end = indexOf(fStart, fEnd, DURATION_S); + if ( end != NOT_FOUND ) + { + //scan seconds + int mlsec = indexOf (fStart, end, MILISECOND_SEPARATOR); + + /*** + * Schema Errata: E2-23 + * at least one digit must follow the decimal point if it appears. + * That is, the value of the seconds component must conform + * to the following pattern: [0-9]+(.[0-9]+)? + */ + if ( mlsec != NOT_FOUND ) + { + /*** + * make usure there is something after the '.' and before the end. + */ + if ( mlsec+1 == end ) + { + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_dur_inv_seconds + , fBuffer); + } + + fValue[Second] = negate * parseInt(fStart, mlsec); + fMiliSecond = negate * parseMiliSecond(mlsec+1, end); + } + else + { + fValue[Second] = negate * parseInt(fStart,end); + } + + fStart = end+1; + designator = true; + } + + // no additional data should appear after last item + // P1Y1M1DT is illigal value as well + if ( (fStart != fEnd) || + fBuffer[--fStart] == DATETIME_SEPARATOR ) + { + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_dur_NoTimeAfterT + , fBuffer); + } + } + + if ( !designator ) + { + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_dur_NoElementAtAll + , fBuffer); + } + +} + +// --------------------------------------------------------------------------- +// Scanners +// --------------------------------------------------------------------------- + +// +// [-]{CCYY-MM-DD} +// +// Note: CCYY could be more than 4 digits +// Assuming fStart point to the beginning of the Date Section +// fStart updated to point to the position right AFTER the second 'D' +// Since the lenght of CCYY might be variable, we can't check format upfront +// +void DateTime::getDate() +{ + + // Ensure enough chars in buffer + if ( (fStart+YMD_MIN_SIZE) > fEnd) + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_date_incomplete + , fBuffer); + + getYearMonth(); // Scan YearMonth and + // fStart point to the next '-' + + if (fBuffer[fStart++] != DATE_SEPARATOR) + { + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_date_invalid + , fBuffer); + //("CCYY-MM must be followed by '-' sign"); + } + + fValue[Day] = parseInt(fStart, fStart+2); + fStart += 2 ; //fStart points right after the Day + + return; +} + +// +// hh:mm:ss[.msssss]['Z'] +// hh:mm:ss[.msssss][['+'|'-']hh:mm] +// 012345678 +// +// Note: Assuming fStart point to the beginning of the Time Section +// fStart updated to point to the position right AFTER the second 's' +// or ms if any +// +void DateTime::getTime() +{ + + // Ensure enough chars in buffer + if ( (fStart+TIME_MIN_SIZE) > fEnd) + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_time_incomplete + , fBuffer); + //"Imcomplete Time Format" + + // check (fixed) format first + if ((fBuffer[fStart + 2] != TIME_SEPARATOR) || + (fBuffer[fStart + 5] != TIME_SEPARATOR) ) + { + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_time_invalid + , fBuffer); + //("Error in parsing time" ); + } + + // + // get hours, minute and second + // + fValue[Hour] = parseInt(fStart + 0, fStart + 2); + fValue[Minute] = parseInt(fStart + 3, fStart + 5); + fValue[Second] = parseInt(fStart + 6, fStart + 8); + fStart += 8; + + // to see if any ms and/or utc part after that + if (fStart >= fEnd) + return; + + //find UTC sign if any + int sign = findUTCSign(fStart); + + //parse miliseconds + int milisec = (fBuffer[fStart] == MILISECOND_SEPARATOR)? fStart : NOT_FOUND; + if ( milisec != NOT_FOUND ) + { + fStart++; // skip the '.' + // make sure we have some thing between the '.' and fEnd + if (fStart >= fEnd) + { + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_ms_noDigit + , fBuffer); + //("ms shall be present once '.' is present" ); + } + + if ( sign == NOT_FOUND ) + { + fMiliSecond = parseMiliSecond(fStart, fEnd); //get ms between '.' and fEnd + fStart = fEnd; + } + else + { + fMiliSecond = parseMiliSecond(fStart, sign); //get ms between UTC sign and fEnd + } + } + else if(sign == 0 || sign != fStart) + { + // seconds has more than 2 digits + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_min_invalid + , fBuffer); + } + + //parse UTC time zone (hh:mm) + if ( sign > 0 ) { + getTimeZone(sign); + } + +} + +// +// [-]{CCYY-MM} +// +// Note: CCYY could be more than 4 digits +// fStart updated to point AFTER the second 'M' (probably meet the fEnd) +// +void DateTime::getYearMonth() +{ + + // Ensure enough chars in buffer + if ( (fStart+YMONTH_MIN_SIZE) > fEnd) + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_ym_incomplete + , fBuffer); + //"Imcomplete YearMonth Format"; + + // skip the first leading '-' + int start = ( fBuffer[0] == chDash ) ? fStart + 1 : fStart; + + // + // search for year separator '-' + // + int yearSeparator = indexOf(start, fEnd, DATE_SEPARATOR); + if ( yearSeparator == NOT_FOUND) + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_ym_invalid + , fBuffer); + //("Year separator is missing or misplaced"); + + fValue[CentYear] = parseIntYear(yearSeparator); + fStart = yearSeparator + 1; //skip the '-' and point to the first M + + // + //gonna check we have enough byte for month + // + if ((fStart + 2) > fEnd ) + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_ym_noMonth + , fBuffer); + //"no month in buffer" + + fValue[Month] = parseInt(fStart, yearSeparator + 3); + fStart += 2; //fStart points right after the MONTH + + return; +} + +void DateTime::parseTimeZone() +{ + if ( fStart < fEnd ) + { + int sign = findUTCSign(fStart); + if ( sign < 0 ) + { + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_tz_noUTCsign + , fBuffer); + //("Error in month parsing"); + } + else + { + getTimeZone(sign); + } + } + + return; +} + +// +// 'Z' +// ['+'|'-']hh:mm +// +// Note: Assuming fStart points to the beginning of TimeZone section +// fStart updated to meet fEnd +// +void DateTime::getTimeZone(const int sign) +{ + + if ( fBuffer[sign] == UTC_STD_CHAR ) + { + if ((sign + 1) != fEnd ) + { + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_tz_stuffAfterZ + , fBuffer); + //"Error in parsing time zone"); + } + + return; + } + + // + // otherwise, it has to be this format + // '[+|-]'hh:mm + // 1 23456 7 + // sign fEnd + // + if ( ( ( sign + TIMEZONE_SIZE + 1) != fEnd ) || + ( fBuffer[sign + 3] != TIMEZONE_SEPARATOR ) ) + { + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_tz_invalid + , fBuffer); + //("Error in parsing time zone"); + } + + fTimeZone[hh] = parseInt(sign+1, sign+3); + fTimeZone[mm] = parseInt(sign+4, fEnd); + + return; +} + +// --------------------------------------------------------------------------- +// Validator and normalizer +// --------------------------------------------------------------------------- + +/** + * If timezone present - normalize dateTime [E Adding durations to dateTimes] + * + * @param date CCYY-MM-DDThh:mm:ss+03 + * @return CCYY-MM-DDThh:mm:ssZ + */ +void DateTime::normalize() +{ + + if ((fValue[utc] == UTC_UNKNOWN) || + (fValue[utc] == UTC_STD) ) + return; + + int negate = (fValue[utc] == UTC_POS)? -1: 1; + + // add mins + int temp = fValue[Minute] + negate * fTimeZone[mm]; + int carry = fQuotient(temp, 60); + fValue[Minute] = mod(temp, 60, carry); + + //add hours + temp = fValue[Hour] + negate * fTimeZone[hh] + carry; + carry = fQuotient(temp, 24); + fValue[Hour] = mod(temp, 24, carry); + + fValue[Day] += carry; + + while (1) + { + temp = maxDayInMonthFor(fValue[CentYear], fValue[Month]); + if (fValue[Day] < 1) + { + fValue[Day] += maxDayInMonthFor(fValue[CentYear], fValue[Month] - 1); + carry = -1; + } + else if ( fValue[Day] > temp ) + { + fValue[Day] -= temp; + carry = 1; + } + else + { + break; + } + + temp = fValue[Month] + carry; + fValue[Month] = modulo(temp, 1, 13); + fValue[CentYear] += fQuotient(temp, 1, 13); + } + + // set to normalized + fValue[utc] = UTC_STD; + + return; +} + +void DateTime::validateDateTime() const +{ + + //REVISIT: should we throw an exception for not valid dates + // or reporting an error message should be sufficient? + if ( fValue[CentYear] == 0 ) + { + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_year_zero + , fBuffer); + //"The year \"0000\" is an illegal year value"); + } + + if ( fValue[Month] < 1 || + fValue[Month] > 12 ) + { + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_mth_invalid + , fBuffer); + //"The month must have values 1 to 12"); + } + + //validate days + if ( fValue[Day] > maxDayInMonthFor( fValue[CentYear], fValue[Month]) || + fValue[Day] == 0 ) + { + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_day_invalid + , fBuffer); + //"The day must have values 1 to 31"); + } + + //validate hours + if ((fValue[Hour] < 0) || + (fValue[Hour] > 24) || + ((fValue[Hour] == 24) && ((fValue[Minute] !=0) || + (fValue[Second] !=0) || + (fMiliSecond !=0)))) + { + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_hour_invalid + , fBuffer); + //("Hour must have values 0-23"); + } + + //validate minutes + if ( fValue[Minute] < 0 || + fValue[Minute] > 59 ) + { + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_min_invalid + , fBuffer); + //"Minute must have values 0-59"); + } + + //validate seconds + if ( fValue[Second] < 0 || + fValue[Second] > 60 ) + { + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_second_invalid + , fBuffer); + //"Second must have values 0-60"); + } + + //validate time-zone hours + if ( (abs(fTimeZone[hh]) > 14) || + ((abs(fTimeZone[hh]) == 14) && (fTimeZone[mm] != 0)) ) + { + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_tz_hh_invalid + , fBuffer); + //"Time zone should have range -14..+14"); + } + + //validate time-zone minutes + if ( abs(fTimeZone[mm]) > 59 ) + { + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_min_invalid + , fBuffer); + //("Minute must have values 0-59"); + } + + return; +} + +// ----------------------------------------------------------------------- +// locator and converter +// ----------------------------------------------------------------------- +int DateTime::indexOf(const int start, const int end, const XMLCh ch) const +{ + for ( int i = start; i < end; i++ ) + if ( fBuffer[i] == ch ) + return i; + + return NOT_FOUND; +} + +int DateTime::findUTCSign (const int start) +{ + int pos; + for ( int index = start; index < fEnd; index++ ) + { + pos = XMLString::indexOf(UTC_SET, fBuffer[index]); + if ( pos != NOT_FOUND) + { + fValue[utc] = pos+1; // refer to utcType, there is 1 diff + return index; + } + } + + return NOT_FOUND; +} + +// +// Note: +// start: starting point in fBuffer +// end: ending point in fBuffer (exclusive) +// fStart NOT updated +// +int DateTime::parseInt(const int start, const int end) const +{ + unsigned int retVal = 0; + for (int i=start; i < end; i++) { + + if (fBuffer[i] < chDigit_0 || fBuffer[i] > chDigit_9) + ThrowXML(NumberFormatException, XMLExcepts::XMLNUM_Inv_chars); + + retVal = (retVal * 10) + (unsigned int) (fBuffer[i] - chDigit_0); + } + + return (int) retVal; +} + +// +// Note: +// start: pointing to the first digit after the '.' +// end: pointing to one position after the last digit +// fStart NOT updated +// +double DateTime::parseMiliSecond(const int start, const int end) const +{ + + unsigned int miliSecLen = (end-1) - (start-1) + 1; //to include the '.' + XMLCh* miliSecData = new XMLCh[miliSecLen + 1]; + ArrayJanitor janMili(miliSecData); + XMLString::copyNString(miliSecData, &(fBuffer[start-1]), miliSecLen); + *(miliSecData + miliSecLen) = chNull; + + char *nptr = XMLString::transcode(miliSecData); + ArrayJanitor jan(nptr); + size_t strLen = strlen(nptr); + char *endptr = 0; + errno = 0; + + //printf("milisec=<%s>\n", nptr); + + double retVal = strtod(nptr, &endptr); + + // check if all chars are valid char + if ( (endptr - nptr) != strLen) + ThrowXML(NumberFormatException, XMLExcepts::XMLNUM_Inv_chars); + + // we don't check underflow occurs since + // nothing we can do about it. + return retVal; +} + +// +// [-]CCYY +// +// Note: start from fStart +// end (exclusive) +// fStart NOT updated +// +int DateTime::parseIntYear(const int end) const +{ + // skip the first leading '-' + int start = ( fBuffer[0] == chDash ) ? fStart + 1 : fStart; + + int length = end - start; + if (length < 4) + { + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_year_tooShort + , fBuffer); + //"Year must have 'CCYY' format"); + } + else if (length > 4 && + fBuffer[start] == chDigit_0) + { + ThrowXML1(SchemaDateTimeException + , XMLExcepts::DateTime_year_leadingZero + , fBuffer); + //"Leading zeros are required if the year value would otherwise have fewer than four digits; + // otherwise they are forbidden"); + } + + bool negative = (fBuffer[0] == chDash); + int yearVal = parseInt((negative ? 1 : 0), end); + return ( negative ? (-1) * yearVal : yearVal ); +} + +/*** + * E2-41 + * + * 3.2.7.2 Canonical representation + * + * Except for trailing fractional zero digits in the seconds representation, + * '24:00:00' time representations, and timezone (for timezoned values), + * the mapping from literals to values is one-to-one. Where there is more + * than one possible representation, the canonical representation is as follows: + * redundant trailing zero digits in fractional-second literals are prohibited. + * An hour representation of '24' is prohibited. Timezoned values are canonically + * represented by appending 'Z' to the nontimezoned representation. (All + * timezoned dateTime values are UTC.) + * + * .'24:00:00' -> '00:00:00' + * .milisecond: trailing zeros removed + * .'Z' + * + ***/ +XMLCh* DateTime::getDateTimeCanonicalRepresentation() const +{ + XMLCh *miliStartPtr, *miliEndPtr; + searchMiliSeconds(miliStartPtr, miliEndPtr); + size_t miliSecondsLen = miliEndPtr - miliStartPtr; + + XMLCh* retBuf = new XMLCh[21 + miliSecondsLen + 2]; + XMLCh* retPtr = retBuf; + + // (-?) cc+yy-mm-dd'T'hh:mm:ss'Z' ('.'s+)? + // 2+ 8 1 8 1 + // + int additionalLen = fillYearString(retPtr, CentYear); + if(additionalLen != 0) + { + // very bad luck; have to resize the buffer... + XMLCh *tmpBuf = new XMLCh[additionalLen+21+miliSecondsLen +2]; + XMLString::moveChars(tmpBuf, retBuf, 4+additionalLen); + retPtr = tmpBuf+(retPtr-retBuf); + delete[] retBuf; + retBuf = tmpBuf; + } + *retPtr++ = DATE_SEPARATOR; + fillString(retPtr, Month, 2); + *retPtr++ = DATE_SEPARATOR; + fillString(retPtr, Day, 2); + *retPtr++ = DATETIME_SEPARATOR; + + fillString(retPtr, Hour, 2); + if (fValue[Hour] == 24) + { + *(retPtr - 2) = chDigit_0; + *(retPtr - 1) = chDigit_0; + } + *retPtr++ = TIME_SEPARATOR; + fillString(retPtr, Minute, 2); + *retPtr++ = TIME_SEPARATOR; + fillString(retPtr, Second, 2); + + if (miliSecondsLen) + { + *retPtr++ = chPeriod; + XMLString::copyNString(retPtr, miliStartPtr, miliSecondsLen); + retPtr += miliSecondsLen; + } + + *retPtr++ = UTC_STD_CHAR; + *retPtr = chNull; + + return retBuf; +} + +/*** + * 3.2.8 time + * + * . either the time zone must be omitted or, + * if present, the time zone must be Coordinated Universal Time (UTC) indicated by a "Z". + * + * . Additionally, the canonical representation for midnight is 00:00:00. + * +***/ +XMLCh* DateTime::getTimeCanonicalRepresentation() const +{ + XMLCh *miliStartPtr, *miliEndPtr; + searchMiliSeconds(miliStartPtr, miliEndPtr); + size_t miliSecondsLen = miliEndPtr - miliStartPtr; + + XMLCh* retBuf = new XMLCh[10 + miliSecondsLen + 2]; + XMLCh* retPtr = retBuf; + + // 'hh:mm:ss'Z' ('.'s+)? + // 8 1 + // + + fillString(retPtr, Hour, 2); + if (fValue[Hour] == 24) + { + *(retPtr - 2) = chDigit_0; + *(retPtr - 1) = chDigit_0; + } + *retPtr++ = TIME_SEPARATOR; + fillString(retPtr, Minute, 2); + *retPtr++ = TIME_SEPARATOR; + fillString(retPtr, Second, 2); + + if (miliSecondsLen) + { + *retPtr++ = chPeriod; + XMLString::copyNString(retPtr, miliStartPtr, miliSecondsLen); + retPtr += miliSecondsLen; + } + + *retPtr++ = UTC_STD_CHAR; + *retPtr = chNull; + + return retBuf; +} + +void DateTime::fillString(XMLCh*& ptr, valueIndex ind, int expLen) const +{ + XMLCh strBuffer[16]; + assert(expLen < 16); + XMLString::binToText(fValue[ind], strBuffer, expLen, 10); + int actualLen = XMLString::stringLen(strBuffer); + int i; + //append leading zeros + for (i = 0; i < expLen - actualLen; i++) + { + *ptr++ = chDigit_0; + } + + for (i = 0; i < actualLen; i++) + { + *ptr++ = strBuffer[i]; + } + +} + +int DateTime::fillYearString(XMLCh*& ptr, valueIndex ind) const +{ + XMLCh strBuffer[16]; + // let's hope we get no years of 15 digits... + XMLString::binToText(fValue[ind], strBuffer, 15, 10); + int actualLen = XMLString::stringLen(strBuffer); + // don't forget that years can be negative... + int negativeYear = 0; + if(strBuffer[0] == chDash) + { + *ptr++ = strBuffer[0]; + negativeYear = 1; + } + int i; + //append leading zeros + for (i = 0; i < 4 - actualLen+negativeYear; i++) + { + *ptr++ = chDigit_0; + } + + for (i = negativeYear; i < actualLen; i++) + { + *ptr++ = strBuffer[i]; + } + if(actualLen > 4) + return actualLen-4; + return 0; +} + +/*** + * + * .check if the rawData has the mili second component + * .capture the substring + * + ***/ +void DateTime::searchMiliSeconds(XMLCh*& miliStartPtr, XMLCh*& miliEndPtr) const +{ + miliStartPtr = miliEndPtr = 0; + + int milisec = XMLString::indexOf(fBuffer, MILISECOND_SEPARATOR); + if (milisec == -1) + return; + + miliStartPtr = fBuffer + milisec + 1; + miliEndPtr = miliStartPtr; + while (*miliEndPtr) + { + if ((*miliEndPtr < chDigit_0) || (*miliEndPtr > chDigit_9)) + break; + + miliEndPtr++; + } + + //remove trailing zeros + while( *(miliEndPtr - 1) == chDigit_0) + miliEndPtr--; + + return; +} + diff --git a/xmltooling/util/DateTime.h b/xmltooling/util/DateTime.h new file mode 100644 index 0000000..aceaf22 --- /dev/null +++ b/xmltooling/util/DateTime.h @@ -0,0 +1,233 @@ +/* + * Copyright 2001-2006 Internet2 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file DateTime.h + * + * Manipulation of XML date/time data. + */ + +#ifndef _XML_DATETIME_H +#define _XML_DATETIME_H + +#include + +#if defined (_MSC_VER) + #pragma warning( push ) + #pragma warning( disable : 4244 ) +#endif + +#include + +#if defined (_MSC_VER) + #pragma warning( pop ) +#endif + +namespace xmltooling +{ + /** + * Class for manipulating XML date/time information. + * + * This is mostly copied from Xerces-C, but they haven't produced a usable date/time + * class, so we had to incorporate a version of it for now. It can't be inherited + * since the fields needed are private. + */ + class XMLTOOL_API DateTime + { + public: + /// @cond OFF + DateTime(); + DateTime(const XMLCh* const); + DateTime(time_t epoch); + DateTime(const DateTime&); + DateTime& operator=(const DateTime&); + ~DateTime(); + + inline void setBuffer(const XMLCh* const); + + const XMLCh* getRawData() const; + const XMLCh* getFormattedString() const; + int getSign() const; + + XMLCh* getDateTimeCanonicalRepresentation() const; + XMLCh* getTimeCanonicalRepresentation() const; + + void parseDateTime(); + void parseDate(); + void parseTime(); + void parseDay(); + void parseMonth(); + void parseYear(); + void parseMonthDay(); + void parseYearMonth(); + void parseDuration(); + + static int compare(const DateTime* const, const DateTime* const); + static int compare(const DateTime* const, const DateTime* const, bool); + static int compareOrder(const DateTime* const, const DateTime* const); + + int getYear() const {return fValue[CentYear];} + int getMonth() const {return fValue[Month];} + int getDay() const {return fValue[Day];} + int getHour() const {return fValue[Hour];} + int getMinute() const {return fValue[Minute];} + int getSecond() const {return fValue[Second];} + time_t getEpoch() const; + + /// @endcond + private: + enum valueIndex { + CentYear = 0, + Month , + Day , + Hour , + Minute , + Second , + MiliSecond , //not to be used directly + utc , + TOTAL_SIZE + }; + + enum utcType { + UTC_UNKNOWN = 0, + UTC_STD , // set in parse() or normalize() + UTC_POS , // set in parse() + UTC_NEG // set in parse() + }; + + enum timezoneIndex { + hh = 0, + mm , + TIMEZONE_ARRAYSIZE + }; + + static int compareResult(int, int, bool); + static void addDuration(DateTime* pDuration, const DateTime* const pBaseDate, int index); + static int compareResult(const DateTime* const, const DateTime* const, bool, int); + static inline int getRetVal(int, int); + + inline void reset(); + //inline void assertBuffer() const; + inline void copy(const DateTime&); + + inline void initParser(); + inline bool isNormalized() const; + + void getDate(); + void getTime(); + void getYearMonth(); + void getTimeZone(const int); + void parseTimeZone(); + + int findUTCSign(const int start); + int indexOf(const int start, const int end, const XMLCh ch) const; + int parseInt(const int start, const int end) const; + int parseIntYear(const int end) const; + double parseMiliSecond(const int start, const int end) const; + + void validateDateTime() const; + void normalize(); + void fillString(XMLCh*& ptr, valueIndex ind, int expLen) const; + int fillYearString(XMLCh*& ptr, valueIndex ind) const; + void searchMiliSeconds(XMLCh*& miliStartPtr, XMLCh*& miliEndPtr) const; + + bool operator==(const DateTime& toCompare) const; + + static const int DATETIMES[][TOTAL_SIZE]; + int fValue[TOTAL_SIZE]; + int fTimeZone[TIMEZONE_ARRAYSIZE]; + int fStart; + int fEnd; + int fBufferMaxLen; + XMLCh* fBuffer; + + double fMiliSecond; + bool fHasTime; + }; + + inline void DateTime::setBuffer(const XMLCh* const aString) + { + reset(); + fEnd = XMLString::stringLen(aString); + if (fEnd > 0) { + if (fEnd > fBufferMaxLen) { + delete[] fBuffer; + fBufferMaxLen = fEnd + 8; + fBuffer = new XMLCh[fBufferMaxLen+1]; + } + memcpy(fBuffer, aString, (fEnd+1) * sizeof(XMLCh)); + } + } + + inline void DateTime::reset() + { + for ( int i=0; i < XMLDateTime::TOTAL_SIZE; i++ ) + fValue[i] = 0; + + fMiliSecond = 0; + fHasTime = false; + fTimeZone[hh] = fTimeZone[mm] = 0; + fStart = fEnd = 0; + + if (fBuffer) + *fBuffer = 0; + } + + inline void DateTime::copy(const DateTime& rhs) + { + for ( int i = 0; i < XMLDateTime::TOTAL_SIZE; i++ ) + fValue[i] = rhs.fValue[i]; + + fMiliSecond = rhs.fMiliSecond; + fHasTime = rhs.fHasTime; + fTimeZone[hh] = rhs.fTimeZone[hh]; + fTimeZone[mm] = rhs.fTimeZone[mm]; + fStart = rhs.fStart; + fEnd = rhs.fEnd; + + if (fEnd > 0) { + if (fEnd > fBufferMaxLen) { + delete[] fBuffer; + fBufferMaxLen = rhs.fBufferMaxLen; + fBuffer = new XMLCh[fBufferMaxLen+1]; + } + memcpy(fBuffer, rhs.fBuffer, (fEnd+1) * sizeof(XMLCh)); + } + } + + inline void DateTime::initParser() + { + fStart = 0; // to ensure scan from the very first beginning + // in case the pointer is updated accidentally by someone else. + } + + inline bool DateTime::isNormalized() const + { + return (fValue[XMLDateTime::utc] == XMLDateTime::UTC_STD ? true : false); + } + + inline int DateTime::getRetVal(int c1, int c2) + { + if ((c1 == XMLDateTime::LESS_THAN && c2 == XMLDateTime::GREATER_THAN) || + (c1 == XMLDateTime::GREATER_THAN && c2 == XMLDateTime::LESS_THAN)) + return XMLDateTime::INDETERMINATE; + + return (c1 != XMLDateTime::INDETERMINATE) ? c1 : c2; + } + +} + +#endif diff --git a/xmltooling/xmltooling.vcproj b/xmltooling/xmltooling.vcproj index 3b729cc..525480e 100644 --- a/xmltooling/xmltooling.vcproj +++ b/xmltooling/xmltooling.vcproj @@ -229,6 +229,10 @@ Name="util" > + + @@ -403,6 +407,10 @@ Name="util" > + + -- 2.1.4