普通文本  |  98行  |  2.93 KB

// Copyright 2015 The Weave Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/notification/xmpp_stream_parser.h"

#include "src/notification/xml_node.h"

namespace weave {

XmppStreamParser::XmppStreamParser(Delegate* delegate) : delegate_{delegate} {
  parser_ = XML_ParserCreate(nullptr);
  XML_SetUserData(parser_, this);
  XML_SetElementHandler(parser_, &XmppStreamParser::HandleElementStart,
                        &XmppStreamParser::HandleElementEnd);
  XML_SetCharacterDataHandler(parser_, &XmppStreamParser::HandleCharData);
}

XmppStreamParser::~XmppStreamParser() {
  XML_ParserFree(parser_);
}

void XmppStreamParser::ParseData(const std::string& data) {
  XML_Parse(parser_, data.data(), data.size(), 0);
}

void XmppStreamParser::Reset() {
  std::stack<std::unique_ptr<XmlNode>>{}.swap(node_stack_);
  started_ = false;
}

void XmppStreamParser::HandleElementStart(void* user_data,
                                          const XML_Char* element,
                                          const XML_Char** attr) {
  auto self = static_cast<XmppStreamParser*>(user_data);
  std::map<std::string, std::string> attributes;
  if (attr != nullptr) {
    for (size_t n = 0; attr[n] != nullptr && attr[n + 1] != nullptr; n += 2) {
      attributes.insert(std::make_pair(attr[n], attr[n + 1]));
    }
  }
  self->OnOpenElement(element, std::move(attributes));
}

void XmppStreamParser::HandleElementEnd(void* user_data,
                                        const XML_Char* element) {
  auto self = static_cast<XmppStreamParser*>(user_data);
  self->OnCloseElement(element);
}

void XmppStreamParser::HandleCharData(void* user_data,
                                      const char* content,
                                      int length) {
  auto self = static_cast<XmppStreamParser*>(user_data);
  self->OnCharData(std::string{content, static_cast<size_t>(length)});
}

void XmppStreamParser::OnOpenElement(
    const std::string& node_name,
    std::map<std::string, std::string> attributes) {
  if (!started_) {
    started_ = true;
    if (delegate_)
      delegate_->OnStreamStart(node_name, std::move(attributes));
    return;
  }
  node_stack_.emplace(new XmlNode{node_name, std::move(attributes)});
}

void XmppStreamParser::OnCloseElement(const std::string& node_name) {
  if (node_stack_.empty()) {
    if (started_) {
      started_ = false;
      if (delegate_)
        delegate_->OnStreamEnd(node_name);
    }
    return;
  }

  auto node = std::move(node_stack_.top());
  node_stack_.pop();
  if (!node_stack_.empty()) {
    XmlNode* parent = node_stack_.top().get();
    parent->AddChild(std::move(node));
  } else if (delegate_) {
    delegate_->OnStanza(std::move(node));
  }
}

void XmppStreamParser::OnCharData(const std::string& text) {
  if (!node_stack_.empty()) {
    XmlNode* node = node_stack_.top().get();
    node->AppendText(text);
  }
}

}  // namespace weave