From 5ae1c899bd915741ccfc63465708b5395a09be72 Mon Sep 17 00:00:00 2001 From: Scott Cantor Date: Fri, 3 Mar 2006 23:59:59 +0000 Subject: [PATCH] Began to implement collection handling. --- xmltooling/AbstractDOMCachingXMLObject.cpp | 64 ++++- xmltooling/AbstractDOMCachingXMLObject.h | 32 ++- xmltooling/AbstractXMLObject.h | 24 +- xmltooling/XMLObject.h | 13 +- xmltooling/impl/UnknownElement.h | 14 - xmltooling/io/AbstractXMLObjectMarshaller.cpp | 8 +- xmltooling/util/XMLObjectChildrenList.h | 284 +++++++++++++++++++++ xmltooling/xmltooling.vcproj | 4 + xmltoolingtest/MarshallingTest.h | 13 +- xmltoolingtest/UnmarshallingTest.h | 3 +- xmltoolingtest/XMLObjectBaseTestCase.h | 23 +- .../data/SimpleXMLObjectWithChildren.xml | Bin 344 -> 408 bytes xmltoolingtest/xmltoolingtest.vcproj | 12 +- 13 files changed, 428 insertions(+), 66 deletions(-) create mode 100644 xmltooling/util/XMLObjectChildrenList.h diff --git a/xmltooling/AbstractDOMCachingXMLObject.cpp b/xmltooling/AbstractDOMCachingXMLObject.cpp index 750e3f9..c1cd6e2 100644 --- a/xmltooling/AbstractDOMCachingXMLObject.cpp +++ b/xmltooling/AbstractDOMCachingXMLObject.cpp @@ -23,6 +23,8 @@ #include "internal.h" #include "exceptions.h" #include "AbstractDOMCachingXMLObject.h" +#include "io/Unmarshaller.h" +#include "util/XMLHelper.h" #include #include @@ -50,23 +52,27 @@ void AbstractDOMCachingXMLObject::setDOM(DOMElement* dom, bool bindDocument) void AbstractDOMCachingXMLObject::releaseDOM() { - Category& log=Category::getInstance(XMLTOOLING_LOGCAT".DOM"); - if (log.isDebugEnabled()) { - string qname=getElementQName().toString(); - log.debug("releasing cached DOM representation for (%s)", qname.empty() ? "unknown" : qname.c_str()); + if (m_dom) { + Category& log=Category::getInstance(XMLTOOLING_LOGCAT".DOM"); + if (log.isDebugEnabled()) { + string qname=getElementQName().toString(); + log.debug("releasing cached DOM representation for (%s)", qname.empty() ? "unknown" : qname.c_str()); + } + setDOM(NULL); } - setDOM(NULL); } void AbstractDOMCachingXMLObject::releaseParentDOM(bool propagateRelease) { DOMCachingXMLObject* domCachingParent = dynamic_cast(getParent()); if (domCachingParent) { - Category::getInstance(XMLTOOLING_LOGCAT".DOM").debug( - "releasing cached DOM representation for parent object with propagation set to %s", - propagateRelease ? "true" : "false" - ); - domCachingParent->releaseDOM(); + if (domCachingParent->getDOM()) { + Category::getInstance(XMLTOOLING_LOGCAT".DOM").debug( + "releasing cached DOM representation for parent object with propagation set to %s", + propagateRelease ? "true" : "false" + ); + domCachingParent->releaseDOM(); + } if (propagateRelease) domCachingParent->releaseParentDOM(propagateRelease); } @@ -86,13 +92,12 @@ public: void AbstractDOMCachingXMLObject::releaseChildrenDOM(bool propagateRelease) { - vector children; - if (getOrderedChildren(children)) { + if (hasChildren()) { Category::getInstance(XMLTOOLING_LOGCAT".DOM").debug( "releasing cached DOM representation for children with propagation set to %s", propagateRelease ? "true" : "false" ); - for_each(children.begin(),children.end(),bind2nd(_release(),propagateRelease)); + for_each(m_children.begin(),m_children.end(),bind2nd(_release(),propagateRelease)); } } @@ -120,3 +125,36 @@ XMLObject* AbstractDOMCachingXMLObject::prepareForAssignment(const XMLObject* ol return newValue; } + +DOMElement* AbstractDOMCachingXMLObject::cloneDOM(DOMDocument* doc) const +{ + if (getDOM()) { + if (!doc) + doc=DOMImplementationRegistry::getDOMImplementation(NULL)->createDocument(); + return static_cast(doc->importNode(getDOM(),true)); + } + return NULL; +} + +XMLObject* AbstractDOMCachingXMLObject::clone() const +{ + // See if we can clone via the DOM. + DOMElement* domCopy=cloneDOM(); + if (domCopy) { + // Seemed to work, so now we unmarshall the DOM to produce the clone. + const Unmarshaller* u=Unmarshaller::getUnmarshaller(domCopy); + if (!u) { + auto_ptr q(XMLHelper::getNodeQName(domCopy)); + Category::getInstance(XMLTOOLING_LOGCAT".DOM").error( + "DOM clone failed, unable to locate unmarshaller for element (%s)", q->toString().c_str() + ); + } + try { + return u->unmarshall(domCopy, true); // bind document + } + catch (...) { + domCopy->getOwnerDocument()->release(); + } + } + return NULL; +} diff --git a/xmltooling/AbstractDOMCachingXMLObject.h b/xmltooling/AbstractDOMCachingXMLObject.h index 9622d1c..862f925 100644 --- a/xmltooling/AbstractDOMCachingXMLObject.h +++ b/xmltooling/AbstractDOMCachingXMLObject.h @@ -35,6 +35,7 @@ namespace xmltooling { /** * Extension of AbstractXMLObject that implements a DOMCachingXMLObject. + * This is the primary base class for XMLObject implementation classes to use. */ class XMLTOOL_API AbstractDOMCachingXMLObject : public AbstractXMLObject, public DOMCachingXMLObject { @@ -97,8 +98,30 @@ namespace xmltooling { } } + /** + * @see XMLObject::clone() + */ + XMLObject* clone() const; + protected: /** + * Constructor + * + * @param namespaceURI the namespace the element is in + * @param elementLocalName the local name of the XML element this Object represents + */ + AbstractDOMCachingXMLObject(const XMLCh* namespaceURI=NULL, const XMLCh* elementLocalName=NULL, const XMLCh* namespacePrefix=NULL) + : AbstractXMLObject(namespaceURI,elementLocalName, namespacePrefix), m_dom(NULL), m_document(NULL) {} + + /** + * If a DOM representation exists, this clones it into a new document. + * + * @param doc the document to clone into, or NULL, in which case a new document is created + * @return the cloned DOM + */ + DOMElement* cloneDOM(DOMDocument* doc=NULL) const; + + /** * A helper function for derived classes. * This 'normalizes' newString and then if it is different from oldString * invalidates the DOM. It returns the normalized value. @@ -132,15 +155,6 @@ namespace xmltooling { */ XMLObject* prepareForAssignment(const XMLObject* oldValue, XMLObject* newValue); - /** - * Constructor - * - * @param namespaceURI the namespace the element is in - * @param elementLocalName the local name of the XML element this Object represents - */ - AbstractDOMCachingXMLObject(const XMLCh* namespaceURI=NULL, const XMLCh* elementLocalName=NULL, const XMLCh* namespacePrefix=NULL) - : AbstractXMLObject(namespaceURI,elementLocalName, namespacePrefix), m_dom(NULL), m_document(NULL) {} - private: DOMElement* m_dom; DOMDocument* m_document; diff --git a/xmltooling/AbstractXMLObject.h b/xmltooling/AbstractXMLObject.h index 0eeae30..c3aa289 100644 --- a/xmltooling/AbstractXMLObject.h +++ b/xmltooling/AbstractXMLObject.h @@ -23,6 +23,7 @@ #if !defined(__xmltooling_abstractxmlobj_h__) #define __xmltooling_abstractxmlobj_h__ +#include #include #if defined (_MSC_VER) @@ -40,6 +41,7 @@ namespace xmltooling { public: virtual ~AbstractXMLObject() { delete m_typeQname; + std::for_each(m_children.begin(), m_children.end(), cleanup()); } /** @@ -118,7 +120,21 @@ namespace xmltooling { void setParent(XMLObject* parent) { m_parent = parent; } - + + /** + * @see XMLObject::hasChildren() + */ + bool hasChildren() const { + return !m_children.empty(); + } + + /** + * @see XMLObject::getOrderedChildren() + */ + const std::list& getOrderedChildren() const { + return m_children; + } + protected: /** * Constructor @@ -130,6 +146,12 @@ namespace xmltooling { : m_elementQname(namespaceURI,elementLocalName, namespacePrefix), m_typeQname(NULL), m_parent(NULL) { addNamespace(Namespace(namespaceURI, namespacePrefix)); } + + /** + * Underlying list of child objects. + * Manages the lifetime of the children. + */ + std::list m_children; private: XMLObject* m_parent; diff --git a/xmltooling/XMLObject.h b/xmltooling/XMLObject.h index 83e003e..e8fb52a 100644 --- a/xmltooling/XMLObject.h +++ b/xmltooling/XMLObject.h @@ -134,17 +134,16 @@ namespace xmltooling { virtual bool hasChildren() const=0; /** - * Stores an unmodifiable list of child objects in the order that they - * will appear in the serialized representation. + * Returns an unmodifiable list of child objects in the order that they + * should appear in the serialized representation. * - * The validity of the returned objects is not maintained if any non-const + * The validity of the returned list is not maintained if any non-const * operations are performed on the parent object. * - * @param v vector in which to store pointers to child objects - * @return the number of children + * @return the list of children */ - virtual size_t getOrderedChildren(std::vector& v) const=0; - }; + virtual const std::list& getOrderedChildren() const=0; + }; }; diff --git a/xmltooling/impl/UnknownElement.h b/xmltooling/impl/UnknownElement.h index c22fa55..aff3ae5 100644 --- a/xmltooling/impl/UnknownElement.h +++ b/xmltooling/impl/UnknownElement.h @@ -58,20 +58,6 @@ namespace xmltooling { */ XMLObject* clone() const; - /** - * @see XMLObject::hasChildren() - */ - bool hasChildren() const { - return false; - } - - /** - * @see XMLObject::getOrderedChildren() - */ - size_t getOrderedChildren(std::vector& v) const { - return 0; - } - protected: /** * When needed, we can serialize the DOM into XML form and preserve it here. diff --git a/xmltooling/io/AbstractXMLObjectMarshaller.cpp b/xmltooling/io/AbstractXMLObjectMarshaller.cpp index 65bc509..42a4080 100644 --- a/xmltooling/io/AbstractXMLObjectMarshaller.cpp +++ b/xmltooling/io/AbstractXMLObjectMarshaller.cpp @@ -283,6 +283,8 @@ class _marshallchild : public binary_function { public: _marshallchild(void* log) : m_log(log) {} void operator()(XMLObject* obj, DOMElement* element) const { + if (!obj) + return; if (XT_log.isDebugEnabled()) { XT_log.debug("getting marshaller for child XMLObject: %s", obj->getElementQName().toString().c_str()); } @@ -303,8 +305,6 @@ void AbstractXMLObjectMarshaller::marshallChildElements(const XMLObject& xmlObje { XT_log.debug("marshalling child elements for XMLObject"); - vector children; - if (xmlObject.getOrderedChildren(children)) { - for_each(children.begin(),children.end(),bind2nd(_marshallchild(m_log),domElement)); - } + const list& children=xmlObject.getOrderedChildren(); + for_each(children.begin(),children.end(),bind2nd(_marshallchild(m_log),domElement)); } diff --git a/xmltooling/util/XMLObjectChildrenList.h b/xmltooling/util/XMLObjectChildrenList.h new file mode 100644 index 0000000..b528798 --- /dev/null +++ b/xmltooling/util/XMLObjectChildrenList.h @@ -0,0 +1,284 @@ +/* + * 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 XMLObjectChildrenList.h + * + * STL-compatible container wrapper + */ + +#if !defined(__xmltooling_list_h__) +#define __xmltooling_list_h__ + +#include +#include + +#define ListOf(type) xmltooling::XMLObjectChildrenList + +namespace xmltooling { + + // Forward reference + template class XMLObjectChildrenList; + + /** + * STL iterator that mediates access to an iterator over typed XML children. + * @param _Ty a bidrectional sequence of the subtype to iterate over + */ + template + class XMLObjectChildrenIterator + { + typename _Ty::iterator m_iter; + template friend class XMLObjectChildrenList; + public: + typedef typename _Ty::iterator::iterator_category iterator_category; + typedef typename _Ty::iterator::value_type value_type; + typedef typename _Ty::iterator::reference reference; + typedef typename _Ty::iterator::pointer pointer; + typedef typename _Ty::const_iterator::reference const_reference; + typedef typename _Ty::const_iterator::pointer const_pointer; + typedef typename _Ty::iterator::difference_type difference_type; + + XMLObjectChildrenIterator() { + } + + XMLObjectChildrenIterator(typename _Ty::iterator& iter) { + m_iter=iter; + } + + const_reference operator*() const { + return *m_iter; + } + + pointer operator->() const { + return (&**this); + } + + XMLObjectChildrenIterator& operator++() { + // preincrement + ++m_iter; + return (*this); + } + + XMLObjectChildrenIterator& operator--() { + // predecrement + --m_iter; + return (*this); + } + + XMLObjectChildrenIterator operator++(int) { + // postincrement + XMLObjectChildrenIterator _Tmp = *this; + ++*this; + return (_Tmp); + } + + XMLObjectChildrenIterator operator--(int) { + // postdecrement + XMLObjectChildrenIterator _Tmp = *this; + --*this; + return (_Tmp); + } + + XMLObjectChildrenIterator& operator+=(difference_type _Off) { + // increment by integer + m_iter += _Off; + return (*this); + } + + XMLObjectChildrenIterator operator+(difference_type _Off) const { + // return this + integer + XMLObjectChildrenIterator _Tmp = *this; + return (_Tmp += _Off); + } + + XMLObjectChildrenIterator& operator-=(difference_type _Off) { + // decrement by integer + return (*this += -_Off); + } + + XMLObjectChildrenIterator operator-(difference_type _Off) const { + // return this - integer + XMLObjectChildrenIterator _Tmp = *this; + return (_Tmp -= _Off); + } + + difference_type operator-(const XMLObjectChildrenIterator& _Right) const { + // return difference of iterators + return m_iter - _Right.m_iter); + } + + const_reference operator[](difference_type _Off) const { + // subscript + return (*(*this + _Off)); + } + + bool operator==(const XMLObjectChildrenIterator &_Right) const { + // test for iterator equality + return (m_iter == _Right.m_iter); + } + + bool operator!=(const XMLObjectChildrenIterator &_Right) const { + // test for iterator inequality + return (!(m_iter == _Right.m_iter)); + } + }; + + /** + * STL-compatible container that mediates access to underlying lists of typed XML children. + * @param _Tx the subtype to expose a container over + * @param _Ty the base type in the underlying list (defaults to XMLObject) + */ + template + class XMLObjectChildrenList + { + typedef typename std::vector<_Tx*> container; + typename container& m_vector; + typename std::list<_Ty*>& m_list; + typename std::list<_Ty*>::iterator m_fence; + XMLObject* m_parent; + + public: + typedef typename container::value_type value_type; + typedef typename container::reference reference; + typedef typename container::const_reference const_reference; + typedef typename container::difference_type difference_type; + typedef typename container::size_type size_type; + + // We override the iterator types with our constrained wrapper. + typedef XMLObjectChildrenIterator iterator; + typedef const XMLObjectChildrenIterator const_iterator; + + /** + * Constructor to expose a typed collection of children backed by a list of a base type. + * + * @param parent parent object of the collection + * @param v underlying vector of iterators that reference the children + * @param backing backing list for children + * @param ins_fence a marker designating where new children of this type should be added + */ + XMLObjectChildrenList( + XMLObject* parent, + typename container& v, + typename std::list<_Ty*>& backing, + typename std::list<_Ty*>::iterator ins_fence + ) : m_parent(parent), m_vector(v), m_list(backing), m_fence(ins_fence) { + } + + size_type size() const { + // return length of sequence + return m_vector.size(); + } + + bool empty() const { + // test if sequence is empty + return m_vector.empty(); + } + + iterator begin() { + // return iterator for beginning of mutable sequence + return m_vector.begin(); + } + + iterator end() { + // return iterator for end of mutable sequence + return m_vector.end(); + } + + const_iterator begin() const { + // return iterator for beginning of const sequence + return m_vector.begin(); + } + + const_iterator end() const { + // return iterator for end of const sequence + return m_vector.end(); + } + + const_reference at(size_type _Pos) const { + // subscript nonmutable sequence with checking + return m_vector.at(_Pos); + } + + const_reference operator[](size_type _Pos) const { + // subscript nonmutable sequence + return m_vector[_Pos]; + } + + const_reference front() const { + // return first element of nonmutable sequence + return (*begin()); + } + + const_reference back() const { + // return last element of nonmutable sequence + return *(m_vector.back()); + } + + void push_back(const_reference _Val) { + setParent(_Val); + m_list.insert(m_fence,_Val); + m_vector.push_back(_Val); + } + + iterator erase(iterator _Where) { + removeParent(*_Where); + removeChild(*_Where); + return m_vector.erase(_Where.m_iter); + } + + iterator erase(iterator _First, iterator _Last) { + for (iterator i=_First; i!=_Last; i++) { + removeParent(*i); + removeChild(*i); + } + return m_vector.erase(_First,_Last); + } + + private: + void setParent(const_reference _Val) { + if (_Val->getParent()) + throw XMLObjectException("Child object already has a parent."); + _Val->setParent(m_parent); + DOMCachingXMLObject* dc=dynamic_cast(_Val); + if (dc) { + dc->releaseParentDOM(true); + } + } + + void removeParent(const_reference _Val) { + if (_Val->getParent()!=m_parent) + throw XMLObjectException("Child object not owned by this parent."); + _Val->setParent(NULL); + DOMCachingXMLObject* dc=dynamic_cast(m_parent); + if (dc) { + dc->releaseParentDOM(true); + } + } + + void removeChild(const_reference _Val) { + for (typename std::list<_Ty*>::iterator i=m_list.begin(); i!=m_list.end(); i++) { + if ((*i)==_Val) { + m_list.erase(i); + delete _Val; + return; + } + } + } + }; + +}; + +#endif /* __xmltooling_list_h__ */ diff --git a/xmltooling/xmltooling.vcproj b/xmltooling/xmltooling.vcproj index 6a651d8..c2dff62 100644 --- a/xmltooling/xmltooling.vcproj +++ b/xmltooling/xmltooling.vcproj @@ -338,6 +338,10 @@ RelativePath=".\util\XMLHelper.h" > + + sxObject(dynamic_cast(b->buildObject())); TS_ASSERT(sxObject.get()!=NULL); - sxObject->getSimpleXMLObjects().push_back(dynamic_cast(b->buildObject())); - sxObject->getSimpleXMLObjects().push_back(dynamic_cast(b->buildObject())); + ListOf(SimpleXMLObject) kids=sxObject->getSimpleXMLObjects(); + kids.push_back(dynamic_cast(b->buildObject())); + kids.push_back(dynamic_cast(b->buildObject())); + kids.push_back(dynamic_cast(b->buildObject())); + + // Test some collection stuff + auto_ptr_XMLCh foo("Foo"); + auto_ptr_XMLCh bar("Bar"); + kids[0]->setId(foo.get()); + kids.at(2)->setValue(bar.get()); + kids.erase(kids.begin()+1); DOMElement* rootElement = Marshaller::getMarshaller(sxObject.get())->marshall(sxObject.get()); diff --git a/xmltoolingtest/UnmarshallingTest.h b/xmltoolingtest/UnmarshallingTest.h index d4ba4f6..a5c87a4 100644 --- a/xmltoolingtest/UnmarshallingTest.h +++ b/xmltoolingtest/UnmarshallingTest.h @@ -108,7 +108,8 @@ public: auto_ptr sxObject(dynamic_cast(u->unmarshall(doc->getDocumentElement(),true))); TS_ASSERT(sxObject.get()!=NULL); - TSM_ASSERT_EQUALS("Number of child elements was not expected value", 2, sxObject->getSimpleXMLObjects().size()); + ListOf(SimpleXMLObject) kids=sxObject->getSimpleXMLObjects(); + TSM_ASSERT_EQUALS("Number of child elements was not expected value", 2, kids.size()); } void testUnmarshallingWithUnknownChild() { diff --git a/xmltoolingtest/XMLObjectBaseTestCase.h b/xmltoolingtest/XMLObjectBaseTestCase.h index a6e999d..d69e1c0 100644 --- a/xmltoolingtest/XMLObjectBaseTestCase.h +++ b/xmltoolingtest/XMLObjectBaseTestCase.h @@ -22,6 +22,7 @@ #include #include #include +#include #include using namespace xmltooling; @@ -50,7 +51,6 @@ public: virtual ~SimpleXMLObject() { XMLString::release(&m_id); XMLString::release(&m_value); - for_each(m_children.begin(), m_children.end(), cleanup()); } const XMLCh* getId() const { return m_id; } @@ -60,15 +60,19 @@ public: void setValue(const XMLCh* value) { m_value=prepareForAssignment(m_value,value); } // TODO: Leave non-const, but wrap STL container to intercept adds. - list& getSimpleXMLObjects() { return m_children; } - - bool hasChildren() const { return !m_children.empty(); } - size_t getOrderedChildren(vector& children) const { - children.assign(m_children.begin(),m_children.end()); - return children.size(); + ListOf(SimpleXMLObject) getSimpleXMLObjects() { + return ListOf(SimpleXMLObject)(this, m_simples, m_children, m_children.end()); } + SimpleXMLObject* clone() const { - SimpleXMLObject* ret=new SimpleXMLObject(); + auto_ptr domClone(AbstractDOMCachingXMLObject::clone()); + SimpleXMLObject* ret=dynamic_cast(domClone.get()); + if (ret) { + domClone.release(); + return ret; + } + + ret=new SimpleXMLObject(); ret->setId(m_id); ret->setValue(m_value); xmltooling::clone(m_children, ret->m_children); @@ -78,7 +82,7 @@ public: private: XMLCh* m_id; XMLCh* m_value; - list m_children; + vector m_simples; friend class SimpleXMLObjectUnmarshaller; }; @@ -127,6 +131,7 @@ private: SimpleXMLObject* child = dynamic_cast(childXMLObject); if (child) { simpleXMLObject.m_children.push_back(child); + simpleXMLObject.m_simples.push_back(child); } else { throw UnmarshallingException("Unknown child element cannot be added to parent object."); diff --git a/xmltoolingtest/data/SimpleXMLObjectWithChildren.xml b/xmltoolingtest/data/SimpleXMLObjectWithChildren.xml index ce437ed1a52a18a3791a0516b49b6d391260b6a6..f8a9f6763924f605309c7a3a4560f15456b46594 100644 GIT binary patch delta 44 vcmcb?G=q5qBcq@KgC|1@gDry+gBwFW5GqY#)MmG1aAHVgD4MLts0G3R$E66H delta 18 ZcmbQie1mBNBO{~!#2cE7`jc}QbpSQg1x5e> diff --git a/xmltoolingtest/xmltoolingtest.vcproj b/xmltoolingtest/xmltoolingtest.vcproj index 561606c..61df325 100644 --- a/xmltoolingtest/xmltoolingtest.vcproj +++ b/xmltoolingtest/xmltoolingtest.vcproj @@ -209,7 +209,7 @@ > @@ -218,7 +218,7 @@ > @@ -231,7 +231,7 @@ > @@ -240,7 +240,7 @@ > @@ -253,7 +253,7 @@ > @@ -262,7 +262,7 @@ > -- 2.1.4