diff --git a/src/value/valueevents.cpp b/src/value/valueevents.cpp index 877eb16..c04b91d 100644 --- a/src/value/valueevents.cpp +++ b/src/value/valueevents.cpp @@ -1,14 +1,29 @@ #include "valueevents.h" #include "yaml-cpp/value.h" +#include "yaml-cpp/eventhandler.h" +#include "yaml-cpp/mark.h" namespace YAML { - ValueEvents::ValueEvents(const Value& value): m_pMemory(value.m_pMemory), m_root(*value.m_pNode) + void ValueEvents::AliasManager::RegisterReference(const detail::node& node) { - Visit(m_root); + m_anchorByIdentity.insert(std::make_pair(node.ref(), _CreateNewAnchor())); + } + + anchor_t ValueEvents::AliasManager::LookupAnchor(const detail::node& node) const + { + AnchorByIdentity::const_iterator it = m_anchorByIdentity.find(node.ref()); + if(it == m_anchorByIdentity.end()) + return 0; + return it->second; } - void ValueEvents::Visit(const detail::node& node) + ValueEvents::ValueEvents(const Value& value): m_pMemory(value.m_pMemory), m_root(*value.m_pNode) + { + Setup(m_root); + } + + void ValueEvents::Setup(const detail::node& node) { int& refCount = m_refCount[node.ref()]; refCount++; @@ -17,12 +32,67 @@ namespace YAML if(node.type() == ValueType::Sequence) { for(detail::const_node_iterator it=node.begin();it!=node.end();++it) - Visit(**it); + Setup(**it); } else if(node.type() == ValueType::Map) { for(detail::const_node_iterator it=node.begin();it!=node.end();++it) { - Visit(*it->first); - Visit(*it->second); + Setup(*it->first); + Setup(*it->second); } } } + + void ValueEvents::Emit(EventHandler& handler) + { + AliasManager am; + + handler.OnDocumentStart(Mark()); + Emit(m_root, handler, am); + handler.OnDocumentEnd(); + } + + void ValueEvents::Emit(const detail::node& node, EventHandler& handler, AliasManager& am) const + { + anchor_t anchor = NullAnchor; + if(IsAliased(node)) { + anchor = am.LookupAnchor(node); + if(anchor) { + handler.OnAlias(Mark(), anchor); + return; + } + + am.RegisterReference(node); + anchor = am.LookupAnchor(node); + } + + switch(node.type()) { + case ValueType::Undefined: + break; + case ValueType::Null: + handler.OnNull(Mark(), anchor); + break; + case ValueType::Scalar: + handler.OnScalar(Mark(), "", anchor, node.scalar()); + break; + case ValueType::Sequence: + handler.OnSequenceStart(Mark(), "", anchor); + for(detail::const_node_iterator it=node.begin();it!=node.end();++it) + Emit(**it, handler, am); + handler.OnSequenceEnd(); + break; + case ValueType::Map: + handler.OnMapStart(Mark(), "", anchor); + for(detail::const_node_iterator it=node.begin();it!=node.end();++it) { + Emit(*it->first, handler, am); + Emit(*it->second, handler, am); + } + handler.OnMapEnd(); + break; + } + } + + bool ValueEvents::IsAliased(const detail::node& node) const + { + RefCount::const_iterator it = m_refCount.find(node.ref()); + return it != m_refCount.end() && it->second > 1; + } } diff --git a/src/value/valueevents.h b/src/value/valueevents.h index 9273c67..eeb74d6 100644 --- a/src/value/valueevents.h +++ b/src/value/valueevents.h @@ -13,14 +13,36 @@ namespace YAML { class Value; + class EventHandler; class ValueEvents { public: ValueEvents(const Value& value); + void Emit(EventHandler& handler); + private: - void Visit(const detail::node& node); + class AliasManager { + public: + AliasManager(): m_curAnchor(0) {} + + void RegisterReference(const detail::node& node); + anchor_t LookupAnchor(const detail::node& node) const; + + private: + anchor_t _CreateNewAnchor() { return ++m_curAnchor; } + + private: + typedef std::map AnchorByIdentity; + AnchorByIdentity m_anchorByIdentity; + + anchor_t m_curAnchor; + }; + + void Setup(const detail::node& node); + void Emit(const detail::node& node, EventHandler& handler, AliasManager& am) const; + bool IsAliased(const detail::node& node) const; private: detail::shared_memory_holder m_pMemory;