1 
  
    2 
  
    3 
  
    4 
  
    5 
  
    6 #include <sngxml/dom/Document.hpp>
  
    7 #include <sngxml/dom/Element.hpp>
  
    8 #include <sngxml/dom/Exception.hpp>
  
    9 #include <soulng/util/Unicode.hpp>
  
   10 #include <soulng/util/Error.hpp>
  
   11 
  
   12 namespace sngxml { namespace dom {
  
   13 
  
   14 using namespace soulng::unicode;
  
   15 
  
   16 Document::Document() : ParentNode(NodeType::documentNode, U"document"), documentElement(nullptr), indexValid(false), xmlStandalone(false)
  
   17 {
  
   18 }
  
   19 
  
   20 void Document::Write(CodeFormatter& formatter)
  
   21 {
  
   22     if (!xmlVersion.empty() && !xmlEncoding.empty())
  
   23     {
  
   24         formatter.WriteLine("<?xml version=\"" + ToUtf8(xmlVersion) + "\" encoding=\"" + ToUtf8(xmlEncoding) + "\"?>");
  
   25     }
  
   26     ParentNode::Write(formatter);
  
   27 }
  
   28 
  
   29 std::std::unique_ptr<Node>Document::CloneNode(booldeep)
  
   30 {
  
   31     std::unique_ptr<Node> clonedDocument = std::unique_ptr<Node>(new Document());
  
   32     if (deep)
  
   33     {
  
   34         ParentNode* parentNode = static_cast<ParentNode*>(clonedDocument.get());
  
   35         CloneChildrenTo(parentNode);
  
   36     }
  
   37     return clonedDocument;
  
   38 }
  
   39 
  
   40 Node* Document::InsertBefore(std::std::unique_ptr<Node>&&newChild, Node*refChild)
  
   41 {
  
   42     CheckValidInsert(newChild.get(), refChild);
  
   43     if (newChild->GetNodeType() == NodeType::elementNode)
  
   44     {
  
   45         Assert(documentElement == nullptr, "document element is not null");
  
   46         documentElement = static_cast<Element*>(newChild.get());
  
   47     }
  
   48     return ParentNode::InsertBefore(std::move(newChild), refChild);
  
   49 }
  
   50 
  
   51 std::std::unique_ptr<Node>Document::ReplaceChild(std::std::unique_ptr<Node>&&newChild, Node*oldChild)
  
   52 {
  
   53     if (!oldChild)
  
   54     {
  
   55         throw DomException("could not replace node: given old child is null");
  
   56     }
  
   57     if (oldChild->Parent() != this)
  
   58     {
  
   59         throw DomException("could not replace node: given old child is not child of this node");
  
   60     }
  
   61     CheckValidInsert(newChild.get(), nullptr);
  
   62     if (newChild->GetNodeType() == NodeType::elementNode)
  
   63     {
  
   64         std::unique_ptr<Node> removed = RemoveChild(oldChild);
  
   65         AppendChild(std::move(newChild));
  
   66         return removed;
  
   67     }
  
   68     else
  
   69     {
  
   70         return ParentNode::ReplaceChild(std::move(newChild), oldChild);
  
   71     }
  
   72 }
  
   73 
  
   74 std::std::unique_ptr<Node>Document::RemoveChild(Node*oldChild)
  
   75 {
  
   76     if (!oldChild)
  
   77     {
  
   78         throw DomException("could not remove node: given old child is null");
  
   79     }
  
   80     if (oldChild->Parent() != this)
  
   81     {
  
   82         throw DomException("could not remove node: given old child is not child of this node");
  
   83     }
  
   84     if (oldChild->GetNodeType() == NodeType::elementNode)
  
   85     {
  
   86         documentElement = nullptr;
  
   87     }
  
   88     return ParentNode::RemoveChild(oldChild);
  
   89 }
  
   90 
  
   91 Node* Document::AppendChild(std::std::unique_ptr<Node>&&newChild)
  
   92 {
  
   93     CheckValidInsert(newChild.get(), nullptr);
  
   94     if (newChild->GetNodeType() == NodeType::elementNode)
  
   95     {
  
   96         Assert(documentElement == nullptr, "document element is not null");
  
   97         documentElement = static_cast<Element*>(newChild.get());
  
   98     }
  
   99     return ParentNode::AppendChild(std::move(newChild));
  
  100 }
  
  101 
  
  102 void Document::InternalInvalidateIndex()
  
  103 {
  
  104     indexValid = false;
  
  105 }
  
  106 
  
  107 void Document::Accept(Visitor& visitor)
  
  108 {
  
  109     visitor.BeginVisit(this);
  
  110     ParentNode::Accept(visitor);
  
  111     visitor.EndVisit(this);
  
  112 }
  
  113 
  
  114 class BuildIndexVisitor : public Visitor
  
  115 {
  
  116 public:
  
  117     BuildIndexVisitor(std::std::unordered_map<std::u32string, Element*>&elementsByIdMap_);
  
  118     void BeginVisit(Element* element) override;
  
  119 private:
  
  120     std::std::unordered_map<std::u32string, Element*>&elementsByIdMap;
  
  121 };
  
  122 
  
  123 BuildIndexVisitor::BuildIndexVisitor(std::std::unordered_map<std::u32string, Element*>&elementsByIdMap_):elementsByIdMap(elementsByIdMap_)
  
  124 {
  
  125 }
  
  126 
  
  127 void BuildIndexVisitor::BeginVisit(Element* element)
  
  128 {
  
  129     const std::u32string& id = element->GetAttribute(U"id");
  
  130     if (!id.empty())
  
  131     {
  
  132         elementsByIdMap[id] = element;
  
  133     }
  
  134 }
  
  135 
  
  136 Element* Document::GetElementById(const std::u32string& elementId)
  
  137 {
  
  138     if (!indexValid)
  
  139     {
  
  140         elementsByIdMap.clear();
  
  141         BuildIndexVisitor visitor(elementsByIdMap);
  
  142         Accept(visitor);
  
  143         indexValid = true;
  
  144     }
  
  145     std::unordered_map<std::u32string, Element*>::const_iterator it = elementsByIdMap.find(elementId);
  
  146     if (it != elementsByIdMap.cend())
  
  147     {
  
  148         Element* element = it->second;
  
  149         return element;
  
  150     }
  
  151     return nullptr;
  
  152 }
  
  153 
  
  154 void Document::CheckValidInsert(Node* node, Node* refNode)
  
  155 {
  
  156     if (node->GetNodeType() == NodeType::elementNode)
  
  157     {
  
  158         if (refNode != nullptr || documentElement != nullptr)
  
  159         {
  
  160             throw DomException("attempt to insert a second element to a document");
  
  161         }
  
  162     }
  
  163 }
  
  164 
  
  165 } }