blob: bd3f0c3d99ffc6cf80b8c3e123910d15129b6b97 [file] [log] [blame]
// Copyright 2014 The Chromium OS 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 "chromeos-dbus-bindings/xml_interface_parser.h"
#include <utility>
#include <base/file_util.h>
#include <base/files/file_path.h>
#include <base/logging.h>
#include <base/stl_util.h>
using std::string;
using std::vector;
namespace chromeos_dbus_bindings {
// static
const char XmlInterfaceParser::kArgumentTag[] = "arg";
const char XmlInterfaceParser::kInterfaceTag[] = "interface";
const char XmlInterfaceParser::kMethodTag[] = "method";
const char XmlInterfaceParser::kNodeTag[] = "node";
const char XmlInterfaceParser::kSignalTag[] = "signal";
const char XmlInterfaceParser::kNameAttribute[] = "name";
const char XmlInterfaceParser::kTypeAttribute[] = "type";
const char XmlInterfaceParser::kDirectionAttribute[] = "direction";
const char XmlInterfaceParser::kArgumentDirectionIn[] = "in";
const char XmlInterfaceParser::kArgumentDirectionOut[] = "out";
bool XmlInterfaceParser::ParseXmlInterfaceFile(
const base::FilePath& interface_file) {
string contents;
if (!base::ReadFileToString(interface_file, &contents)) {
LOG(ERROR) << "Failed to read file " << interface_file.value();
return false;
}
auto parser = XML_ParserCreate(nullptr);
XML_SetUserData(parser, this);
XML_SetElementHandler(parser,
&XmlInterfaceParser::HandleElementStart,
&XmlInterfaceParser::HandleElementEnd);
const int kIsFinal = XML_TRUE;
element_path_.clear();
XML_Status res = XML_Parse(parser,
contents.c_str(),
contents.size(),
kIsFinal);
XML_ParserFree(parser);
if (res != XML_STATUS_OK) {
LOG(ERROR) << "XML parse failure";
return false;
}
CHECK(element_path_.empty());
return true;
}
void XmlInterfaceParser::OnOpenElement(
const string& element_name, const XmlAttributeMap& attributes) {
element_path_.push_back(element_name);
if (element_path_ == vector<string> { kNodeTag, kInterfaceTag }) {
string interface_name = GetValidatedElementName(attributes, kInterfaceTag);
CHECK(interface_.name.empty())
<< "Found a second interface named " << interface_name << ". "
<< "Interface " << interface_.name << " has already been parsed.";
interface_.name = interface_name;
} else if (element_path_ == vector<string> {
kNodeTag, kInterfaceTag, kMethodTag }) {
interface_.methods.push_back(
Interface::Method(GetValidatedElementName(attributes, kMethodTag)));
} else if (element_path_ == vector<string> {
kNodeTag, kInterfaceTag, kMethodTag, kArgumentTag }) {
AddMethodArgument(attributes);
} else if (element_path_ == vector<string> {
kNodeTag, kInterfaceTag, kSignalTag }) {
interface_.signals.push_back(
Interface::Signal(GetValidatedElementName(attributes, kSignalTag)));
} else if (element_path_ == vector<string> {
kNodeTag, kInterfaceTag, kSignalTag, kArgumentTag }) {
AddSignalArgument(attributes);
}
}
void XmlInterfaceParser::AddMethodArgument(const XmlAttributeMap& attributes) {
CHECK(!interface_.methods.empty())
<< " we have a method argument but the interface has no methods";
string argument_direction;
bool is_direction_paramter_present = GetElementAttribute(
attributes,
string(kMethodTag) + " " + kArgumentTag,
kDirectionAttribute,
&argument_direction);
vector<Interface::Argument>* argument_list = nullptr;
if (!is_direction_paramter_present ||
argument_direction == kArgumentDirectionIn) {
argument_list = &interface_.methods.back().input_arguments;
} else if (argument_direction == kArgumentDirectionOut) {
argument_list = &interface_.methods.back().output_arguments;
} else {
LOG(FATAL) << "Unknown method argument direction " << argument_direction;
}
argument_list->push_back(ParseArgument(attributes, kMethodTag));
}
void XmlInterfaceParser::AddSignalArgument(const XmlAttributeMap& attributes) {
CHECK(interface_.signals.size())
<< " we have a signal argument but the interface has no signals";
interface_.signals.back().arguments.push_back(
ParseArgument(attributes, kSignalTag));
}
void XmlInterfaceParser::OnCloseElement(const string& element_name) {
VLOG(1) << "Close Element " << element_name;
CHECK(!element_path_.empty());
CHECK_EQ(element_path_.back(), element_name);
element_path_.pop_back();
}
// static
bool XmlInterfaceParser::GetElementAttribute(
const XmlAttributeMap& attributes,
const string& element_type,
const string& element_key,
string* element_value) {
if (!ContainsKey(attributes, element_key)) {
return false;
}
*element_value = attributes.find(element_key)->second;
VLOG(1) << "Got " << element_type << " element with "
<< element_key << " = " << *element_value;
return true;
}
// static
string XmlInterfaceParser::GetValidatedElementAttribute(
const XmlAttributeMap& attributes,
const string& element_type,
const string& element_key) {
string element_value;
CHECK(GetElementAttribute(attributes,
element_type,
element_key,
&element_value))
<< element_type << " does not contain a " << element_key << " attribute";
CHECK(!element_value.empty()) << element_type << " " << element_key
<< " attribute is empty";
return element_value;
}
// static
string XmlInterfaceParser::GetValidatedElementName(
const XmlAttributeMap& attributes,
const string& element_type) {
return GetValidatedElementAttribute(attributes, element_type, kNameAttribute);
}
// static
Interface::Argument XmlInterfaceParser::ParseArgument(
const XmlAttributeMap& attributes, const string& element_type) {
string element_and_argument = element_type + " " + kArgumentTag;
string argument_name;
// Since the "name" field is optional, use the un-validated variant.
GetElementAttribute(attributes,
element_and_argument,
kNameAttribute,
&argument_name);
string argument_type = GetValidatedElementAttribute(
attributes, element_and_argument, kTypeAttribute);
return Interface::Argument(argument_name, argument_type);
}
// static
void XmlInterfaceParser::HandleElementStart(void* user_data,
const XML_Char* element,
const XML_Char** attr) {
XmlAttributeMap attributes;
if (attr != nullptr) {
for (size_t n = 0; attr[n] != nullptr && attr[n+1] != nullptr; n += 2) {
auto key = attr[n];
auto value = attr[n + 1];
attributes.insert(std::make_pair(key, value));
}
}
auto parser = reinterpret_cast<XmlInterfaceParser*>(user_data);
parser->OnOpenElement(element, attributes);
}
// static
void XmlInterfaceParser::HandleElementEnd(void* user_data,
const XML_Char* element) {
auto parser = reinterpret_cast<XmlInterfaceParser*>(user_data);
parser->OnCloseElement(element);
}
} // namespace chromeos_dbus_bindings