#if !defined (INCLUDED_TRAVERSELIB_H) #define INCLUDED_TRAVERSELIB_H #include "debugging/debugging.h" #include "scenelib.h" #include "undolib.h" #include "container/container.h" #include #include #include class TraversableObserverInsertOutputIterator { protected: scene::Traversable::Observer* m_observer; public: typedef std::output_iterator_tag iterator_category; typedef void difference_type; typedef void value_type; typedef void pointer; typedef void reference; TraversableObserverInsertOutputIterator(scene::Traversable::Observer* observer) : m_observer(observer) { } TraversableObserverInsertOutputIterator& operator=(const NodeReference& node) { m_observer->insert(node); return *this; } TraversableObserverInsertOutputIterator& operator*() { return *this; } TraversableObserverInsertOutputIterator& operator++() { return *this; } TraversableObserverInsertOutputIterator& operator++(int) { return *this; } }; class TraversableObserverEraseOutputIterator { protected: scene::Traversable::Observer* m_observer; public: typedef std::output_iterator_tag iterator_category; typedef void difference_type; typedef void value_type; typedef void pointer; typedef void reference; TraversableObserverEraseOutputIterator(scene::Traversable::Observer* observer) : m_observer(observer) { } TraversableObserverEraseOutputIterator& operator=(const NodeReference& node) { m_observer->erase(node); return *this; } TraversableObserverEraseOutputIterator& operator*() { return *this; } TraversableObserverEraseOutputIterator& operator++() { return *this; } TraversableObserverEraseOutputIterator& operator++(int) { return *this; } }; typedef UnsortedSet UnsortedNodeSet; /// \brief Calls \p observer->\c insert for each node that exists only in \p other and \p observer->\c erase for each node that exists only in \p self inline void nodeset_diff(const UnsortedNodeSet& self, const UnsortedNodeSet& other, scene::Traversable::Observer* observer) { std::vector sorted(self.begin(), self.end()); std::vector other_sorted(other.begin(), other.end()); std::sort(sorted.begin(), sorted.end()); std::sort(other_sorted.begin(), other_sorted.end()); std::set_difference(sorted.begin(), sorted.end(), other_sorted.begin(), other_sorted.end(), TraversableObserverEraseOutputIterator(observer)); std::set_difference(other_sorted.begin(), other_sorted.end(), sorted.begin(), sorted.end(), TraversableObserverInsertOutputIterator(observer)); } /// \brief A sequence of node references which notifies an observer of inserts and deletions, and uses the global undo system to provide undo for modifications. class TraversableNodeSet : public scene::Traversable { UnsortedNodeSet m_children; UndoableObject m_undo; Observer* m_observer; void copy(const TraversableNodeSet& other) { m_children = other.m_children; } void notifyInsertAll() { if(m_observer) { for(UnsortedNodeSet::iterator i = m_children.begin(); i != m_children.end(); ++i) { m_observer->insert(*i); } } } void notifyEraseAll() { if(m_observer) { for(UnsortedNodeSet::iterator i = m_children.begin(); i != m_children.end(); ++i) { m_observer->erase(*i); } } } public: TraversableNodeSet() : m_undo(*this), m_observer(0) { } TraversableNodeSet(const TraversableNodeSet& other) : scene::Traversable(other), m_undo(*this), m_observer(0) { copy(other); notifyInsertAll(); } ~TraversableNodeSet() { notifyEraseAll(); } TraversableNodeSet& operator=(const TraversableNodeSet& other) { #if 1 // optimised change-tracking using diff algorithm if(m_observer) { nodeset_diff(m_children, other.m_children, m_observer); } copy(other); #else TraversableNodeSet tmp(other); tmp.swap(*this); #endif return *this; } void swap(TraversableNodeSet& other) { std::swap(m_children, other.m_children); std::swap(m_observer, other.m_observer); } void attach(Observer* observer) { ASSERT_MESSAGE(m_observer == 0, "TraversableNodeSet::attach: observer cannot be attached"); m_observer = observer; notifyInsertAll(); } void detach(Observer* observer) { ASSERT_MESSAGE(m_observer == observer, "TraversableNodeSet::detach: observer cannot be detached"); notifyEraseAll(); m_observer = 0; } /// \brief \copydoc scene::Traversable::insert() void insert(scene::Node& node) { ASSERT_MESSAGE(&node != 0, "TraversableNodeSet::insert: sanity check failed"); m_undo.save(); ASSERT_MESSAGE(m_children.find(NodeSmartReference(node)) == m_children.end(), "TraversableNodeSet::insert - element already exists"); m_children.insert(NodeSmartReference(node)); if(m_observer) { m_observer->insert(node); } } /// \brief \copydoc scene::Traversable::erase() void erase(scene::Node& node) { ASSERT_MESSAGE(&node != 0, "TraversableNodeSet::erase: sanity check failed"); m_undo.save(); ASSERT_MESSAGE(m_children.find(NodeSmartReference(node)) != m_children.end(), "TraversableNodeSet::erase - failed to find element"); if(m_observer) { m_observer->erase(node); } m_children.erase(NodeSmartReference(node)); } /// \brief \copydoc scene::Traversable::traverse() void traverse(const Walker& walker) { UnsortedNodeSet::iterator i = m_children.begin(); while(i != m_children.end()) { // post-increment the iterator Node_traverseSubgraph(*i++, walker); // the Walker can safely remove the current node from // this container without invalidating the iterator } } /// \brief \copydoc scene::Traversable::empty() bool empty() const { return m_children.empty(); } void instanceAttach(MapFile* map) { m_undo.instanceAttach(map); } void instanceDetach(MapFile* map) { m_undo.instanceDetach(map); } }; namespace std { /// \brief Swaps the values of \p self and \p other. /// Overloads std::swap. inline void swap(TraversableNodeSet& self, TraversableNodeSet& other) { self.swap(other); } } class TraversableNode : public scene::Traversable { public: TraversableNode() : m_node(0), m_observer(0) { } // traverse void attach(Observer* observer) { ASSERT_MESSAGE(m_observer == 0, "TraversableNode::attach - cannot attach observer"); m_observer = observer; if(m_node != 0) { m_observer->insert(*m_node); } } void detach(Observer* observer) { ASSERT_MESSAGE(m_observer == observer, "TraversableNode::detach - cannot detach observer"); if(m_node != 0) { m_observer->erase(*m_node); } m_observer = 0; } void insert(scene::Node& node) { ASSERT_MESSAGE(m_node == 0, "TraversableNode::insert - element already exists"); m_node = &node; node.IncRef(); if(m_observer != 0) { m_observer->insert(node); } } void erase(scene::Node& node) { ASSERT_MESSAGE(m_node == &node, "TraversableNode::erase - failed to find element"); if(m_observer != 0) { m_observer->erase(node); } m_node = 0; node.DecRef(); } void traverse(const scene::Traversable::Walker& walker) { if(m_node != 0) { Node_traverseSubgraph(*m_node, walker); } } bool empty() const { return m_node != 0; } scene::Node& get() { return *m_node; } private: scene::Node* m_node; Observer* m_observer; }; class TraversableObserverPairRelay : public scene::Traversable::Observer { scene::Traversable::Observer* m_first; scene::Traversable::Observer* m_second; public: TraversableObserverPairRelay() : m_first(0), m_second(0) { } void attach(scene::Traversable::Observer* observer) { ASSERT_MESSAGE(m_first == 0 || m_second == 0, "TraversableObserverPair::attach: cannot attach observer"); if(m_first == 0) { m_first = observer; } else if(m_second == 0) { m_second = observer; } } void detach(scene::Traversable::Observer* observer) { ASSERT_MESSAGE(m_first == observer || m_second == observer, "TraversableObserverPair::detach: cannot detach observer"); if(m_first == observer) { m_first = 0; } else if(m_second == observer) { m_second = 0; } } void insert(scene::Node& node) { if(m_first != 0) { m_first->insert(node); } if(m_second != 0) { m_second->insert(node); } } void erase(scene::Node& node) { if(m_second != 0) { m_second->erase(node); } if(m_first != 0) { m_first->erase(node); } } }; #endif