summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Wiley <wiley@google.com>2015-07-29 17:27:03 -0700
committerChristopher Wiley <wiley@google.com>2015-07-29 17:27:03 -0700
commite3c51041cd39e382b9d68184124a3dd1cfcb06d4 (patch)
tree06142e16477dcecf01e0c6acf56993e0ebbbd7e2
parentc4a3805076ae13371b94da9fdc98670226ef4706 (diff)
parent5d162e5e777d4f516e34343830628e34e753c0c6 (diff)
downloaddbus-binding-generator-e3c51041cd39e382b9d68184124a3dd1cfcb06d4.tar.gz
Merge branch 'rewrite-chromeos-dbus-bindings' into merge-chromeos-dbus-bindings
-rw-r--r--chromeos-dbus-bindings/adaptor_generator.cc500
-rw-r--r--chromeos-dbus-bindings/adaptor_generator.h79
-rw-r--r--chromeos-dbus-bindings/adaptor_generator_unittest.cc313
-rw-r--r--chromeos-dbus-bindings/chromeos-dbus-bindings.gyp89
-rw-r--r--chromeos-dbus-bindings/dbus_signature.cc245
-rw-r--r--chromeos-dbus-bindings/dbus_signature.h90
-rw-r--r--chromeos-dbus-bindings/dbus_signature_unittest.cc120
-rw-r--r--chromeos-dbus-bindings/generate_chromeos_dbus_bindings.cc215
-rw-r--r--chromeos-dbus-bindings/header_generator.cc70
-rw-r--r--chromeos-dbus-bindings/header_generator.h81
-rw-r--r--chromeos-dbus-bindings/indented_text.cc140
-rw-r--r--chromeos-dbus-bindings/indented_text.h70
-rw-r--r--chromeos-dbus-bindings/indented_text_unittest.cc183
-rw-r--r--chromeos-dbus-bindings/interface.h83
-rw-r--r--chromeos-dbus-bindings/method_name_generator.cc46
-rw-r--r--chromeos-dbus-bindings/method_name_generator.h39
-rw-r--r--chromeos-dbus-bindings/method_name_generator_unittest.cc69
-rw-r--r--chromeos-dbus-bindings/name_parser.cc98
-rw-r--r--chromeos-dbus-bindings/name_parser.h63
-rw-r--r--chromeos-dbus-bindings/name_parser_unittest.cc123
-rw-r--r--chromeos-dbus-bindings/proxy_generator.cc1270
-rw-r--r--chromeos-dbus-bindings/proxy_generator.h188
-rw-r--r--chromeos-dbus-bindings/proxy_generator_mock_unittest.cc197
-rw-r--r--chromeos-dbus-bindings/proxy_generator_unittest.cc1389
-rw-r--r--chromeos-dbus-bindings/test_utils.cc81
-rw-r--r--chromeos-dbus-bindings/test_utils.h33
-rw-r--r--chromeos-dbus-bindings/testrunner.cc16
-rw-r--r--chromeos-dbus-bindings/xml_interface_parser.cc351
-rw-r--r--chromeos-dbus-bindings/xml_interface_parser.h136
-rw-r--r--chromeos-dbus-bindings/xml_interface_parser_unittest.cc129
30 files changed, 6506 insertions, 0 deletions
diff --git a/chromeos-dbus-bindings/adaptor_generator.cc b/chromeos-dbus-bindings/adaptor_generator.cc
new file mode 100644
index 0000000..44897a1
--- /dev/null
+++ b/chromeos-dbus-bindings/adaptor_generator.cc
@@ -0,0 +1,500 @@
+// 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/adaptor_generator.h"
+
+#include <string>
+
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <chromeos/strings/string_utils.h>
+
+#include "chromeos-dbus-bindings/dbus_signature.h"
+#include "chromeos-dbus-bindings/indented_text.h"
+#include "chromeos-dbus-bindings/interface.h"
+#include "chromeos-dbus-bindings/name_parser.h"
+
+using base::StringPrintf;
+using std::string;
+using std::vector;
+
+namespace chromeos_dbus_bindings {
+
+// static
+bool AdaptorGenerator::GenerateAdaptors(
+ const std::vector<Interface>& interfaces,
+ const base::FilePath& output_file) {
+ IndentedText text;
+ CHECK(!interfaces.empty()) << "At least one interface must be provided";
+
+ text.AddLine("// Automatic generation of D-Bus interfaces:");
+ for (const auto& interface : interfaces) {
+ text.AddLine(StringPrintf("// - %s", interface.name.c_str()));
+ }
+ string header_guard = GenerateHeaderGuard(output_file);
+ text.AddLine(StringPrintf("#ifndef %s", header_guard.c_str()));
+ text.AddLine(StringPrintf("#define %s", header_guard.c_str()));
+ text.AddLine("#include <memory>");
+ text.AddLine("#include <string>");
+ text.AddLine("#include <tuple>");
+ text.AddLine("#include <vector>");
+ text.AddBlankLine();
+ text.AddLine("#include <base/macros.h>");
+ text.AddLine("#include <dbus/object_path.h>");
+ text.AddLine("#include <chromeos/any.h>");
+ text.AddLine("#include <chromeos/dbus/dbus_object.h>");
+ text.AddLine("#include <chromeos/dbus/exported_object_manager.h>");
+ text.AddLine("#include <chromeos/variant_dictionary.h>");
+
+ for (const auto& interface : interfaces)
+ GenerateInterfaceAdaptor(interface, &text);
+
+ text.AddLine(StringPrintf("#endif // %s", header_guard.c_str()));
+
+ return WriteTextToFile(output_file, text);
+}
+
+// static
+void AdaptorGenerator::GenerateInterfaceAdaptor(
+ const Interface& interface,
+ IndentedText *text) {
+ NameParser parser{interface.name};
+ string itf_name = parser.MakeInterfaceName(false);
+ string class_name = parser.MakeAdaptorName(false);
+ string full_itf_name = parser.MakeFullCppName();
+
+ text->AddBlankLine();
+ parser.AddOpenNamespaces(text, false);
+
+ text->AddBlankLine();
+ text->AddLine(StringPrintf("// Interface definition for %s.",
+ full_itf_name.c_str()));
+ text->AddComments(interface.doc_string);
+ text->AddLine(StringPrintf("class %s {", itf_name.c_str()));
+ text->AddLineWithOffset("public:", kScopeOffset);
+ text->PushOffset(kBlockOffset);
+ text->AddLine(StringPrintf("virtual ~%s() = default;", itf_name.c_str()));
+ AddInterfaceMethods(interface, text);
+ text->PopOffset();
+ text->AddLine("};");
+
+ text->AddBlankLine();
+ text->AddLine(StringPrintf("// Interface adaptor for %s.",
+ full_itf_name.c_str()));
+ text->AddLine(StringPrintf("class %s {", class_name.c_str()));
+ text->AddLineWithOffset("public:", kScopeOffset);
+ text->PushOffset(kBlockOffset);
+ AddConstructor(class_name, itf_name, text);
+ AddRegisterWithDBusObject(itf_name, interface, text);
+ AddSendSignalMethods(interface, text);
+ AddPropertyMethodImplementation(interface, text);
+ if (!interface.path.empty()) {
+ text->AddBlankLine();
+ text->AddLine("static dbus::ObjectPath GetObjectPath() {");
+ text->PushOffset(kBlockOffset);
+ text->AddLine(StringPrintf("return dbus::ObjectPath{\"%s\"};",
+ interface.path.c_str()));
+ text->PopOffset();
+ text->AddLine("}");
+ }
+ text->PopOffset();
+
+ text->AddBlankLine();
+ text->AddLineWithOffset("private:", kScopeOffset);
+ text->PushOffset(kBlockOffset);
+ AddSignalDataMembers(interface, text);
+ AddPropertyDataMembers(interface, text);
+
+ text->AddLine(StringPrintf(
+ "%s* interface_; // Owned by container of this adapter.",
+ itf_name.c_str()));
+
+ text->AddBlankLine();
+ text->AddLine(StringPrintf("DISALLOW_COPY_AND_ASSIGN(%s);",
+ class_name.c_str()));
+ text->PopOffset();
+ text->AddLine("};");
+
+ text->AddBlankLine();
+ parser.AddCloseNamespaces(text, false);
+}
+
+// static
+void AdaptorGenerator::AddConstructor(const string& class_name,
+ const string& itf_name,
+ IndentedText *text) {
+ text->AddLine(StringPrintf("%s(%s* interface) : interface_(interface) {}",
+ class_name.c_str(), itf_name.c_str()));
+}
+
+// static
+void AdaptorGenerator::AddRegisterWithDBusObject(
+ const std::string& itf_name,
+ const Interface& interface,
+ IndentedText *text) {
+ text->AddBlankLine();
+ text->AddLine(
+ "void RegisterWithDBusObject(chromeos::dbus_utils::DBusObject* object) {");
+ text->PushOffset(kBlockOffset);
+ text->AddLine("chromeos::dbus_utils::DBusInterface* itf =");
+ text->AddLineWithOffset(
+ StringPrintf("object->AddOrGetInterface(\"%s\");",
+ interface.name.c_str()), kLineContinuationOffset);
+ RegisterInterface(itf_name, interface, text);
+ text->PopOffset();
+ text->AddLine("}");
+}
+
+// static
+void AdaptorGenerator::RegisterInterface(const string& itf_name,
+ const Interface& interface,
+ IndentedText *text) {
+ if (!interface.methods.empty())
+ text->AddBlankLine();
+ for (const auto& method : interface.methods) {
+ string add_handler_name;
+ switch (method.kind) {
+ case Interface::Method::Kind::kSimple:
+ add_handler_name = "AddSimpleMethodHandler";
+ break;
+ case Interface::Method::Kind::kNormal:
+ if (method.include_dbus_message)
+ add_handler_name = "AddSimpleMethodHandlerWithErrorAndMessage";
+ else
+ add_handler_name = "AddSimpleMethodHandlerWithError";
+ break;
+ case Interface::Method::Kind::kAsync:
+ if (method.include_dbus_message)
+ add_handler_name = "AddMethodHandlerWithMessage";
+ else
+ add_handler_name = "AddMethodHandler";
+ break;
+ case Interface::Method::Kind::kRaw:
+ add_handler_name = "AddRawMethodHandler";
+ break;
+ }
+
+ text->AddLine(StringPrintf("itf->%s(", add_handler_name.c_str()));
+ text->PushOffset(kLineContinuationOffset);
+ text->AddLine(StringPrintf("\"%s\",", method.name.c_str()));
+ text->AddLine("base::Unretained(interface_),");
+ text->AddLine(StringPrintf("&%s::%s);", itf_name.c_str(),
+ method.name.c_str()));
+ text->PopOffset();
+ }
+
+ // Register signals.
+ if (!interface.signals.empty())
+ text->AddBlankLine();
+ for (const auto& signal : interface.signals) {
+ string signal_var_name = StringPrintf("signal_%s_", signal.name.c_str());
+ string signal_type_name = StringPrintf("Signal%sType", signal.name.c_str());
+ text->AddLine(StringPrintf("%s = itf->RegisterSignalOfType<%s>(\"%s\");",
+ signal_var_name.c_str(),
+ signal_type_name.c_str(),
+ signal.name.c_str()));
+ }
+
+ // Register exported properties.
+ if (!interface.properties.empty())
+ text->AddBlankLine();
+ for (const auto& property : interface.properties) {
+ string variable_name = NameParser{property.name}.MakeVariableName();
+ string write_access;
+ if (property.access == "write") {
+ write_access = "kWriteOnly";
+ } else if (property.access == "readwrite") {
+ write_access = "kReadWrite";
+ }
+ if (!write_access.empty()) {
+ text->AddLine(StringPrintf("%s_.SetAccessMode(", variable_name.c_str()));
+ text->PushOffset(kLineContinuationOffset);
+ text->AddLine(
+ StringPrintf(
+ "chromeos::dbus_utils::ExportedPropertyBase::Access::%s);",
+ write_access.c_str()));
+ text->PopOffset();
+ text->AddLine(StringPrintf("%s_.SetValidator(", variable_name.c_str()));
+ text->PushOffset(kLineContinuationOffset);
+ text->AddLineAndPushOffsetTo(
+ StringPrintf(
+ "base::Bind(&%s::Validate%s,",
+ NameParser{interface.name}.MakeAdaptorName(false).c_str(),
+ property.name.c_str()),
+ 1, '(');
+ text->AddLine("base::Unretained(this)));");
+ text->PopOffset();
+ text->PopOffset();
+ }
+ text->AddLine(StringPrintf("itf->AddProperty(%sName(), &%s_);",
+ property.name.c_str(), variable_name.c_str()));
+ }
+}
+
+// static
+void AdaptorGenerator::AddInterfaceMethods(const Interface& interface,
+ IndentedText *text) {
+ IndentedText block;
+ DbusSignature signature;
+ if (!interface.methods.empty())
+ block.AddBlankLine();
+
+ for (const auto& method : interface.methods) {
+ string const_method;
+ if (method.is_const)
+ const_method = " const";
+
+ string return_type = "void";
+ vector<string> method_params;
+ auto input_arguments_copy = method.input_arguments;
+ auto output_arguments_copy = method.output_arguments;
+ switch (method.kind) {
+ case Interface::Method::Kind::kSimple:
+ if (output_arguments_copy.size() == 1) {
+ CHECK(signature.Parse(output_arguments_copy[0].type, &return_type));
+ output_arguments_copy.clear();
+ }
+ break;
+ case Interface::Method::Kind::kNormal:
+ method_params.push_back("chromeos::ErrorPtr* error");
+ if (method.include_dbus_message)
+ method_params.push_back("dbus::Message* message");
+ return_type = "bool";
+ break;
+ case Interface::Method::Kind::kAsync: {
+ std::vector<std::string> out_types;
+ for (const auto& argument : output_arguments_copy) {
+ string param_type;
+ CHECK(signature.Parse(argument.type, &param_type));
+ out_types.push_back(param_type);
+ }
+ method_params.push_back(base::StringPrintf(
+ "std::unique_ptr<chromeos::dbus_utils::DBusMethodResponse<%s>> "
+ "response",
+ chromeos::string_utils::Join(", ", out_types).c_str()));
+ if (method.include_dbus_message)
+ method_params.push_back("dbus::Message* message");
+ output_arguments_copy.clear();
+ break;
+ }
+ case Interface::Method::Kind::kRaw:
+ method_params.push_back("dbus::MethodCall* method_call");
+ method_params.push_back("chromeos::dbus_utils::ResponseSender sender");
+ // Raw methods don't take static parameters or return values directly.
+ input_arguments_copy.clear();
+ output_arguments_copy.clear();
+ break;
+ }
+ block.AddComments(method.doc_string);
+ string method_start = StringPrintf("virtual %s %s(",
+ return_type.c_str(),
+ method.name.c_str());
+ string method_end = StringPrintf(")%s = 0;", const_method.c_str());
+ int index = 0;
+ for (const auto& argument : input_arguments_copy) {
+ string param_type;
+ CHECK(signature.Parse(argument.type, &param_type));
+ MakeConstReferenceIfNeeded(&param_type);
+ string param_name = GetArgName("in", argument.name, ++index);
+ method_params.push_back(param_type + ' ' + param_name);
+ }
+
+ for (const auto& argument : output_arguments_copy) {
+ string param_type;
+ CHECK(signature.Parse(argument.type, &param_type));
+ string param_name = GetArgName("out", argument.name, ++index);
+ method_params.push_back(param_type + "* " + param_name);
+ }
+
+ if (method_params.empty()) {
+ block.AddLine(method_start + method_end);
+ } else {
+ block.AddLine(method_start);
+ block.PushOffset(kLineContinuationOffset);
+ for (size_t i = 0; i < method_params.size() - 1; i++)
+ block.AddLine(method_params[i] + ',');
+ block.AddLine(method_params.back() + method_end);
+ block.PopOffset();
+ }
+ }
+ text->AddBlock(block);
+}
+
+// static
+void AdaptorGenerator::AddSendSignalMethods(
+ const Interface& interface,
+ IndentedText *text) {
+ IndentedText block;
+ DbusSignature signature;
+
+ if (!interface.signals.empty())
+ block.AddBlankLine();
+
+ for (const auto& signal : interface.signals) {
+ block.AddComments(signal.doc_string);
+ string method_start = StringPrintf("void Send%sSignal(",
+ signal.name.c_str());
+ string method_end = ") {";
+
+ int index = 0;
+ vector<string> method_params;
+ vector<string> param_names;
+ for (const auto& argument : signal.arguments) {
+ string param_type;
+ CHECK(signature.Parse(argument.type, &param_type));
+ MakeConstReferenceIfNeeded(&param_type);
+ string param_name = GetArgName("in", argument.name, ++index);
+ param_names.push_back(param_name);
+ method_params.push_back(param_type + ' ' + param_name);
+ }
+
+ if (method_params.empty()) {
+ block.AddLine(method_start + method_end);
+ } else {
+ block.AddLine(method_start);
+ block.PushOffset(kLineContinuationOffset);
+ for (size_t i = 0; i < method_params.size() - 1; i++)
+ block.AddLine(method_params[i] + ',');
+ block.AddLine(method_params.back() + method_end);
+ block.PopOffset();
+ }
+
+ string args = chromeos::string_utils::Join(", ", param_names);
+ block.PushOffset(kBlockOffset);
+ block.AddLine(StringPrintf("auto signal = signal_%s_.lock();",
+ signal.name.c_str()));
+ block.AddLine("if (signal)");
+ block.AddLineWithOffset(StringPrintf("signal->Send(%s);", args.c_str()),
+ kBlockOffset);
+ block.PopOffset();
+ block.AddLine("}");
+ }
+ text->AddBlock(block);
+}
+
+// static
+void AdaptorGenerator::AddSignalDataMembers(const Interface& interface,
+ IndentedText *text) {
+ IndentedText block;
+ DbusSignature signature;
+
+ for (const auto& signal : interface.signals) {
+ string signal_type_name = StringPrintf("Signal%sType", signal.name.c_str());
+ string signal_type_alias_begin =
+ StringPrintf("using %s = chromeos::dbus_utils::DBusSignal<",
+ signal_type_name.c_str());
+ string signal_type_alias_end = ">;";
+ vector<string> params;
+ for (const auto& argument : signal.arguments) {
+ string param;
+ CHECK(signature.Parse(argument.type, &param));
+ if (!argument.name.empty())
+ base::StringAppendF(&param, " /*%s*/", argument.name.c_str());
+ params.push_back(param);
+ }
+ if (params.empty()) {
+ block.AddLine(signal_type_alias_begin + signal_type_alias_end);
+ } else {
+ block.AddLine(signal_type_alias_begin);
+ block.PushOffset(kLineContinuationOffset);
+ for (size_t i = 0; i < params.size() - 1; i++)
+ block.AddLine(params[i] + ',');
+ block.AddLine(params.back() + signal_type_alias_end);
+ block.PopOffset();
+ }
+ block.AddLine(
+ StringPrintf("std::weak_ptr<%s> signal_%s_;",
+ signal_type_name.c_str(), signal.name.c_str()));
+ block.AddBlankLine();
+ }
+ text->AddBlock(block);
+}
+
+// static
+void AdaptorGenerator::AddPropertyMethodImplementation(
+ const Interface& interface,
+ IndentedText *text) {
+ IndentedText block;
+ DbusSignature signature;
+
+ for (const auto& property : interface.properties) {
+ block.AddBlankLine();
+ string type;
+ CHECK(signature.Parse(property.type, &type));
+ string variable_name = NameParser{property.name}.MakeVariableName();
+
+ // Property name accessor.
+ block.AddComments(property.doc_string);
+ block.AddLine(StringPrintf("static const char* %sName() { return \"%s\"; }",
+ property.name.c_str(), property.name.c_str()));
+
+ // Getter method.
+ block.AddLine(StringPrintf("%s Get%s() const {",
+ type.c_str(),
+ property.name.c_str()));
+ block.PushOffset(kBlockOffset);
+ block.AddLine(StringPrintf("return %s_.GetValue().Get<%s>();",
+ variable_name.c_str(),
+ type.c_str()));
+ block.PopOffset();
+ block.AddLine("}");
+
+ // Setter method.
+ MakeConstReferenceIfNeeded(&type);
+ block.AddLine(StringPrintf("void Set%s(%s %s) {",
+ property.name.c_str(),
+ type.c_str(),
+ variable_name.c_str()));
+ block.PushOffset(kBlockOffset);
+ block.AddLine(StringPrintf("%s_.SetValue(%s);",
+ variable_name.c_str(),
+ variable_name.c_str()));
+ block.PopOffset();
+ block.AddLine("}");
+
+ // Validation method for property with write access.
+ if (property.access != "read") {
+ CHECK(signature.Parse(property.type, &type));
+ block.AddLine(StringPrintf("virtual bool Validate%s(",
+ property.name.c_str()));
+ block.PushOffset(kLineContinuationOffset);
+ // Explicitly specify the "value" parameter as const & to match the
+ // validator callback function signature.
+ block.AddLine(
+ StringPrintf(
+ "chromeos::ErrorPtr* /*error*/, const %s& /*value*/) {",
+ type.c_str()));
+ block.PopOffset();
+ block.PushOffset(kBlockOffset);
+ block.AddLine("return true;");
+ block.PopOffset();
+ block.AddLine("}");
+ }
+ }
+ text->AddBlock(block);
+}
+
+// static
+void AdaptorGenerator::AddPropertyDataMembers(const Interface& interface,
+ IndentedText *text) {
+ IndentedText block;
+ DbusSignature signature;
+
+ for (const auto& property : interface.properties) {
+ string type;
+ CHECK(signature.Parse(property.type, &type));
+ string variable_name = NameParser{property.name}.MakeVariableName();
+
+ block.AddLine(
+ StringPrintf("chromeos::dbus_utils::ExportedProperty<%s> %s_;",
+ type.c_str(), variable_name.c_str()));
+ }
+ if (!interface.properties.empty())
+ block.AddBlankLine();
+
+ text->AddBlock(block);
+}
+
+} // namespace chromeos_dbus_bindings
diff --git a/chromeos-dbus-bindings/adaptor_generator.h b/chromeos-dbus-bindings/adaptor_generator.h
new file mode 100644
index 0000000..57e2bf5
--- /dev/null
+++ b/chromeos-dbus-bindings/adaptor_generator.h
@@ -0,0 +1,79 @@
+// 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.
+
+#ifndef CHROMEOS_DBUS_BINDINGS_ADAPTOR_GENERATOR_H_
+#define CHROMEOS_DBUS_BINDINGS_ADAPTOR_GENERATOR_H_
+
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+
+#include "chromeos-dbus-bindings/header_generator.h"
+#include "chromeos-dbus-bindings/indented_text.h"
+
+namespace base {
+
+class FilePath;
+
+} // namespace base
+
+namespace chromeos_dbus_bindings {
+
+class IndentedText;
+struct Interface;
+
+class AdaptorGenerator : public HeaderGenerator {
+ public:
+ static bool GenerateAdaptors(const std::vector<Interface>& interfaces,
+ const base::FilePath& output_file);
+
+ private:
+ friend class AdaptorGeneratorTest;
+
+ // Generates one interface adaptor.
+ static void GenerateInterfaceAdaptor(const Interface& interface,
+ IndentedText *text);
+
+ // Generates the method prototypes for an interface declaration.
+ static void AddInterfaceMethods(const Interface& interface,
+ IndentedText *text);
+
+ // Generates the constructor for the adaptor.
+ static void AddConstructor(const std::string& class_name,
+ const std::string& itf_name,
+ IndentedText *text);
+
+ // Generates RegisterWithDBusObject() method.
+ static void AddRegisterWithDBusObject(const std::string& itf_name,
+ const Interface& interface,
+ IndentedText *text);
+
+ // Generates the code to register the interface with a D-Bus object.
+ static void RegisterInterface(const std::string& itf_name,
+ const Interface& interface,
+ IndentedText *text);
+
+ // Generates adaptor methods to send the signals.
+ static void AddSendSignalMethods(const Interface& interface,
+ IndentedText *text);
+
+ // Generates DBusSignal data members for the signals.
+ static void AddSignalDataMembers(const Interface& interface,
+ IndentedText *text);
+
+ // Generates adaptor accessor methods for the properties.
+ static void AddPropertyMethodImplementation(const Interface& interface,
+ IndentedText *text);
+
+ // Generate ExportProperty data members for the properties.
+ static void AddPropertyDataMembers(const Interface& interface,
+ IndentedText *text);
+
+ DISALLOW_COPY_AND_ASSIGN(AdaptorGenerator);
+};
+
+} // namespace chromeos_dbus_bindings
+
+#endif // CHROMEOS_DBUS_BINDINGS_ADAPTOR_GENERATOR_H_
diff --git a/chromeos-dbus-bindings/adaptor_generator_unittest.cc b/chromeos-dbus-bindings/adaptor_generator_unittest.cc
new file mode 100644
index 0000000..9d9fad7
--- /dev/null
+++ b/chromeos-dbus-bindings/adaptor_generator_unittest.cc
@@ -0,0 +1,313 @@
+// 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/adaptor_generator.h"
+
+#include <string>
+#include <vector>
+
+#include <base/files/file_path.h>
+#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+#include <gtest/gtest.h>
+
+#include "chromeos-dbus-bindings/interface.h"
+#include "chromeos-dbus-bindings/test_utils.h"
+
+using std::string;
+using std::vector;
+using testing::Test;
+
+namespace chromeos_dbus_bindings {
+
+namespace {
+
+const char kDBusTypeArryOfObjects[] = "ao";
+const char kDBusTypeBool[] = "b";
+const char kDBusTypeInt32[] = "i";
+const char kDBusTypeInt64[] = "x";
+const char kDBusTypeString[] = "s";
+
+const char kPropertyAccessReadOnly[] = "read";
+const char kPropertyAccessReadWrite[] = "readwrite";
+
+const char kInterfaceName[] = "org.chromium.Test";
+const char kInterfaceName2[] = "org.chromium.Test2";
+
+const char kExpectedContent[] = R"literal_string(
+#include <memory>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include <base/macros.h>
+#include <dbus/object_path.h>
+#include <chromeos/any.h>
+#include <chromeos/dbus/dbus_object.h>
+#include <chromeos/dbus/exported_object_manager.h>
+#include <chromeos/variant_dictionary.h>
+
+namespace org {
+namespace chromium {
+
+// Interface definition for org::chromium::Test.
+class TestInterface {
+ public:
+ virtual ~TestInterface() = default;
+
+ virtual bool Kaneda(
+ chromeos::ErrorPtr* error,
+ dbus::Message* message,
+ const std::string& in_iwata,
+ const std::vector<dbus::ObjectPath>& in_clarke,
+ std::string* out_3) = 0;
+ virtual bool Tetsuo(
+ chromeos::ErrorPtr* error,
+ int32_t in_1,
+ int64_t* out_2) = 0;
+ virtual bool Kei(
+ chromeos::ErrorPtr* error) = 0;
+ virtual bool Kiyoko(
+ chromeos::ErrorPtr* error,
+ int64_t* out_akira,
+ std::string* out_2) = 0;
+};
+
+// Interface adaptor for org::chromium::Test.
+class TestAdaptor {
+ public:
+ TestAdaptor(TestInterface* interface) : interface_(interface) {}
+
+ void RegisterWithDBusObject(chromeos::dbus_utils::DBusObject* object) {
+ chromeos::dbus_utils::DBusInterface* itf =
+ object->AddOrGetInterface("org.chromium.Test");
+
+ itf->AddSimpleMethodHandlerWithErrorAndMessage(
+ "Kaneda",
+ base::Unretained(interface_),
+ &TestInterface::Kaneda);
+ itf->AddSimpleMethodHandlerWithError(
+ "Tetsuo",
+ base::Unretained(interface_),
+ &TestInterface::Tetsuo);
+ itf->AddSimpleMethodHandlerWithError(
+ "Kei",
+ base::Unretained(interface_),
+ &TestInterface::Kei);
+ itf->AddSimpleMethodHandlerWithError(
+ "Kiyoko",
+ base::Unretained(interface_),
+ &TestInterface::Kiyoko);
+
+ signal_Update_ = itf->RegisterSignalOfType<SignalUpdateType>("Update");
+ signal_Mapping_ = itf->RegisterSignalOfType<SignalMappingType>("Mapping");
+
+ itf->AddProperty(CharacterNameName(), &character_name_);
+ write_property_.SetAccessMode(
+ chromeos::dbus_utils::ExportedPropertyBase::Access::kReadWrite);
+ write_property_.SetValidator(
+ base::Bind(&TestAdaptor::ValidateWriteProperty,
+ base::Unretained(this)));
+ itf->AddProperty(WritePropertyName(), &write_property_);
+ }
+
+ void SendUpdateSignal() {
+ auto signal = signal_Update_.lock();
+ if (signal)
+ signal->Send();
+ }
+ void SendMappingSignal(
+ const std::string& in_key,
+ const std::vector<dbus::ObjectPath>& in_2) {
+ auto signal = signal_Mapping_.lock();
+ if (signal)
+ signal->Send(in_key, in_2);
+ }
+
+ static const char* CharacterNameName() { return "CharacterName"; }
+ std::string GetCharacterName() const {
+ return character_name_.GetValue().Get<std::string>();
+ }
+ void SetCharacterName(const std::string& character_name) {
+ character_name_.SetValue(character_name);
+ }
+
+ static const char* WritePropertyName() { return "WriteProperty"; }
+ std::string GetWriteProperty() const {
+ return write_property_.GetValue().Get<std::string>();
+ }
+ void SetWriteProperty(const std::string& write_property) {
+ write_property_.SetValue(write_property);
+ }
+ virtual bool ValidateWriteProperty(
+ chromeos::ErrorPtr* /*error*/, const std::string& /*value*/) {
+ return true;
+ }
+
+ static dbus::ObjectPath GetObjectPath() {
+ return dbus::ObjectPath{"/org/chromium/Test"};
+ }
+
+ private:
+ using SignalUpdateType = chromeos::dbus_utils::DBusSignal<>;
+ std::weak_ptr<SignalUpdateType> signal_Update_;
+
+ using SignalMappingType = chromeos::dbus_utils::DBusSignal<
+ std::string /*key*/,
+ std::vector<dbus::ObjectPath>>;
+ std::weak_ptr<SignalMappingType> signal_Mapping_;
+
+ chromeos::dbus_utils::ExportedProperty<std::string> character_name_;
+ chromeos::dbus_utils::ExportedProperty<std::string> write_property_;
+
+ TestInterface* interface_; // Owned by container of this adapter.
+
+ DISALLOW_COPY_AND_ASSIGN(TestAdaptor);
+};
+
+} // namespace chromium
+} // namespace org
+
+namespace org {
+namespace chromium {
+
+// Interface definition for org::chromium::Test2.
+class Test2Interface {
+ public:
+ virtual ~Test2Interface() = default;
+
+ virtual std::string Kaneda2(
+ const std::string& in_iwata) const = 0;
+ virtual void Tetsuo2(
+ std::unique_ptr<chromeos::dbus_utils::DBusMethodResponse<int64_t>> response,
+ int32_t in_1) = 0;
+ virtual void Kei2(
+ std::unique_ptr<chromeos::dbus_utils::DBusMethodResponse<bool>> response,
+ dbus::Message* message) = 0;
+};
+
+// Interface adaptor for org::chromium::Test2.
+class Test2Adaptor {
+ public:
+ Test2Adaptor(Test2Interface* interface) : interface_(interface) {}
+
+ void RegisterWithDBusObject(chromeos::dbus_utils::DBusObject* object) {
+ chromeos::dbus_utils::DBusInterface* itf =
+ object->AddOrGetInterface("org.chromium.Test2");
+
+ itf->AddSimpleMethodHandler(
+ "Kaneda2",
+ base::Unretained(interface_),
+ &Test2Interface::Kaneda2);
+ itf->AddMethodHandler(
+ "Tetsuo2",
+ base::Unretained(interface_),
+ &Test2Interface::Tetsuo2);
+ itf->AddMethodHandlerWithMessage(
+ "Kei2",
+ base::Unretained(interface_),
+ &Test2Interface::Kei2);
+ }
+
+ private:
+ Test2Interface* interface_; // Owned by container of this adapter.
+
+ DISALLOW_COPY_AND_ASSIGN(Test2Adaptor);
+};
+
+} // namespace chromium
+} // namespace org
+)literal_string";
+
+} // namespace
+class AdaptorGeneratorTest : public Test {
+ public:
+ void SetUp() override {
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ }
+
+ protected:
+ base::FilePath CreateInputFile(const string& contents) {
+ base::FilePath path;
+ EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(), &path));
+ int written = base::WriteFile(path, contents.c_str(), contents.size());
+ EXPECT_EQ(contents.size(), static_cast<size_t>(written));
+ return path;
+ }
+
+ base::ScopedTempDir temp_dir_;
+};
+
+TEST_F(AdaptorGeneratorTest, GenerateAdaptors) {
+ Interface interface;
+ interface.name = kInterfaceName;
+ interface.path = "/org/chromium/Test";
+ interface.methods.emplace_back(
+ "Kaneda",
+ vector<Interface::Argument>{
+ {"iwata", kDBusTypeString},
+ {"clarke", kDBusTypeArryOfObjects}},
+ vector<Interface::Argument>{{"", kDBusTypeString}});
+ interface.methods.back().include_dbus_message = true;
+ interface.methods.emplace_back(
+ "Tetsuo",
+ vector<Interface::Argument>{{"", kDBusTypeInt32}},
+ vector<Interface::Argument>{{"", kDBusTypeInt64}});
+ interface.methods.emplace_back("Kei");
+ // Interface methods with more than one return argument should be ignored.
+ interface.methods.emplace_back(
+ "Kiyoko",
+ vector<Interface::Argument>{},
+ vector<Interface::Argument>{
+ {"akira", kDBusTypeInt64},
+ {"", kDBusTypeString}});
+ // Signals generate helper methods to send them.
+ interface.signals.emplace_back(
+ "Update",
+ vector<Interface::Argument>{});
+ interface.signals.emplace_back(
+ "Mapping",
+ vector<Interface::Argument>{
+ {"key", kDBusTypeString},
+ {"", kDBusTypeArryOfObjects}});
+ interface.properties.emplace_back(
+ "CharacterName",
+ kDBusTypeString,
+ kPropertyAccessReadOnly);
+ interface.properties.emplace_back(
+ "WriteProperty",
+ kDBusTypeString,
+ kPropertyAccessReadWrite);
+
+ Interface interface2;
+ interface2.name = kInterfaceName2;
+ interface2.methods.emplace_back(
+ "Kaneda2",
+ vector<Interface::Argument>{{"iwata", kDBusTypeString}},
+ vector<Interface::Argument>{{"", kDBusTypeString}});
+ interface2.methods.back().is_const = true;
+ interface2.methods.back().kind = Interface::Method::Kind::kSimple;
+ interface2.methods.emplace_back(
+ "Tetsuo2",
+ vector<Interface::Argument>{{"", kDBusTypeInt32}},
+ vector<Interface::Argument>{{"", kDBusTypeInt64}});
+ interface2.methods.back().kind = Interface::Method::Kind::kAsync;
+ interface2.methods.emplace_back(
+ "Kei2",
+ vector<Interface::Argument>{},
+ vector<Interface::Argument>{{"", kDBusTypeBool}});
+ interface2.methods.back().kind = Interface::Method::Kind::kAsync;
+ interface2.methods.back().include_dbus_message = true;
+
+ base::FilePath output_path = temp_dir_.path().Append("output.h");
+ EXPECT_TRUE(AdaptorGenerator::GenerateAdaptors({interface, interface2},
+ output_path));
+ string contents;
+ EXPECT_TRUE(base::ReadFileToString(output_path, &contents));
+ // The header guards contain the (temporary) filename, so we search for
+ // the content we need within the string.
+ test_utils::EXPECT_TEXT_CONTAINED(kExpectedContent, contents);
+}
+
+} // namespace chromeos_dbus_bindings
diff --git a/chromeos-dbus-bindings/chromeos-dbus-bindings.gyp b/chromeos-dbus-bindings/chromeos-dbus-bindings.gyp
new file mode 100644
index 0000000..218edb7
--- /dev/null
+++ b/chromeos-dbus-bindings/chromeos-dbus-bindings.gyp
@@ -0,0 +1,89 @@
+{
+ 'target_defaults': {
+ 'variables': {
+ 'deps': [
+ 'libchrome-<(libbase_ver)',
+ 'libchromeos-<(libbase_ver)',
+ ],
+ },
+ 'cflags': [
+ '-Wextra',
+ '-Wno-unused-parameter', # for scoped_ptr.h, included indirectly
+ ],
+ 'cflags_cc': [
+ '-fno-strict-aliasing',
+ '-Woverloaded-virtual',
+ ],
+ },
+ 'targets': [
+ {
+ 'target_name': 'libchromeos-dbus-bindings',
+ 'type': 'static_library',
+ 'sources': [
+ 'adaptor_generator.cc',
+ 'dbus_signature.cc',
+ 'header_generator.cc',
+ 'indented_text.cc',
+ 'method_name_generator.cc',
+ 'name_parser.cc',
+ 'proxy_generator.cc',
+ 'xml_interface_parser.cc',
+ ],
+ 'variables': {
+ 'exported_deps': [
+ 'expat',
+ ],
+ 'deps': [
+ 'dbus-1',
+ '<@(exported_deps)',
+ ],
+ },
+ 'all_dependent_settings': {
+ 'variables': {
+ 'deps': [
+ '<@(exported_deps)',
+ ],
+ },
+ },
+ 'link_settings': {
+ 'variables': {
+ 'deps': [
+ 'expat',
+ ],
+ },
+ },
+ },
+ {
+ 'target_name': 'generate-chromeos-dbus-bindings',
+ 'type': 'executable',
+ 'dependencies': ['libchromeos-dbus-bindings'],
+ 'sources': [
+ 'generate_chromeos_dbus_bindings.cc',
+ ]
+ },
+ ],
+ 'conditions': [
+ ['USE_test == 1', {
+ 'targets': [
+ {
+ 'target_name': 'chromeos_dbus_bindings_unittest',
+ 'type': 'executable',
+ 'dependencies': ['libchromeos-dbus-bindings'],
+ 'includes': ['../common-mk/common_test.gypi'],
+ 'sources': [
+ 'testrunner.cc',
+ 'adaptor_generator_unittest.cc',
+ 'dbus_signature_unittest.cc',
+ 'indented_text_unittest.cc',
+ 'method_name_generator_unittest.cc',
+ 'name_parser_unittest.cc',
+ 'proxy_generator_mock_unittest.cc',
+ 'proxy_generator_unittest.cc',
+ 'test_utils.cc',
+ 'xml_interface_parser_unittest.cc',
+ ],
+ },
+ ],
+ }],
+ ],
+}
diff --git a/chromeos-dbus-bindings/dbus_signature.cc b/chromeos-dbus-bindings/dbus_signature.cc
new file mode 100644
index 0000000..a6e59d4
--- /dev/null
+++ b/chromeos-dbus-bindings/dbus_signature.cc
@@ -0,0 +1,245 @@
+// 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/dbus_signature.h"
+
+#include <base/logging.h>
+#include <base/strings/stringprintf.h>
+#include <chromeos/strings/string_utils.h>
+#include <dbus/dbus-protocol.h>
+
+using base::StringPrintf;
+using std::string;
+using std::vector;
+
+namespace chromeos_dbus_bindings {
+
+// static
+const char DbusSignature::kArrayTypename[] = "std::vector";
+const char DbusSignature::kBooleanTypename[] = "bool";
+const char DbusSignature::kByteTypename[] = "uint8_t";
+const char DbusSignature::kDefaultObjectPathTypename[] = "dbus::ObjectPath";
+const char DbusSignature::kDictTypename[] = "std::map";
+const char DbusSignature::kDoubleTypename[] = "double";
+const char DbusSignature::kSigned16Typename[] = "int16_t";
+const char DbusSignature::kSigned32Typename[] = "int32_t";
+const char DbusSignature::kSigned64Typename[] = "int64_t";
+const char DbusSignature::kStringTypename[] = "std::string";
+const char DbusSignature::kUnixFdTypename[] = "dbus::FileDescriptor";
+const char DbusSignature::kUnsigned16Typename[] = "uint16_t";
+const char DbusSignature::kUnsigned32Typename[] = "uint32_t";
+const char DbusSignature::kUnsigned64Typename[] = "uint64_t";
+const char DbusSignature::kVariantTypename[] = "chromeos::Any";
+const char DbusSignature::kVariantDictTypename[] =
+ "chromeos::VariantDictionary";
+const char DbusSignature::kTupleTypename[] = "std::tuple";
+
+DbusSignature::DbusSignature()
+ : object_path_typename_(kDefaultObjectPathTypename) {}
+
+bool DbusSignature::Parse(const string& signature, string* output) {
+ string::const_iterator end;
+ if (!GetTypenameForSignature(
+ signature.begin(), signature.end(), &end, output)) {
+ LOG(ERROR) << "Parse failed for signature " << signature;
+ return false;
+ }
+ if (end != signature.end()) {
+ LOG(WARNING) << "A portion of signature " << signature
+ << " is left unparsed: " << string(end, signature.end());
+ }
+ return true;
+}
+
+bool DbusSignature::GetTypenameForSignature(
+ string::const_iterator signature,
+ string::const_iterator end,
+ string::const_iterator* next,
+ string* output) {
+ if (signature == end) {
+ LOG(ERROR) << "Signature is empty";
+ return false;
+ }
+
+ string::const_iterator cur = signature;
+ int signature_value = *cur++;
+ switch (signature_value) {
+ case DBUS_STRUCT_BEGIN_CHAR:
+ if (!GetStructTypenameForSignature(cur, end, &cur, output)) {
+ return false;
+ }
+ break;
+
+ case DBUS_TYPE_ARRAY:
+ if (!GetArrayTypenameForSignature(cur, end, &cur, output)) {
+ return false;
+ }
+ break;
+
+ case DBUS_TYPE_BOOLEAN:
+ *output = kBooleanTypename;
+ break;
+
+ case DBUS_TYPE_BYTE:
+ *output = kByteTypename;
+ break;
+
+ case DBUS_TYPE_DOUBLE:
+ *output = kDoubleTypename;
+ break;
+
+ case DBUS_TYPE_OBJECT_PATH:
+ *output = object_path_typename_;
+ break;
+
+ case DBUS_TYPE_INT16:
+ *output = kSigned16Typename;
+ break;
+
+ case DBUS_TYPE_INT32:
+ *output = kSigned32Typename;
+ break;
+
+ case DBUS_TYPE_INT64:
+ *output = kSigned64Typename;
+ break;
+
+ case DBUS_TYPE_STRING:
+ *output = kStringTypename;
+ break;
+
+ case DBUS_TYPE_UNIX_FD:
+ *output = kUnixFdTypename;
+ break;
+
+ case DBUS_TYPE_UINT16:
+ *output = kUnsigned16Typename;
+ break;
+
+ case DBUS_TYPE_UINT32:
+ *output = kUnsigned32Typename;
+ break;
+
+ case DBUS_TYPE_UINT64:
+ *output = kUnsigned64Typename;
+ break;
+
+ case DBUS_TYPE_VARIANT:
+ *output = kVariantTypename;
+ break;
+
+ default:
+ LOG(ERROR) << "Unexpected token " << *signature;
+ return false;
+ }
+
+ if (next) {
+ *next = cur;
+ }
+
+ return true;
+}
+
+bool DbusSignature::GetArrayTypenameForSignature(
+ string::const_iterator signature,
+ string::const_iterator end,
+ string::const_iterator* next,
+ string* output) {
+ string::const_iterator cur = signature;
+ if (cur == end) {
+ LOG(ERROR) << "At end of string while reading array parameter";
+ return false;
+ }
+
+ if (*cur == DBUS_DICT_ENTRY_BEGIN_CHAR) {
+ vector<string> children;
+ ++cur;
+ while (cur != end && *cur != DBUS_DICT_ENTRY_END_CHAR) {
+ children.emplace_back();
+ if (!GetTypenameForSignature(cur, end, &cur, &children.back())) {
+ LOG(ERROR) << "Unable to decode child elements starting at "
+ << string(cur, end);
+ return false;
+ }
+ }
+ if (cur == end) {
+ LOG(ERROR) << "At end of string while processing dict "
+ << "starting at " << string(signature, end);
+ return false;
+ }
+
+ DCHECK_EQ(DBUS_DICT_ENTRY_END_CHAR, *cur);
+ ++cur;
+
+ if (children.size() != 2) {
+ LOG(ERROR) << "Dict entry contains " << children.size()
+ << " members starting at " << string(signature, end)
+ << " where only 2 children is valid.";
+ return false;
+ }
+ string dict_signature{signature, cur};
+ if (dict_signature == "{sv}") {
+ *output = kVariantDictTypename;
+ } else {
+ *output = StringPrintf("%s<%s, %s>", kDictTypename,
+ children[0].c_str(), children[1].c_str());
+ }
+ } else {
+ string child;
+ if (!GetTypenameForSignature(cur, end, &cur, &child)) {
+ LOG(ERROR) << "Unable to decode child element starting at "
+ << string(cur, end);
+ return false;
+ }
+ *output = StringPrintf("%s<%s>", kArrayTypename, child.c_str());
+ }
+
+ if (next) {
+ *next = cur;
+ }
+
+ return true;
+}
+
+bool DbusSignature::GetStructTypenameForSignature(
+ string::const_iterator signature,
+ string::const_iterator end,
+ string::const_iterator* next,
+ string* output) {
+ string::const_iterator cur = signature;
+ if (cur == end) {
+ LOG(ERROR) << "At end of string while reading struct parameter";
+ return false;
+ }
+
+ vector<string> children;
+ while (cur != end && *cur != DBUS_STRUCT_END_CHAR) {
+ children.emplace_back();
+ if (!GetTypenameForSignature(cur, end, &cur, &children.back())) {
+ LOG(ERROR) << "Unable to decode child elements starting at "
+ << string(cur, end);
+ return false;
+ }
+ }
+ if (cur == end) {
+ LOG(ERROR) << "At end of string while processing struct "
+ << "starting at " << string(signature, end);
+ return false;
+ }
+
+ DCHECK_EQ(DBUS_STRUCT_END_CHAR, *cur);
+ ++cur;
+
+ *output = StringPrintf(
+ "%s<%s>", kTupleTypename,
+ chromeos::string_utils::Join(", ", children).c_str());
+
+ if (next) {
+ *next = cur;
+ }
+
+ return true;
+}
+
+} // namespace chromeos_dbus_bindings
diff --git a/chromeos-dbus-bindings/dbus_signature.h b/chromeos-dbus-bindings/dbus_signature.h
new file mode 100644
index 0000000..6a5a10b
--- /dev/null
+++ b/chromeos-dbus-bindings/dbus_signature.h
@@ -0,0 +1,90 @@
+// 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.
+
+#ifndef CHROMEOS_DBUS_BINDINGS_DBUS_SIGNATURE_H_
+#define CHROMEOS_DBUS_BINDINGS_DBUS_SIGNATURE_H_
+
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+#include <gtest/gtest_prod.h> // for FRIEND_TEST
+
+namespace chromeos_dbus_bindings {
+
+class DbusSignature {
+ public:
+ DbusSignature();
+ virtual ~DbusSignature() = default;
+
+ // Returns a C++ typename in |output| for a D-Bus signature in |signature|
+ // and returns true on success. Returns false otherwise.
+ bool Parse(const std::string& signature, std::string* output);
+
+ void set_object_path_typename(const std::string& object_path_typename) {
+ object_path_typename_ = object_path_typename;
+ }
+
+ private:
+ friend class DbusSignatureTest;
+ FRIEND_TEST(DbusSignatureTest, DefaultObjectPathTypename);
+ FRIEND_TEST(DbusSignatureTest, ParseSuccesses);
+
+ // Typenames are C++ syntax types.
+ static const char kArrayTypename[];
+ static const char kBooleanTypename[];
+ static const char kByteTypename[];
+ static const char kDefaultObjectPathTypename[];
+ static const char kDictTypename[];
+ static const char kDoubleTypename[];
+ static const char kSigned16Typename[];
+ static const char kSigned32Typename[];
+ static const char kSigned64Typename[];
+ static const char kStringTypename[];
+ static const char kUnixFdTypename[];
+ static const char kUnsigned16Typename[];
+ static const char kUnsigned32Typename[];
+ static const char kUnsigned64Typename[];
+ static const char kVariantTypename[];
+ static const char kVariantDictTypename[];
+ static const char kPairTypename[];
+ static const char kTupleTypename[];
+
+ // Returns the C++ type name for the next D-Bus signature in the string at
+ // |signature| in |output|, as well as the next position within the string
+ // that parsing should continue |next|. It is not an error to pass a
+ // pointer to |signature| or nullptr as |next|. Returns true on success.
+ bool GetTypenameForSignature(std::string::const_iterator signature,
+ std::string::const_iterator end,
+ std::string::const_iterator* next,
+ std::string* output);
+
+ // Utility task for GetTypenameForSignature() which handles array objects
+ // and decodes them into a map or vector depending on the encoded sub-elements
+ // in the array. The arguments and return values are the same
+ // as GetTypenameForSignature().
+ bool GetArrayTypenameForSignature(std::string::const_iterator signature,
+ std::string::const_iterator end,
+ std::string::const_iterator* next,
+ std::string* output);
+
+ // Utility task for GetTypenameForSignature() which handles STRUCT objects
+ // and decodes them into a pair or tuple depending on the number of structure
+ // elements. The arguments and return values are the same
+ // as GetTypenameForSignature().
+ bool GetStructTypenameForSignature(std::string::const_iterator signature,
+ std::string::const_iterator end,
+ std::string::const_iterator* next,
+ std::string* output);
+
+
+ // The C++ typename to be used for D-Bus object pathnames.
+ std::string object_path_typename_;
+
+ DISALLOW_COPY_AND_ASSIGN(DbusSignature);
+};
+
+} // namespace chromeos_dbus_bindings
+
+#endif // CHROMEOS_DBUS_BINDINGS_DBUS_SIGNATURE_H_
diff --git a/chromeos-dbus-bindings/dbus_signature_unittest.cc b/chromeos-dbus-bindings/dbus_signature_unittest.cc
new file mode 100644
index 0000000..96ebbe5
--- /dev/null
+++ b/chromeos-dbus-bindings/dbus_signature_unittest.cc
@@ -0,0 +1,120 @@
+// 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/dbus_signature.h"
+
+#include <map>
+#include <string>
+
+#include <dbus/dbus-protocol.h>
+#include <gtest/gtest.h>
+
+using std::map;
+using std::string;
+using testing::Test;
+
+namespace chromeos_dbus_bindings {
+
+namespace {
+
+// Failing signatures.
+const char kEmptySignature[] = "";
+const char kEmptyDictSignature[] = "a{}";
+const char kMissingArraryParameterSignature[] = "a";
+const char kMissingArraryParameterInnerSignature[] = "a{sa}i";
+const char kOrphanDictSignature[] = "a{s{i}}";
+const char kTooFewDictMembersSignature[] = "a{s}";
+const char kTooManyDictMembersSignature[] = "a{sa{i}u}";
+const char kUnclosedDictOuterSignature[] = "a{s";
+const char kUnclosedDictInnerSignature[] = "a{a{u}";
+const char kUnexpectedCloseSignature[] = "a}i{";
+const char kUnknownSignature[] = "al";
+
+// Define an object type name to disambiguate the typenames above.
+const char kObjectPathTypename[] = "ObjectPathType";
+
+} // namespace
+
+class DbusSignatureTest : public Test {
+ protected:
+ DbusSignature signature_;
+};
+
+TEST_F(DbusSignatureTest, ParseFailures) {
+ for (const auto& failing_string : { kEmptySignature,
+ kEmptyDictSignature,
+ kMissingArraryParameterSignature,
+ kMissingArraryParameterInnerSignature,
+ kOrphanDictSignature,
+ kTooFewDictMembersSignature,
+ kTooManyDictMembersSignature,
+ kUnclosedDictOuterSignature,
+ kUnclosedDictInnerSignature,
+ kUnexpectedCloseSignature,
+ kUnknownSignature }) {
+ string unused_output;
+ EXPECT_FALSE(signature_.Parse(failing_string, &unused_output))
+ << "Expected signature " << failing_string
+ << " to fail but it succeeded";
+ }
+}
+
+TEST_F(DbusSignatureTest, DefaultObjectPathTypename) {
+ // The ParseSuccesses test below overrides the default object typename, so
+ // test the default behavior separately.
+ string output;
+ EXPECT_TRUE(signature_.Parse(DBUS_TYPE_OBJECT_PATH_AS_STRING, &output));
+ EXPECT_EQ(DbusSignature::kDefaultObjectPathTypename, output);
+}
+
+TEST_F(DbusSignatureTest, ParseSuccesses) {
+ const map<string, string> parse_values {
+ // Simple types.
+ { DBUS_TYPE_BOOLEAN_AS_STRING, DbusSignature::kBooleanTypename },
+ { DBUS_TYPE_BYTE_AS_STRING, DbusSignature::kByteTypename },
+ { DBUS_TYPE_DOUBLE_AS_STRING, DbusSignature::kDoubleTypename },
+ { DBUS_TYPE_OBJECT_PATH_AS_STRING, kObjectPathTypename },
+ { DBUS_TYPE_INT16_AS_STRING, DbusSignature::kSigned16Typename },
+ { DBUS_TYPE_INT32_AS_STRING, DbusSignature::kSigned32Typename },
+ { DBUS_TYPE_INT64_AS_STRING, DbusSignature::kSigned64Typename },
+ { DBUS_TYPE_STRING_AS_STRING, DbusSignature::kStringTypename },
+ { DBUS_TYPE_UNIX_FD_AS_STRING, DbusSignature::kUnixFdTypename },
+ { DBUS_TYPE_UINT16_AS_STRING, DbusSignature::kUnsigned16Typename },
+ { DBUS_TYPE_UINT32_AS_STRING, DbusSignature::kUnsigned32Typename },
+ { DBUS_TYPE_UINT64_AS_STRING, DbusSignature::kUnsigned64Typename },
+ { DBUS_TYPE_VARIANT_AS_STRING, DbusSignature::kVariantTypename },
+
+ // Complex types.
+ { "ab", "std::vector<bool>" },
+ { "ay", "std::vector<uint8_t>" },
+ { "aay", "std::vector<std::vector<uint8_t>>" },
+ { "ao", "std::vector<ObjectPathType>" },
+ { "a{oa{sa{sv}}}", "std::map<ObjectPathType, std::map<std::string, "
+ "chromeos::VariantDictionary>>" },
+ { "a{os}", "std::map<ObjectPathType, std::string>" },
+ { "as", "std::vector<std::string>" },
+ { "a{ss}", "std::map<std::string, std::string>" },
+ { "a{sa{ss}}", "std::map<std::string, std::map<std::string, "
+ "std::string>>"},
+ { "a{sa{sv}}", "std::map<std::string, chromeos::VariantDictionary>" },
+ { "a{sv}", "chromeos::VariantDictionary" },
+ { "a{sv}Garbage", "chromeos::VariantDictionary" },
+ { "at", "std::vector<uint64_t>" },
+ { "a{iv}", "std::map<int32_t, chromeos::Any>" },
+ { "(ib)", "std::tuple<int32_t, bool>" },
+ { "(ibs)", "std::tuple<int32_t, bool, std::string>" },
+ };
+ signature_.set_object_path_typename(kObjectPathTypename);
+ for (const auto& parse_test : parse_values) {
+ string output;
+ EXPECT_TRUE(signature_.Parse(parse_test.first, &output))
+ << "Expected signature " << parse_test.first
+ << " to succeed but it failed.";
+ EXPECT_EQ(parse_test.second, output)
+ << "Expected typename for " << parse_test.first
+ << " to be " << parse_test.second << " but instead it was " << output;
+ }
+}
+
+} // namespace chromeos_dbus_bindings
diff --git a/chromeos-dbus-bindings/generate_chromeos_dbus_bindings.cc b/chromeos-dbus-bindings/generate_chromeos_dbus_bindings.cc
new file mode 100644
index 0000000..dd67a3c
--- /dev/null
+++ b/chromeos-dbus-bindings/generate_chromeos_dbus_bindings.cc
@@ -0,0 +1,215 @@
+// 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 <memory>
+#include <string>
+
+#include <base/command_line.h>
+#include <base/files/file_path.h>
+#include <base/files/file_util.h>
+#include <base/json/json_reader.h>
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+#include <base/values.h>
+#include <chromeos/syslog_logging.h>
+
+#include "chromeos-dbus-bindings/adaptor_generator.h"
+#include "chromeos-dbus-bindings/method_name_generator.h"
+#include "chromeos-dbus-bindings/proxy_generator.h"
+#include "chromeos-dbus-bindings/xml_interface_parser.h"
+
+using chromeos_dbus_bindings::AdaptorGenerator;
+using chromeos_dbus_bindings::MethodNameGenerator;
+using chromeos_dbus_bindings::ProxyGenerator;
+using chromeos_dbus_bindings::ServiceConfig;
+
+namespace switches {
+
+static const char kHelp[] = "help";
+static const char kMethodNames[] = "method-names";
+static const char kAdaptor[] = "adaptor";
+static const char kProxy[] = "proxy";
+static const char kMock[] = "mock";
+static const char kServiceConfig[] = "service-config";
+static const char kHelpMessage[] = "\n"
+ "generate-chromeos-dbus-bindings itf1.xml [itf2.xml...] [switches]\n"
+ " itf1.xml, ... = the input interface file(s) [mandatory].\n"
+ "Available Switches: \n"
+ " --method-names=<method name header filename>\n"
+ " The output header file with string constants for each method name.\n"
+ " --adaptor=<adaptor header filename>\n"
+ " The output header file name containing the DBus adaptor class.\n"
+ " --proxy=<proxy header filename>\n"
+ " The output header file name containing the DBus proxy class.\n"
+ " --mock=<mock header filename>\n"
+ " The output header file name containing the DBus proxy mock class.\n"
+ " --service-config=<config.json>\n"
+ " The DBus service configuration file for the generator.\n";
+
+} // namespace switches
+
+namespace {
+// GYP sometimes enclosed the target file name in extra set of quotes like:
+// generate-chromeos-dbus-bindings in.xml "--adaptor=\"out.h\""
+// So, this function helps us to remove them.
+base::FilePath RemoveQuotes(const std::string& path) {
+ std::string unquoted;
+ base::TrimString(path, "\"'", &unquoted);
+ return base::FilePath{unquoted};
+}
+
+// Makes a canonical path by making the path absolute and by removing any
+// '..' which makes base::ReadFileToString() to fail.
+base::FilePath SanitizeFilePath(const std::string& path) {
+ base::FilePath path_in = RemoveQuotes(path);
+ base::FilePath path_out = base::MakeAbsoluteFilePath(path_in);
+ if (path_out.value().empty()) {
+ LOG(WARNING) << "Failed to canonicalize '" << path << "'";
+ path_out = path_in;
+ }
+ return path_out;
+}
+
+
+// Load the service configuration from the provided JSON file.
+bool LoadConfig(const base::FilePath& path, ServiceConfig *config) {
+ std::string contents;
+ if (!base::ReadFileToString(path, &contents))
+ return false;
+
+ std::unique_ptr<base::Value> json{base::JSONReader::Read(contents).release()};
+ if (!json)
+ return false;
+
+ base::DictionaryValue* dict = nullptr; // Aliased with |json|.
+ if (!json->GetAsDictionary(&dict))
+ return false;
+
+ dict->GetStringWithoutPathExpansion("service_name", &config->service_name);
+
+ base::DictionaryValue* om_dict = nullptr; // Owned by |dict|.
+ if (dict->GetDictionaryWithoutPathExpansion("object_manager", &om_dict)) {
+ if (!om_dict->GetStringWithoutPathExpansion("name",
+ &config->object_manager.name) &&
+ !config->service_name.empty()) {
+ config->object_manager.name = config->service_name + ".ObjectManager";
+ }
+ om_dict->GetStringWithoutPathExpansion("object_path",
+ &config->object_manager.object_path);
+ if (config->object_manager.name.empty()) {
+ LOG(ERROR) << "Object manager name is missing.";
+ return false;
+ }
+ }
+
+ base::ListValue* list = nullptr; // Owned by |dict|.
+ if (dict->GetListWithoutPathExpansion("ignore_interfaces", &list)) {
+ config->ignore_interfaces.reserve(list->GetSize());
+ for (base::Value* item : *list) {
+ std::string interface_name;
+ if (!item->GetAsString(&interface_name)) {
+ LOG(ERROR) << "Invalid interface name in [ignore_interfaces] section";
+ return false;
+ }
+ config->ignore_interfaces.push_back(interface_name);
+ }
+ }
+
+ return true;
+}
+
+} // anonymous namespace
+
+int main(int argc, char** argv) {
+ base::CommandLine::Init(argc, argv);
+ base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
+
+ // Setup logging to stderr. This also parses some implicit flags using the
+ // CommandLine singleton.
+ chromeos::InitLog(chromeos::kLogToStderr | chromeos::kLogHeader);
+
+ if (cl->HasSwitch(switches::kHelp)) {
+ LOG(INFO) << switches::kHelpMessage;
+ return 0;
+ }
+
+ auto input_files = cl->GetArgs();
+ if (input_files.empty()) {
+ LOG(ERROR) << "At least one file must be specified.";
+ LOG(ERROR) << switches::kHelpMessage;
+ return 1;
+ }
+
+ ServiceConfig config;
+ if (cl->HasSwitch(switches::kServiceConfig)) {
+ std::string config_file = cl->GetSwitchValueASCII(switches::kServiceConfig);
+ if (!config_file.empty() &&
+ !LoadConfig(SanitizeFilePath(config_file), &config)) {
+ LOG(ERROR) << "Failed to load DBus service config file " << config_file;
+ return 1;
+ }
+ }
+
+ chromeos_dbus_bindings::XmlInterfaceParser parser;
+ for (const auto& input : input_files) {
+ std::string contents;
+ if (!base::ReadFileToString(SanitizeFilePath(input), &contents)) {
+ LOG(ERROR) << "Failed to read file " << input;
+ return 1;
+ }
+ if (!parser.ParseXmlInterfaceFile(contents, config.ignore_interfaces)) {
+ LOG(ERROR) << "Failed to parse interface file " << input;
+ return 1;
+ }
+ }
+
+ if (cl->HasSwitch(switches::kMethodNames)) {
+ std::string method_name_file =
+ cl->GetSwitchValueASCII(switches::kMethodNames);
+ VLOG(1) << "Outputting method names to " << method_name_file;
+ if (!MethodNameGenerator::GenerateMethodNames(
+ parser.interfaces(),
+ RemoveQuotes(method_name_file))) {
+ LOG(ERROR) << "Failed to output method names.";
+ return 1;
+ }
+ }
+
+ if (cl->HasSwitch(switches::kAdaptor)) {
+ std::string adaptor_file = cl->GetSwitchValueASCII(switches::kAdaptor);
+ VLOG(1) << "Outputting adaptor to " << adaptor_file;
+ if (!AdaptorGenerator::GenerateAdaptors(parser.interfaces(),
+ RemoveQuotes(adaptor_file))) {
+ LOG(ERROR) << "Failed to output adaptor.";
+ return 1;
+ }
+ }
+
+ base::FilePath proxy_path; // Used by both Proxy and Mock generation.
+ if (cl->HasSwitch(switches::kProxy)) {
+ std::string proxy_file = cl->GetSwitchValueASCII(switches::kProxy);
+ proxy_path = RemoveQuotes(proxy_file);
+ base::NormalizeFilePath(proxy_path, &proxy_path);
+ VLOG(1) << "Outputting proxy to " << proxy_path.value();
+ if (!ProxyGenerator::GenerateProxies(config, parser.interfaces(),
+ proxy_path)) {
+ LOG(ERROR) << "Failed to output proxy.";
+ return 1;
+ }
+ }
+
+ if (cl->HasSwitch(switches::kMock)) {
+ std::string mock_file = cl->GetSwitchValueASCII(switches::kMock);
+ base::FilePath mock_path = RemoveQuotes(mock_file);
+ base::NormalizeFilePath(mock_path, &mock_path);
+ VLOG(1) << "Outputting mock to " << mock_path.value();
+ if (!ProxyGenerator::GenerateMocks(config, parser.interfaces(), mock_path,
+ proxy_path)) {
+ LOG(ERROR) << "Failed to output mock.";
+ return 1;
+ }
+ }
+
+ return 0;
+}
diff --git a/chromeos-dbus-bindings/header_generator.cc b/chromeos-dbus-bindings/header_generator.cc
new file mode 100644
index 0000000..0a24597
--- /dev/null
+++ b/chromeos-dbus-bindings/header_generator.cc
@@ -0,0 +1,70 @@
+// 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/header_generator.h"
+
+#include <string>
+
+#include <base/files/file_path.h>
+#include <base/files/file_util.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <chromeos/strings/string_utils.h>
+
+#include "chromeos-dbus-bindings/indented_text.h"
+
+using std::string;
+using std::vector;
+
+namespace chromeos_dbus_bindings {
+
+// static
+string HeaderGenerator::GenerateHeaderGuard(
+ const base::FilePath& output_file) {
+ string guard = base::StringPrintf("____chromeos_dbus_binding__%s",
+ output_file.value().c_str());
+ for (auto& c : guard) {
+ if (IsAsciiAlpha(c)) {
+ c = base::ToUpperASCII(c);
+ } else if (!IsAsciiDigit(c)) {
+ c = '_';
+ }
+ }
+ return guard;
+}
+
+// static
+bool HeaderGenerator::IsIntegralType(const string& type) {
+ return type.find("::") == std::string::npos;
+}
+
+// static
+void HeaderGenerator::MakeConstReferenceIfNeeded(std::string* type) {
+ if (!IsIntegralType(*type)) {
+ *type = base::StringPrintf("const %s&", type->c_str());
+ }
+}
+
+// static
+bool HeaderGenerator::WriteTextToFile(
+ const base::FilePath& output_file, const IndentedText &text) {
+ string contents = text.GetContents();
+ int expected_write_return = contents.size();
+ if (base::WriteFile(output_file, contents.c_str(), contents.size()) !=
+ expected_write_return) {
+ LOG(ERROR) << "Failed to write file " << output_file.value();
+ return false;
+ }
+ return true;
+}
+
+// static
+string HeaderGenerator::GetArgName(const char* prefix,
+ const string& arg_name,
+ int arg_index) {
+ string name = arg_name.empty() ? std::to_string(arg_index) : arg_name;
+ return base::StringPrintf("%s_%s", prefix, name.c_str());
+}
+
+} // namespace chromeos_dbus_bindings
diff --git a/chromeos-dbus-bindings/header_generator.h b/chromeos-dbus-bindings/header_generator.h
new file mode 100644
index 0000000..0c28bd7
--- /dev/null
+++ b/chromeos-dbus-bindings/header_generator.h
@@ -0,0 +1,81 @@
+// 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.
+
+#ifndef CHROMEOS_DBUS_BINDINGS_HEADER_GENERATOR_H_
+#define CHROMEOS_DBUS_BINDINGS_HEADER_GENERATOR_H_
+
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+
+namespace base {
+
+class FilePath;
+
+};
+
+namespace chromeos_dbus_bindings {
+
+struct Interface;
+class IndentedText;
+
+// General D-Bus service configuration settings used by Adaptor/Proxy code
+// generators.
+struct ServiceConfig {
+ // D-Bus service name to be used when constructing proxy objects.
+ // If omitted (empty), the service name parameter will be added to the
+ // constructor of generated proxy class(es).
+ std::string service_name;
+ // Object Manager settings.
+ struct {
+ // The name of the Object Manager class to use. If empty, no object manager
+ // is generated in the proxy code (this also disables property support on
+ // proxy objects).
+ // This is a "fake" name used to generate namespaces and the actual class
+ // name for the object manager proxy. This name has no relationship to the
+ // actual D-Bus properties of the actual object manager.
+ std::string name;
+ // The D-Bus path to Object Manager instance.
+ std::string object_path;
+ } object_manager;
+
+ // A list of interfaces we should ignore and not generate any adaptors and
+ // proxies for.
+ std::vector<std::string> ignore_interfaces;
+};
+
+class HeaderGenerator {
+ protected:
+ // Create a unique header guard string to protect multiple includes of header.
+ static std::string GenerateHeaderGuard(const base::FilePath& output_file);
+
+ // Used to decide whether the argument should be a const reference.
+ static bool IsIntegralType(const std::string& type);
+
+ // If |type| is a non-integral type, converts it into a const reference.
+ static void MakeConstReferenceIfNeeded(std::string* type);
+
+ // Writes indented text to a file.
+ static bool WriteTextToFile(const base::FilePath& output_file,
+ const IndentedText& text);
+
+ // Generate a name of a method/signal argument based on the name provided in
+ // the XML file. If |arg_name| is empty, it generates a name using
+ // the |arg_index| counter.
+ static std::string GetArgName(const char* prefix,
+ const std::string& arg_name,
+ int arg_index);
+
+ static const int kScopeOffset = 1;
+ static const int kBlockOffset = 2;
+ static const int kLineContinuationOffset = 4;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HeaderGenerator);
+};
+
+} // namespace chromeos_dbus_bindings
+
+#endif // CHROMEOS_DBUS_BINDINGS_HEADER_GENERATOR_H_
diff --git a/chromeos-dbus-bindings/indented_text.cc b/chromeos-dbus-bindings/indented_text.cc
new file mode 100644
index 0000000..8d42091
--- /dev/null
+++ b/chromeos-dbus-bindings/indented_text.cc
@@ -0,0 +1,140 @@
+// 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/indented_text.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+#include <chromeos/strings/string_utils.h>
+
+using std::string;
+using std::vector;
+
+namespace chromeos_dbus_bindings {
+
+IndentedText::IndentedText() : offset_(0) {}
+
+void IndentedText::AddBlankLine() {
+ AddLine("");
+}
+
+void IndentedText::AddBlock(const IndentedText& block) {
+ AddBlockWithOffset(block, 0);
+}
+
+void IndentedText::AddBlockWithOffset(const IndentedText& block, size_t shift) {
+ for (const auto& member : block.contents_) {
+ AddLineWithOffset(member.first, member.second + shift);
+ }
+}
+
+void IndentedText::AddLine(const std::string& line) {
+ AddLineWithOffset(line, 0);
+}
+
+void IndentedText::AddLineWithOffset(const std::string& line, size_t shift) {
+ contents_.emplace_back(line, shift + offset_);
+}
+
+void IndentedText::AddLineAndPushOffsetTo(const std::string& line,
+ size_t occurrence,
+ char c) {
+ AddLine(line);
+ size_t pos = 0;
+ while (occurrence > 0) {
+ pos = line.find(c, pos);
+ CHECK(pos != string::npos);
+ pos++;
+ occurrence--;
+ }
+ PushOffset(pos);
+}
+
+void IndentedText::AddComments(const std::string& doc_string) {
+ // Try to retain indentation in the comments. Find the first non-empty line
+ // of the comment and find its whitespace indentation prefix.
+ // For all subsequent lines, remove the same whitespace prefix as found
+ // at the first line of the comment but keep any additional spaces to
+ // maintain the comment layout.
+ auto lines = chromeos::string_utils::Split(doc_string, "\n", false, false);
+ vector<string> lines_out;
+ lines_out.reserve(lines.size());
+ bool first_nonempty_found = false;
+ std::string trim_prefix;
+ for (string line : lines) {
+ base::TrimWhitespaceASCII(line, base::TRIM_TRAILING, &line);
+ if (!first_nonempty_found) {
+ size_t pos = line.find_first_not_of(" \t");
+ if (pos != std::string::npos) {
+ first_nonempty_found = true;
+ trim_prefix = line.substr(0, pos);
+ lines_out.push_back(line.substr(pos));
+ }
+ } else {
+ if (base::StartsWithASCII(line, trim_prefix, false)) {
+ line = line.substr(trim_prefix.length());
+ } else {
+ base::TrimWhitespaceASCII(line, base::TRIM_LEADING, &line);
+ }
+ lines_out.push_back(line);
+ }
+ }
+
+ // We already eliminated all empty lines at the beginning of the comment
+ // block. Now remove the trailing empty lines.
+ while (!lines_out.empty() && lines_out.back().empty())
+ lines_out.pop_back();
+
+ for (const string& line : lines_out) {
+ const bool all_whitespace = (line.find_first_not_of(" \t") == string::npos);
+ if (all_whitespace) {
+ AddLine("//");
+ } else {
+ AddLine("// " + line);
+ }
+ }
+}
+
+string IndentedText::GetContents() const {
+ string output;
+ for (const string& line : GetLines()) {
+ output.append(line);
+ output.append("\n");
+ }
+ return output;
+}
+
+std::vector<std::string> IndentedText::GetLines() const {
+ vector<string> result;
+ for (const auto& member : contents_) {
+ const string& line = member.first;
+ size_t shift = line.empty() ? 0 : member.second;
+ string indent(shift, ' ');
+ result.push_back(indent + line);
+ }
+ return result;
+}
+
+void IndentedText::PushOffset(size_t shift) {
+ offset_ += shift;
+ offset_history_.push_back(shift);
+}
+
+void IndentedText::PopOffset() {
+ CHECK(!offset_history_.empty());
+ offset_ -= offset_history_.back();
+ offset_history_.pop_back();
+}
+
+void IndentedText::Reset() {
+ offset_ = 0;
+ offset_history_.clear();
+ contents_.clear();
+}
+
+} // namespace chromeos_dbus_bindings
diff --git a/chromeos-dbus-bindings/indented_text.h b/chromeos-dbus-bindings/indented_text.h
new file mode 100644
index 0000000..6651a58
--- /dev/null
+++ b/chromeos-dbus-bindings/indented_text.h
@@ -0,0 +1,70 @@
+// 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.
+
+#ifndef CHROMEOS_DBUS_BINDINGS_INDENTED_TEXT_H_
+#define CHROMEOS_DBUS_BINDINGS_INDENTED_TEXT_H_
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <base/macros.h>
+
+namespace chromeos_dbus_bindings {
+
+class IndentedText {
+ public:
+ IndentedText();
+ virtual ~IndentedText() = default;
+
+ // Insert a blank line.
+ void AddBlankLine();
+
+ // Insert a block of indented text.
+ void AddBlock(const IndentedText& block);
+ void AddBlockWithOffset(const IndentedText& block, size_t shift);
+
+ // Add a line at the current indentation.
+ void AddLine(const std::string& line);
+ void AddLineWithOffset(const std::string& line, size_t shift);
+ // Adds a line and pushes an offset past the |nth_occurrence| of character |c|
+ // in that line, effectively allowing to align following line to the position
+ // following that character.
+ void AddLineAndPushOffsetTo(const std::string& line,
+ size_t nth_occurrence,
+ char c);
+
+ // Adds a block of comments.
+ void AddComments(const std::string& doc_string);
+
+ // Return a string representing the indented text.
+ std::string GetContents() const;
+
+ // Return a list of lines representing the intended indented text, not
+ // including the \n.
+ std::vector<std::string> GetLines() const;
+
+ // Add or remove an offset to the current stack of indentation offsets.
+ void PushOffset(size_t shift);
+ void PopOffset();
+
+ // Reset to initial state.
+ void Reset();
+
+
+ private:
+ using IndentedLine = std::pair<std::string, size_t>;
+
+ friend class IndentedTextTest;
+
+ size_t offset_;
+ std::vector<size_t> offset_history_;
+ std::vector<IndentedLine> contents_;
+
+ DISALLOW_COPY_AND_ASSIGN(IndentedText);
+};
+
+} // namespace chromeos_dbus_bindings
+
+#endif // CHROMEOS_DBUS_BINDINGS_INDENTED_TEXT_H_
diff --git a/chromeos-dbus-bindings/indented_text_unittest.cc b/chromeos-dbus-bindings/indented_text_unittest.cc
new file mode 100644
index 0000000..a75c396
--- /dev/null
+++ b/chromeos-dbus-bindings/indented_text_unittest.cc
@@ -0,0 +1,183 @@
+// 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/indented_text.h"
+
+#include <string>
+#include <vector>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using std::string;
+using std::vector;
+using testing::ElementsAre;
+using testing::Test;
+
+namespace chromeos_dbus_bindings {
+
+class IndentedTextTest : public Test {
+ protected:
+ size_t GetOffset() const { return text_.offset_; }
+ const vector<size_t>& GetHistory() const { return text_.offset_history_; }
+ IndentedText text_;
+};
+
+TEST_F(IndentedTextTest, Constructor) {
+ EXPECT_EQ("", text_.GetContents());
+ EXPECT_EQ(0u, GetOffset());
+ EXPECT_TRUE(GetHistory().empty());
+}
+
+TEST_F(IndentedTextTest, AddLine) {
+ const char kTestString0[] = "test";
+ text_.AddLine(kTestString0);
+ EXPECT_EQ(string(kTestString0) + "\n", text_.GetContents());
+ EXPECT_EQ(0u, GetOffset());
+ EXPECT_TRUE(GetHistory().empty());
+
+ const char kTestString1[] = "me";
+ text_.AddLine(kTestString1);
+ EXPECT_EQ(string(kTestString0) + "\n" + kTestString1 + "\n",
+ text_.GetContents());
+ EXPECT_EQ(0u, GetOffset());
+ EXPECT_TRUE(GetHistory().empty());
+}
+
+TEST_F(IndentedTextTest, AddLineWithOffset) {
+ const char kTestString[] = "test";
+ const int kShift = 4;
+ text_.AddLineWithOffset(kTestString, kShift);
+ EXPECT_EQ(string(kShift, ' ') + kTestString + "\n", text_.GetContents());
+}
+
+TEST_F(IndentedTextTest, AddLineAndPushOffsetTo) {
+ text_.AddLineAndPushOffsetTo("foo(bar(baz", 2, '(');
+ EXPECT_THAT(GetHistory(), ElementsAre(8));
+}
+
+TEST_F(IndentedTextTest, AddBlock) {
+ IndentedText block0;
+ const char kTestString[] = "test";
+ block0.AddLineWithOffset(kTestString, 10);
+ block0.AddLineWithOffset(kTestString, 20);
+ IndentedText block1;
+ block1.AddLineWithOffset(kTestString, 5);
+ block1.AddLineWithOffset(kTestString, 15);
+ text_.AddBlock(block0);
+ text_.AddBlock(block1);
+ EXPECT_EQ(block0.GetContents() + block1.GetContents(), text_.GetContents());
+}
+
+TEST_F(IndentedTextTest, AddBlockWithOffset) {
+ const char kTestString[] = "test";
+ IndentedText block;
+ const size_t kOffset0 = 0;
+ block.AddLineWithOffset(kTestString, kOffset0);
+ const size_t kOffset1 = 4;
+ block.AddLineWithOffset(kTestString, kOffset1);
+ const size_t kOffset2 = 20;
+ text_.AddBlockWithOffset(block, kOffset2);
+ EXPECT_EQ(string(kOffset2 + kOffset0, ' ') + kTestString + "\n" +
+ string(kOffset2 + kOffset1, ' ') + kTestString + "\n",
+ text_.GetContents());
+}
+
+TEST_F(IndentedTextTest, PushPop) {
+ const char kTestString[] = "test";
+ text_.AddLine(kTestString);
+
+ const size_t kShift0 = 2;
+ text_.PushOffset(kShift0);
+ EXPECT_EQ(2u, GetOffset());
+ EXPECT_THAT(GetHistory(), ElementsAre(kShift0));
+ text_.AddLine(kTestString);
+
+ const size_t kShift1 = 4;
+ text_.PushOffset(kShift1);
+ EXPECT_EQ(kShift0 + kShift1, GetOffset());
+ EXPECT_THAT(GetHistory(), ElementsAre(kShift0, kShift1));
+ text_.AddLine(kTestString);
+
+ text_.PopOffset();
+ text_.AddLine(kTestString);
+ EXPECT_EQ(2u, GetOffset());
+ EXPECT_THAT(GetHistory(), ElementsAre(kShift0));
+
+ text_.PopOffset();
+ text_.AddLine(kTestString);
+ EXPECT_EQ(0u, GetOffset());
+ EXPECT_TRUE(GetHistory().empty());
+
+ EXPECT_EQ(string(kTestString) + "\n" +
+ string(kShift0, ' ') + kTestString + "\n" +
+ string(kShift0 + kShift1, ' ') + kTestString + "\n" +
+ string(kShift0, ' ') + kTestString + "\n" +
+ string(kTestString) + "\n",
+ text_.GetContents());
+}
+
+TEST_F(IndentedTextTest, Reset) {
+ text_.PushOffset(10);
+ text_.AddLine("test");
+ EXPECT_NE("", text_.GetContents());
+ EXPECT_NE(0u, GetOffset());
+ EXPECT_FALSE(GetHistory().empty());
+ text_.Reset();
+ EXPECT_EQ("", text_.GetContents());
+ EXPECT_EQ(0u, GetOffset());
+ EXPECT_TRUE(GetHistory().empty());
+}
+
+TEST_F(IndentedTextTest, AddComments_Empty) {
+ text_.AddComments("");
+ EXPECT_EQ("", text_.GetContents());
+}
+
+TEST_F(IndentedTextTest, AddComments_WhitespaceOnly) {
+ text_.AddComments(" \n \t \n");
+ EXPECT_EQ("", text_.GetContents());
+}
+
+TEST_F(IndentedTextTest, AddComments_EmptyLines) {
+ string comment_block = R"(
+
+ line1
+
+ line2
+
+
+ )";
+ text_.AddComments(comment_block);
+ EXPECT_EQ("// line1\n"
+ "//\n"
+ "// line2\n", text_.GetContents());
+}
+
+TEST_F(IndentedTextTest, AddComments_Indentation) {
+ string comment_block = R"(
+ line1
+ - bullet1
+ line2
+ - bullet2
+ line3
+ )";
+ text_.AddComments(comment_block);
+ EXPECT_EQ("// line1\n"
+ "// - bullet1\n"
+ "// line2\n"
+ "// - bullet2\n"
+ "// line3\n", text_.GetContents());
+}
+
+TEST_F(IndentedTextTest, GetLines) {
+ text_.AddLine("no indent");
+ text_.PushOffset(2);
+ text_.AddLine("2 spaces");
+ text_.AddLine("");
+
+ EXPECT_EQ((vector<string>{"no indent", " 2 spaces", ""}), text_.GetLines());
+}
+
+} // namespace chromeos_dbus_bindings
diff --git a/chromeos-dbus-bindings/interface.h b/chromeos-dbus-bindings/interface.h
new file mode 100644
index 0000000..73b1481
--- /dev/null
+++ b/chromeos-dbus-bindings/interface.h
@@ -0,0 +1,83 @@
+// 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.
+
+#ifndef CHROMEOS_DBUS_BINDINGS_INTERFACE_H_
+#define CHROMEOS_DBUS_BINDINGS_INTERFACE_H_
+
+#include <string>
+#include <vector>
+
+namespace chromeos_dbus_bindings {
+
+struct Interface {
+ struct Argument {
+ Argument(const std::string& name_in,
+ const std::string& type_in) : name(name_in), type(type_in) {}
+ std::string name;
+ std::string type;
+ };
+ struct Method {
+ enum class Kind {
+ kSimple,
+ kNormal,
+ kAsync,
+ kRaw
+ };
+ Method(const std::string& name_in,
+ const std::vector<Argument>& input_arguments_in,
+ const std::vector<Argument>& output_arguments_in)
+ : name(name_in),
+ input_arguments(input_arguments_in),
+ output_arguments(output_arguments_in) {}
+ Method(const std::string& name_in,
+ const std::vector<Argument>& input_arguments_in)
+ : name(name_in),
+ input_arguments(input_arguments_in) {}
+ explicit Method(const std::string& name_in) : name(name_in) {}
+ std::string name;
+ std::vector<Argument> input_arguments;
+ std::vector<Argument> output_arguments;
+ std::string doc_string;
+ Kind kind{Kind::kNormal};
+ bool is_const{false};
+ bool include_dbus_message{false};
+ };
+ struct Signal {
+ Signal(const std::string& name_in,
+ const std::vector<Argument>& arguments_in)
+ : name(name_in), arguments(arguments_in) {}
+ explicit Signal(const std::string& name_in) : name(name_in) {}
+ std::string name;
+ std::vector<Argument> arguments;
+ std::string doc_string;
+ };
+ struct Property {
+ Property(const std::string& name_in,
+ const std::string& type_in,
+ const std::string& access_in)
+ : name(name_in), type(type_in), access(access_in) {}
+ std::string name;
+ std::string type;
+ std::string access;
+ std::string doc_string;
+ };
+
+ Interface() = default;
+ Interface(const std::string& name_in,
+ const std::vector<Method>& methods_in,
+ const std::vector<Signal>& signals_in,
+ const std::vector<Property>& properties_in)
+ : name(name_in), methods(methods_in), signals(signals_in),
+ properties(properties_in) {}
+ std::string name;
+ std::string path;
+ std::vector<Method> methods;
+ std::vector<Signal> signals;
+ std::vector<Property> properties;
+ std::string doc_string;
+};
+
+} // namespace chromeos_dbus_bindings
+
+#endif // CHROMEOS_DBUS_BINDINGS_INTERFACE_H_
diff --git a/chromeos-dbus-bindings/method_name_generator.cc b/chromeos-dbus-bindings/method_name_generator.cc
new file mode 100644
index 0000000..5332660
--- /dev/null
+++ b/chromeos-dbus-bindings/method_name_generator.cc
@@ -0,0 +1,46 @@
+// 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/method_name_generator.h"
+
+#include <base/files/file_path.h>
+#include <base/strings/stringprintf.h>
+
+#include "chromeos-dbus-bindings/indented_text.h"
+#include "chromeos-dbus-bindings/interface.h"
+#include "chromeos-dbus-bindings/name_parser.h"
+
+using std::string;
+using std::vector;
+
+namespace chromeos_dbus_bindings {
+
+// static
+string MethodNameGenerator::GenerateMethodNameConstant(
+ const string& method_name) {
+ return "k" + method_name + "Method";
+}
+
+// static
+bool MethodNameGenerator::GenerateMethodNames(
+ const vector<Interface>& interfaces,
+ const base::FilePath& output_file) {
+ string contents;
+ IndentedText text;
+ for (const auto& interface : interfaces) {
+ text.AddBlankLine();
+ NameParser parser{interface.name};
+ parser.AddOpenNamespaces(&text, true);
+ for (const auto& method : interface.methods) {
+ text.AddLine(
+ base::StringPrintf("const char %s[] = \"%s\";",
+ GenerateMethodNameConstant(method.name).c_str(),
+ method.name.c_str()));
+ }
+ parser.AddCloseNamespaces(&text, true);
+ }
+ return HeaderGenerator::WriteTextToFile(output_file, text);
+}
+
+} // namespace chromeos_dbus_bindings
diff --git a/chromeos-dbus-bindings/method_name_generator.h b/chromeos-dbus-bindings/method_name_generator.h
new file mode 100644
index 0000000..24a3dc1
--- /dev/null
+++ b/chromeos-dbus-bindings/method_name_generator.h
@@ -0,0 +1,39 @@
+// 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.
+
+#ifndef CHROMEOS_DBUS_BINDINGS_METHOD_NAME_GENERATOR_H_
+#define CHROMEOS_DBUS_BINDINGS_METHOD_NAME_GENERATOR_H_
+
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+
+#include "chromeos-dbus-bindings/header_generator.h"
+
+namespace base {
+
+class FilePath;
+
+} // namespace base
+
+namespace chromeos_dbus_bindings {
+
+struct Interface;
+
+class MethodNameGenerator : public HeaderGenerator {
+ public:
+ static bool GenerateMethodNames(const std::vector<Interface>& interfaces,
+ const base::FilePath& output_file);
+ static std::string GenerateMethodNameConstant(const std::string& method_name);
+
+ private:
+ friend class MethodNameGeneratorTest;
+
+ DISALLOW_COPY_AND_ASSIGN(MethodNameGenerator);
+};
+
+} // namespace chromeos_dbus_bindings
+
+#endif // CHROMEOS_DBUS_BINDINGS_METHOD_NAME_GENERATOR_H_
diff --git a/chromeos-dbus-bindings/method_name_generator_unittest.cc b/chromeos-dbus-bindings/method_name_generator_unittest.cc
new file mode 100644
index 0000000..040c188
--- /dev/null
+++ b/chromeos-dbus-bindings/method_name_generator_unittest.cc
@@ -0,0 +1,69 @@
+// 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/method_name_generator.h"
+
+#include <string>
+#include <vector>
+
+#include <base/files/file_path.h>
+#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+#include <gtest/gtest.h>
+
+#include "chromeos-dbus-bindings/interface.h"
+
+using std::string;
+using testing::Test;
+
+namespace chromeos_dbus_bindings {
+
+namespace {
+
+const char kMethodName0[] = "Zircon";
+const char kMethodName1[] = "Encrusted";
+const char kMethodName2[] = "Tweezers";
+const char kExpectedOutput[] = R"(
+namespace MyInterface {
+const char kZirconMethod[] = "Zircon";
+const char kEncrustedMethod[] = "Encrusted";
+const char kTweezersMethod[] = "Tweezers";
+} // namespace MyInterface
+)";
+
+} // namespace
+
+class MethodNameGeneratorTest : public Test {
+ public:
+ void SetUp() override {
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ }
+
+ protected:
+ base::FilePath CreateInputFile(const string& contents) {
+ base::FilePath path;
+ EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(), &path));
+ int written = base::WriteFile(path, contents.c_str(), contents.size());
+ EXPECT_EQ(contents.size(), static_cast<size_t>(written));
+ return path;
+ }
+
+ base::ScopedTempDir temp_dir_;
+};
+
+TEST_F(MethodNameGeneratorTest, GnerateMethodNames) {
+ Interface interface;
+ interface.name = "MyInterface";
+ interface.methods.emplace_back(kMethodName0);
+ interface.methods.emplace_back(kMethodName1);
+ interface.methods.emplace_back(kMethodName2);
+ base::FilePath output_path = temp_dir_.path().Append("output.h");
+ EXPECT_TRUE(MethodNameGenerator::GenerateMethodNames({interface},
+ output_path));
+ string contents;
+ EXPECT_TRUE(base::ReadFileToString(output_path, &contents));
+ EXPECT_STREQ(kExpectedOutput, contents.c_str());
+}
+
+} // namespace chromeos_dbus_bindings
diff --git a/chromeos-dbus-bindings/name_parser.cc b/chromeos-dbus-bindings/name_parser.cc
new file mode 100644
index 0000000..35cbb61
--- /dev/null
+++ b/chromeos-dbus-bindings/name_parser.cc
@@ -0,0 +1,98 @@
+// 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/name_parser.h"
+
+#include <string>
+
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <chromeos/strings/string_utils.h>
+
+#include "chromeos-dbus-bindings/indented_text.h"
+
+namespace chromeos_dbus_bindings {
+
+namespace {
+
+void AddOpenNamespace(IndentedText *text, const std::string& name) {
+ text->AddLine(base::StringPrintf("namespace %s {", name.c_str()));
+}
+
+void AddCloseNamespace(IndentedText *text, const std::string& name) {
+ text->AddLine(base::StringPrintf("} // namespace %s", name.c_str()));
+}
+
+} // anonymous namespace
+
+NameParser::NameParser(const std::string& name)
+ : namespaces{chromeos::string_utils::Split(name, ".")} {
+ CHECK(!namespaces.empty()) << "Empty name specified";
+ type_name = namespaces.back();
+ namespaces.pop_back();
+}
+
+std::string NameParser::MakeFullyQualified(const std::string& name) const {
+ std::vector<std::string> parts = namespaces;
+ parts.push_back(name);
+ return chromeos::string_utils::Join("::", parts);
+}
+
+std::string NameParser::MakeFullCppName() const {
+ return MakeFullyQualified(type_name);
+}
+
+std::string NameParser::MakeVariableName() const {
+ // Convert CamelCase name to google_style variable name.
+ std::string result;
+ bool last_upper = true;
+ for (char c : type_name) {
+ bool is_upper = isupper(c);
+ if (is_upper) {
+ if (!last_upper)
+ result += '_';
+ c = base::ToLowerASCII(c);
+ }
+ last_upper = is_upper;
+ result.push_back(c);
+ }
+ return result;
+}
+
+std::string NameParser::MakeInterfaceName(bool fully_qualified) const {
+ std::string interface_name = type_name + "Interface";
+ return fully_qualified ? MakeFullyQualified(interface_name) : interface_name;
+}
+
+std::string NameParser::MakeProxyName(bool fully_qualified) const {
+ std::string proxy_name = type_name + "Proxy";
+ return fully_qualified ? MakeFullyQualified(proxy_name) : proxy_name;
+}
+
+std::string NameParser::MakeAdaptorName(bool fully_qualified) const {
+ std::string adaptor_name = type_name + "Adaptor";
+ return fully_qualified ? MakeFullyQualified(adaptor_name) : adaptor_name;
+}
+
+void NameParser::AddOpenNamespaces(IndentedText *text,
+ bool add_main_type) const {
+ for (const auto& ns : namespaces) {
+ AddOpenNamespace(text, ns);
+ }
+
+ if (add_main_type)
+ AddOpenNamespace(text, type_name);
+}
+
+void NameParser::AddCloseNamespaces(IndentedText *text,
+ bool add_main_type) const {
+ if (add_main_type)
+ AddCloseNamespace(text, type_name);
+
+ for (auto it = namespaces.rbegin(); it != namespaces.rend(); ++it) {
+ AddCloseNamespace(text, *it);
+ }
+}
+
+} // namespace chromeos_dbus_bindings
diff --git a/chromeos-dbus-bindings/name_parser.h b/chromeos-dbus-bindings/name_parser.h
new file mode 100644
index 0000000..0282819
--- /dev/null
+++ b/chromeos-dbus-bindings/name_parser.h
@@ -0,0 +1,63 @@
+// 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.
+
+#ifndef CHROMEOS_DBUS_BINDINGS_NAME_PARSER_H_
+#define CHROMEOS_DBUS_BINDINGS_NAME_PARSER_H_
+
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+
+namespace chromeos_dbus_bindings {
+
+struct Interface;
+class IndentedText;
+
+// A helper class that allows to decompose D-Bus name strings such as
+// "org.chromium.TestInterface" into components and be able to construct the
+// corresponding C++ identifiers, namespaces, variable names, etc.
+class NameParser {
+ public:
+ explicit NameParser(const std::string& name);
+
+ // Returns fully-qualified C++ type name for the current D-Bus name
+ // for example "org::chromium::TestInterface".
+ std::string MakeFullCppName() const;
+
+ // Returns a variable name suitable for object of this type.
+ // For example "test_interface".
+ std::string MakeVariableName() const;
+
+ // Returns a name of an interface for the given type, optionally qualifying
+ // it with the C++ namespaces.
+ std::string MakeInterfaceName(bool fully_qualified) const;
+
+ // Returns a name of a proxy class for the given type, optionally qualifying
+ // it with the C++ namespaces.
+ std::string MakeProxyName(bool fully_qualified) const;
+
+ // Returns a name of an adaptor class for the given type, optionally
+ // qualifying it with the C++ namespaces.
+ std::string MakeAdaptorName(bool fully_qualified) const;
+
+ // Adds opening "namespace ... {" statements to |text|.
+ // If |add_main_type| is true, adds the main type name as a namespace as well.
+ void AddOpenNamespaces(IndentedText *text, bool add_main_type) const;
+
+ // Adds closing "} // namespace ..." statements to |text|.
+ // If |add_main_type| is true, adds the main type name as a namespace as well.
+ void AddCloseNamespaces(IndentedText *text, bool add_main_type) const;
+
+ std::string type_name; // e.g. "TestInterface".
+ std::vector<std::string> namespaces; // e.g. {"org", "chromium"}.
+
+ private:
+ // Helper function to prepend the C++ namespaces to the |name|.
+ std::string MakeFullyQualified(const std::string& name) const;
+};
+
+} // namespace chromeos_dbus_bindings
+
+#endif // CHROMEOS_DBUS_BINDINGS_NAME_PARSER_H_
diff --git a/chromeos-dbus-bindings/name_parser_unittest.cc b/chromeos-dbus-bindings/name_parser_unittest.cc
new file mode 100644
index 0000000..25c6c92
--- /dev/null
+++ b/chromeos-dbus-bindings/name_parser_unittest.cc
@@ -0,0 +1,123 @@
+// 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/name_parser.h"
+
+#include <map>
+#include <string>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "chromeos-dbus-bindings/indented_text.h"
+
+namespace chromeos_dbus_bindings {
+
+TEST(NameParser, Parsing_Empty) {
+ EXPECT_DEATH(NameParser{""}, "Empty name specified");
+}
+
+TEST(NameParser, Parsing_NoNamespaces) {
+ NameParser parser{"foo"};
+ EXPECT_EQ("foo", parser.type_name);
+ EXPECT_TRUE(parser.namespaces.empty());
+}
+
+TEST(NameParser, Parsing_FullyQualified) {
+ NameParser parser{"foo.bar.FooBar"};
+ EXPECT_EQ("FooBar", parser.type_name);
+ EXPECT_THAT(parser.namespaces, testing::ElementsAre("foo", "bar"));
+}
+
+TEST(NameParser, MakeFullCppName) {
+ NameParser parser{"foo.bar.FooBar"};
+ EXPECT_EQ("foo::bar::FooBar", parser.MakeFullCppName());
+}
+
+TEST(NameParser, MakeVariableName) {
+ NameParser parser{"foo.bar.FooBar"};
+ EXPECT_EQ("foo_bar", parser.MakeVariableName());
+}
+
+TEST(NameParser, MakeVariableName_NoCapitals) {
+ NameParser parser{"foo"};
+ EXPECT_EQ("foo", parser.MakeVariableName());
+}
+
+TEST(NameParser, MakeVariableName_NoInitialCapital) {
+ NameParser parser{"fooBarBaz"};
+ EXPECT_EQ("foo_bar_baz", parser.MakeVariableName());
+}
+
+TEST(NameParser, MakeVariableName_AllCapitals) {
+ NameParser parser{"UUID"};
+ EXPECT_EQ("uuid", parser.MakeVariableName());
+}
+
+TEST(NameParser, MakeVariableName_MixedCapital) {
+ NameParser parser{"FOObarBaz"};
+ EXPECT_EQ("foobar_baz", parser.MakeVariableName());
+}
+
+TEST(NameParser, MakeInterfaceName) {
+ NameParser parser{"foo.bar.FooBar"};
+ EXPECT_EQ("FooBarInterface", parser.MakeInterfaceName(false));
+ EXPECT_EQ("foo::bar::FooBarInterface", parser.MakeInterfaceName(true));
+}
+
+TEST(NameParser, MakeProxyName) {
+ NameParser parser{"foo.bar.FooBar"};
+ EXPECT_EQ("FooBarProxy", parser.MakeProxyName(false));
+ EXPECT_EQ("foo::bar::FooBarProxy", parser.MakeProxyName(true));
+}
+
+TEST(NameParser, MakeAdaptorName) {
+ NameParser parser{"foo.bar.FooBar"};
+ EXPECT_EQ("FooBarAdaptor", parser.MakeAdaptorName(false));
+ EXPECT_EQ("foo::bar::FooBarAdaptor", parser.MakeAdaptorName(true));
+}
+
+TEST(NameParser, AddOpenNamespaces) {
+ std::string expected =
+ "namespace foo {\n"
+ "namespace bar {\n";
+ NameParser parser{"foo.bar.FooBar"};
+ IndentedText text;
+ parser.AddOpenNamespaces(&text, false);
+ EXPECT_EQ(expected, text.GetContents());
+}
+
+TEST(NameParser, AddOpenNamespaces_WithMainType) {
+ std::string expected =
+ "namespace foo {\n"
+ "namespace bar {\n"
+ "namespace FooBar {\n";
+ NameParser parser{"foo.bar.FooBar"};
+ IndentedText text;
+ parser.AddOpenNamespaces(&text, true);
+ EXPECT_EQ(expected, text.GetContents());
+}
+
+TEST(NameParser, AddCloseNamespaces) {
+ std::string expected =
+ "} // namespace bar\n"
+ "} // namespace foo\n";
+ NameParser parser{"foo.bar.FooBar"};
+ IndentedText text;
+ parser.AddCloseNamespaces(&text, false);
+ EXPECT_EQ(expected, text.GetContents());
+}
+
+TEST(NameParser, AddCloseNamespaces_WithMainType) {
+ std::string expected =
+ "} // namespace FooBar\n"
+ "} // namespace bar\n"
+ "} // namespace foo\n";
+ NameParser parser{"foo.bar.FooBar"};
+ IndentedText text;
+ parser.AddCloseNamespaces(&text, true);
+ EXPECT_EQ(expected, text.GetContents());
+}
+
+} // namespace chromeos_dbus_bindings
diff --git a/chromeos-dbus-bindings/proxy_generator.cc b/chromeos-dbus-bindings/proxy_generator.cc
new file mode 100644
index 0000000..2e96975
--- /dev/null
+++ b/chromeos-dbus-bindings/proxy_generator.cc
@@ -0,0 +1,1270 @@
+// 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/proxy_generator.h"
+
+#include <utility>
+
+#include <base/files/file_path.h>
+#include <base/logging.h>
+#include <base/strings/stringprintf.h>
+#include <chromeos/strings/string_utils.h>
+
+#include "chromeos-dbus-bindings/dbus_signature.h"
+#include "chromeos-dbus-bindings/indented_text.h"
+#include "chromeos-dbus-bindings/name_parser.h"
+
+using base::StringPrintf;
+using std::pair;
+using std::string;
+using std::vector;
+
+namespace chromeos_dbus_bindings {
+
+namespace {
+// Helper struct to encapsulate information about method call parameter during
+// code generation.
+struct ParamDef {
+ ParamDef(const string& param_type, const string& param_name, bool param_ref)
+ : type(param_type), name(param_name), is_const_ref(param_ref) {}
+
+ string type;
+ string name;
+ bool is_const_ref;
+};
+
+string GetParamString(const ParamDef& param_def) {
+ return StringPrintf(param_def.is_const_ref ? "const %s& %s" : "%s* %s",
+ param_def.type.c_str(), param_def.name.c_str());
+}
+} // anonymous namespace
+
+// static
+bool ProxyGenerator::GenerateProxies(
+ const ServiceConfig& config,
+ const std::vector<Interface>& interfaces,
+ const base::FilePath& output_file) {
+ IndentedText text;
+
+ text.AddLine("// Automatic generation of D-Bus interfaces:");
+ for (const auto& interface : interfaces) {
+ text.AddLine(StringPrintf("// - %s", interface.name.c_str()));
+ }
+ string header_guard = GenerateHeaderGuard(output_file);
+ text.AddLine(StringPrintf("#ifndef %s", header_guard.c_str()));
+ text.AddLine(StringPrintf("#define %s", header_guard.c_str()));
+ text.AddLine("#include <memory>");
+ text.AddLine("#include <string>");
+ text.AddLine("#include <vector>");
+ text.AddBlankLine();
+ text.AddLine("#include <base/bind.h>");
+ text.AddLine("#include <base/callback.h>");
+ text.AddLine("#include <base/logging.h>");
+ text.AddLine("#include <base/macros.h>");
+ text.AddLine("#include <base/memory/ref_counted.h>");
+ text.AddLine("#include <chromeos/any.h>");
+ text.AddLine("#include <chromeos/dbus/dbus_method_invoker.h>");
+ text.AddLine("#include <chromeos/dbus/dbus_property.h>");
+ text.AddLine("#include <chromeos/dbus/dbus_signal_handler.h>");
+ text.AddLine("#include <chromeos/errors/error.h>");
+ text.AddLine("#include <chromeos/variant_dictionary.h>");
+ text.AddLine("#include <dbus/bus.h>");
+ text.AddLine("#include <dbus/message.h>");
+ text.AddLine("#include <dbus/object_manager.h>");
+ text.AddLine("#include <dbus/object_path.h>");
+ text.AddLine("#include <dbus/object_proxy.h>");
+ text.AddBlankLine();
+
+ if (!config.object_manager.name.empty()) {
+ // Add forward-declaration for Object Manager proxy class.
+ NameParser parser{config.object_manager.name};
+ parser.AddOpenNamespaces(&text, false);
+ text.AddLine(StringPrintf("class %s;",
+ parser.MakeProxyName(false).c_str()));
+ parser.AddCloseNamespaces(&text, false);
+ text.AddBlankLine();
+ }
+
+ for (const auto& interface : interfaces) {
+ GenerateInterfaceProxyInterface(config, interface, &text);
+ GenerateInterfaceProxy(config, interface, &text);
+ }
+
+ ObjectManager::GenerateProxy(config, interfaces, &text);
+
+ text.AddLine(StringPrintf("#endif // %s", header_guard.c_str()));
+ return WriteTextToFile(output_file, text);
+}
+
+// static
+bool ProxyGenerator::GenerateMocks(const ServiceConfig& config,
+ const std::vector<Interface>& interfaces,
+ const base::FilePath& mock_file,
+ const base::FilePath& proxy_file) {
+ IndentedText text;
+
+ text.AddLine("// Automatic generation of D-Bus interface mock proxies for:");
+ for (const auto& interface : interfaces) {
+ text.AddLine(StringPrintf("// - %s", interface.name.c_str()));
+ }
+ string header_guard = GenerateHeaderGuard(mock_file);
+ text.AddLine(StringPrintf("#ifndef %s", header_guard.c_str()));
+ text.AddLine(StringPrintf("#define %s", header_guard.c_str()));
+ text.AddLine("#include <string>");
+ text.AddLine("#include <vector>");
+ text.AddBlankLine();
+ text.AddLine("#include <base/callback_forward.h>");
+ text.AddLine("#include <base/macros.h>");
+ text.AddLine("#include <chromeos/any.h>");
+ text.AddLine("#include <chromeos/errors/error.h>");
+ text.AddLine("#include <chromeos/variant_dictionary.h>");
+ text.AddLine("#include <gmock/gmock.h>");
+ text.AddBlankLine();
+
+ if (!proxy_file.empty()) {
+ // If we have a proxy header file, it would have the proxy interfaces we
+ // need to base our mocks on, so we need to include that header file.
+ // Generate a relative path from |mock_file| to |proxy_file|.
+
+ // First, get the path components for both source and destination paths.
+ std::vector<base::FilePath::StringType> src_components;
+ mock_file.DirName().GetComponents(&src_components);
+ std::vector<base::FilePath::StringType> dest_components;
+ proxy_file.DirName().GetComponents(&dest_components);
+
+ // Find the common root.
+
+ // I wish we had C++14 and its 4-parameter version of std::mismatch()...
+ auto src_end = src_components.end();
+ if (src_components.size() > dest_components.size())
+ src_end = src_components.begin() + dest_components.size();
+
+ auto mismatch_pair =
+ std::mismatch(src_components.begin(), src_end, dest_components.begin());
+
+ // For each remaining components in the |src_components|, generate the
+ // parent directory references ("..").
+ size_t src_count = std::distance(mismatch_pair.first, src_components.end());
+ std::vector<base::FilePath::StringType> components{
+ src_count, base::FilePath::kParentDirectory};
+ // Append the remaining components from |dest_components|.
+ components.insert(components.end(),
+ mismatch_pair.second, dest_components.end());
+ // Finally, add the base name of the target file name.
+ components.push_back(proxy_file.BaseName().value());
+ // Now reconstruct the relative path.
+ base::FilePath relative_path{base::FilePath::kCurrentDirectory};
+ for (const auto& component : components)
+ relative_path = relative_path.Append(component);
+ text.AddLine(StringPrintf("#include \"%s\"",
+ relative_path.value().c_str()));
+ text.AddBlankLine();
+ }
+
+ for (const auto& interface : interfaces) {
+ // If we have no proxy file, we need the abstract interfaces generated here.
+ if (proxy_file.empty())
+ GenerateInterfaceProxyInterface(config, interface, &text);
+ GenerateInterfaceMock(config, interface, &text);
+ }
+
+ text.AddLine(StringPrintf("#endif // %s", header_guard.c_str()));
+ return WriteTextToFile(mock_file, text);
+}
+
+// static
+void ProxyGenerator::GenerateInterfaceProxyInterface(
+ const ServiceConfig& config,
+ const Interface& interface,
+ IndentedText* text) {
+ NameParser parser{interface.name};
+ string proxy_name = parser.MakeProxyName(false);
+ string base_interface_name = proxy_name + "Interface";
+
+ parser.AddOpenNamespaces(text, false);
+ text->AddBlankLine();
+
+ text->AddLine(StringPrintf("// Abstract interface proxy for %s.",
+ parser.MakeFullCppName().c_str()));
+ text->AddComments(interface.doc_string);
+ text->AddLine(StringPrintf("class %s {", base_interface_name.c_str()));
+ text->AddLineWithOffset("public:", kScopeOffset);
+ text->PushOffset(kBlockOffset);
+ text->AddLine(
+ StringPrintf("virtual ~%s() = default;", base_interface_name.c_str()));
+
+ for (const auto& method : interface.methods) {
+ AddMethodProxy(method, interface.name, true, text);
+ AddAsyncMethodProxy(method, interface.name, true, text);
+ }
+ for (const auto& signal : interface.signals) {
+ AddSignalHandlerRegistration(signal, interface.name, true, text);
+ }
+ AddProperties(config, interface, true, text);
+
+ text->PopOffset();
+ text->AddLine("};");
+ text->AddBlankLine();
+
+ parser.AddCloseNamespaces(text, false);
+ text->AddBlankLine();
+}
+
+// static
+void ProxyGenerator::GenerateInterfaceProxy(const ServiceConfig& config,
+ const Interface& interface,
+ IndentedText* text) {
+ NameParser parser{interface.name};
+ string proxy_name = parser.MakeProxyName(false);
+ string base_interface_name = proxy_name + "Interface";
+
+ parser.AddOpenNamespaces(text, false);
+ text->AddBlankLine();
+
+ text->AddLine(StringPrintf("// Interface proxy for %s.",
+ parser.MakeFullCppName().c_str()));
+ text->AddComments(interface.doc_string);
+ text->AddLine(StringPrintf("class %s final : public %s {",
+ proxy_name.c_str(), base_interface_name.c_str()));
+ text->AddLineWithOffset("public:", kScopeOffset);
+ text->PushOffset(kBlockOffset);
+ AddPropertySet(config, interface, text);
+ AddConstructor(config, interface, proxy_name, text);
+ AddDestructor(proxy_name, text);
+ for (const auto& signal : interface.signals) {
+ AddSignalHandlerRegistration(signal, interface.name, false, text);
+ }
+ AddReleaseObjectProxy(text);
+ AddGetObjectPath(text);
+ AddGetObjectProxy(text);
+ if (!config.object_manager.name.empty() && !interface.properties.empty())
+ AddPropertyPublicMethods(proxy_name, text);
+ for (const auto& method : interface.methods) {
+ AddMethodProxy(method, interface.name, false, text);
+ AddAsyncMethodProxy(method, interface.name, false, text);
+ }
+ AddProperties(config, interface, false, text);
+
+ text->PopOffset();
+ text->AddBlankLine();
+ text->AddLineWithOffset("private:", kScopeOffset);
+
+ text->PushOffset(kBlockOffset);
+ if (!config.object_manager.name.empty() && !interface.properties.empty())
+ AddOnPropertyChanged(text);
+ text->AddLine("scoped_refptr<dbus::Bus> bus_;");
+ if (config.service_name.empty()) {
+ text->AddLine("std::string service_name_;");
+ } else {
+ text->AddLine(StringPrintf("const std::string service_name_{\"%s\"};",
+ config.service_name.c_str()));
+ }
+ if (interface.path.empty()) {
+ text->AddLine("dbus::ObjectPath object_path_;");
+ } else {
+ text->AddLine(StringPrintf("const dbus::ObjectPath object_path_{\"%s\"};",
+ interface.path.c_str()));
+ }
+ if (!config.object_manager.name.empty() && !interface.properties.empty()) {
+ text->AddLine("PropertySet* property_set_;");
+ text->AddLine(StringPrintf("base::Callback<void(%s*, const std::string&)> "
+ "on_property_changed_;",
+ proxy_name.c_str()));
+ }
+ text->AddLine("dbus::ObjectProxy* dbus_object_proxy_;");
+ text->AddBlankLine();
+
+ if (!config.object_manager.name.empty() && !interface.properties.empty()) {
+ text->AddLine(StringPrintf(
+ "friend class %s;",
+ NameParser{config.object_manager.name}.MakeProxyName(true).c_str()));
+ }
+ text->AddLine(StringPrintf("DISALLOW_COPY_AND_ASSIGN(%s);",
+ proxy_name.c_str()));
+ text->PopOffset();
+ text->AddLine("};");
+
+ text->AddBlankLine();
+
+ parser.AddCloseNamespaces(text, false);
+
+ text->AddBlankLine();
+}
+
+// static
+void ProxyGenerator::GenerateInterfaceMock(const ServiceConfig& config,
+ const Interface& interface,
+ IndentedText* text) {
+ NameParser parser{interface.name};
+ string proxy_name = parser.MakeProxyName(false);
+ string base_interface_name = proxy_name + "Interface";
+ string mock_name = proxy_name + "Mock";
+
+ parser.AddOpenNamespaces(text, false);
+ text->AddBlankLine();
+
+ text->AddLine(StringPrintf("// Mock object for %s.",
+ base_interface_name.c_str()));
+ text->AddLine(StringPrintf("class %s final : public %s {",
+ mock_name.c_str(), base_interface_name.c_str()));
+ text->AddLineWithOffset("public:", kScopeOffset);
+ text->PushOffset(kBlockOffset);
+ text->AddLine(StringPrintf("%s() = default;", mock_name.c_str()));
+ text->AddBlankLine();
+
+ for (const auto& method : interface.methods) {
+ AddMethodMock(method, interface.name, text);
+ AddAsyncMethodMock(method, interface.name, text);
+ }
+ for (const auto& signal : interface.signals) {
+ AddSignalHandlerRegistrationMock(signal, text);
+ }
+
+ DbusSignature signature;
+ for (const auto& prop : interface.properties) {
+ string type;
+ CHECK(signature.Parse(prop.type, &type));
+ MakeConstReferenceIfNeeded(&type);
+ string name = NameParser{prop.name}.MakeVariableName();
+ text->AddLine(StringPrintf("MOCK_CONST_METHOD0(%s, %s());",
+ name.c_str(), type.c_str()));
+ }
+
+ text->PopOffset();
+ text->AddBlankLine();
+ text->AddLineWithOffset("private:", kScopeOffset);
+ text->AddLineWithOffset(StringPrintf("DISALLOW_COPY_AND_ASSIGN(%s);",
+ mock_name.c_str()),
+ kBlockOffset);
+ text->AddLine("};");
+
+ parser.AddCloseNamespaces(text, false);
+ text->AddBlankLine();
+}
+
+// static
+void ProxyGenerator::AddConstructor(const ServiceConfig& config,
+ const Interface& interface,
+ const string& class_name,
+ IndentedText* text) {
+ IndentedText block;
+ vector<ParamDef> args{{"scoped_refptr<dbus::Bus>", "bus", true}};
+ if (config.service_name.empty())
+ args.emplace_back("std::string", "service_name", true);
+ if (interface.path.empty())
+ args.emplace_back("dbus::ObjectPath", "object_path", true);
+ if (!config.object_manager.name.empty() && !interface.properties.empty())
+ args.emplace_back("PropertySet", "property_set", false);
+
+ if (args.size() == 1) {
+ block.AddLine(StringPrintf("%s(%s) :", class_name.c_str(),
+ GetParamString(args.front()).c_str()));
+ } else {
+ block.AddLine(StringPrintf("%s(", class_name.c_str()));
+ block.PushOffset(kLineContinuationOffset);
+ for (size_t i = 0; i < args.size() - 1; i++) {
+ block.AddLine(StringPrintf("%s,", GetParamString(args[i]).c_str()));
+ }
+ block.AddLine(StringPrintf("%s) :", GetParamString(args.back()).c_str()));
+ }
+ block.PushOffset(kLineContinuationOffset);
+ for (const auto& arg : args) {
+ block.AddLine(StringPrintf("%s_{%s},", arg.name.c_str(),
+ arg.name.c_str()));
+ }
+ block.AddLine("dbus_object_proxy_{");
+ block.AddLineWithOffset(
+ "bus_->GetObjectProxy(service_name_, object_path_)} {",
+ kLineContinuationOffset);
+ block.PopOffset();
+ if (args.size() > 1)
+ block.PopOffset();
+ block.AddLine("}");
+ block.AddBlankLine();
+ text->AddBlock(block);
+}
+
+// static
+void ProxyGenerator::AddDestructor(const string& class_name,
+ IndentedText* text) {
+ IndentedText block;
+ block.AddLine(StringPrintf("~%s() override {", class_name.c_str()));
+ block.AddLine("}");
+ text->AddBlock(block);
+}
+
+// static
+void ProxyGenerator::AddReleaseObjectProxy(IndentedText* text) {
+ text->AddBlankLine();
+ text->AddLine("void ReleaseObjectProxy(const base::Closure& callback) {");
+ text->AddLineWithOffset(
+ "bus_->RemoveObjectProxy(service_name_, object_path_, callback);",
+ kBlockOffset);
+ text->AddLine("}");
+}
+
+// static
+void ProxyGenerator::AddGetObjectPath(IndentedText* text) {
+ text->AddBlankLine();
+ text->AddLine("const dbus::ObjectPath& GetObjectPath() const {");
+ text->AddLineWithOffset("return object_path_;", kBlockOffset);
+ text->AddLine("}");
+}
+
+// static
+void ProxyGenerator::AddGetObjectProxy(IndentedText* text) {
+ text->AddBlankLine();
+ text->AddLine("dbus::ObjectProxy* GetObjectProxy() const { "
+ "return dbus_object_proxy_; }");
+}
+
+// static
+void ProxyGenerator::AddPropertyPublicMethods(const string& class_name,
+ IndentedText* text) {
+ text->AddBlankLine();
+ text->AddLine("void SetPropertyChangedCallback(");
+ text->AddLineWithOffset(
+ StringPrintf("const base::Callback<void(%s*, "
+ "const std::string&)>& callback) {", class_name.c_str()),
+ kLineContinuationOffset);
+ text->AddLineWithOffset("on_property_changed_ = callback;", kBlockOffset);
+ text->AddLine("}");
+ text->AddBlankLine();
+
+ text->AddLine("const PropertySet* GetProperties() const "
+ "{ return property_set_; }");
+ text->AddLine("PropertySet* GetProperties() { return property_set_; }");
+}
+
+// static
+void ProxyGenerator::AddOnPropertyChanged(IndentedText* text) {
+ text->AddLine("void OnPropertyChanged(const std::string& property_name) {");
+ text->PushOffset(kBlockOffset);
+ text->AddLine("if (!on_property_changed_.is_null())");
+ text->PushOffset(kBlockOffset);
+ text->AddLine("on_property_changed_.Run(this, property_name);");
+ text->PopOffset();
+ text->PopOffset();
+ text->AddLine("}");
+ text->AddBlankLine();
+}
+
+void ProxyGenerator::AddSignalHandlerRegistration(
+ const Interface::Signal& signal,
+ const string& interface_name,
+ bool declaration_only,
+ IndentedText* text) {
+ IndentedText block;
+ block.AddBlankLine();
+ block.AddLine(StringPrintf("%svoid Register%sSignalHandler(",
+ declaration_only ? "virtual " : "",
+ signal.name.c_str()));
+ block.PushOffset(kLineContinuationOffset);
+ AddSignalCallbackArg(signal, false, &block);
+ block.AddLine(StringPrintf(
+ "dbus::ObjectProxy::OnConnectedCallback on_connected_callback)%s",
+ declaration_only ? " = 0;" : " override {"));
+ if (!declaration_only) {
+ block.PopOffset(); // Method signature arguments
+ block.PushOffset(kBlockOffset);
+ block.AddLine("chromeos::dbus_utils::ConnectToSignal(");
+ block.PushOffset(kLineContinuationOffset);
+ block.AddLine("dbus_object_proxy_,");
+ block.AddLine(StringPrintf("\"%s\",", interface_name.c_str()));
+ block.AddLine(StringPrintf("\"%s\",", signal.name.c_str()));
+ block.AddLine("signal_callback,");
+ block.AddLine("on_connected_callback);");
+ block.PopOffset(); // Function call line continuation
+ block.PopOffset(); // Method body
+ block.AddLine("}");
+ }
+ text->AddBlock(block);
+}
+
+// static
+void ProxyGenerator::AddPropertySet(const ServiceConfig& config,
+ const Interface& interface,
+ IndentedText* text) {
+ // Must have ObjectManager in order for property system to work correctly.
+ if (config.object_manager.name.empty())
+ return;
+
+ IndentedText block;
+ block.AddLine("class PropertySet : public dbus::PropertySet {");
+ block.AddLineWithOffset("public:", kScopeOffset);
+ block.PushOffset(kBlockOffset);
+ block.AddLineAndPushOffsetTo("PropertySet(dbus::ObjectProxy* object_proxy,",
+ 1, '(');
+ block.AddLine("const PropertyChangedCallback& callback)");
+ block.PopOffset();
+ block.PushOffset(kLineContinuationOffset);
+ block.AddLineAndPushOffsetTo(": dbus::PropertySet{object_proxy,", 1, '{');
+ block.AddLine(StringPrintf("\"%s\",", interface.name.c_str()));
+ block.AddLine("callback} {");
+ block.PopOffset();
+ block.PopOffset();
+ block.PushOffset(kBlockOffset);
+ for (const auto& prop : interface.properties) {
+ block.AddLine(
+ StringPrintf("RegisterProperty(%sName(), &%s);",
+ prop.name.c_str(),
+ NameParser{prop.name}.MakeVariableName().c_str()));
+ }
+ block.PopOffset();
+ block.AddLine("}");
+ block.AddBlankLine();
+
+ DbusSignature signature;
+ for (const auto& prop : interface.properties) {
+ string type;
+ CHECK(signature.Parse(prop.type, &type));
+ block.AddLine(
+ StringPrintf("chromeos::dbus_utils::Property<%s> %s;",
+ type.c_str(),
+ NameParser{prop.name}.MakeVariableName().c_str()));
+ }
+ block.AddBlankLine();
+
+ block.PopOffset();
+ block.AddLineWithOffset("private:", kScopeOffset);
+ block.AddLineWithOffset("DISALLOW_COPY_AND_ASSIGN(PropertySet);",
+ kBlockOffset);
+ block.AddLine("};");
+ block.AddBlankLine();
+
+ text->AddBlock(block);
+}
+
+// static
+void ProxyGenerator::AddProperties(const ServiceConfig& config,
+ const Interface& interface,
+ bool declaration_only,
+ IndentedText* text) {
+ // Must have ObjectManager in order for property system to work correctly.
+ if (config.object_manager.name.empty())
+ return;
+
+ if (declaration_only && !interface.properties.empty())
+ text->AddBlankLine();
+
+ DbusSignature signature;
+ for (const auto& prop : interface.properties) {
+ if (declaration_only) {
+ text->AddLine(
+ StringPrintf("static const char* %sName() { return \"%s\"; }",
+ prop.name.c_str(),
+ prop.name.c_str()));
+ }
+ string type;
+ CHECK(signature.Parse(prop.type, &type));
+ MakeConstReferenceIfNeeded(&type);
+ string name = NameParser{prop.name}.MakeVariableName();
+ if (!declaration_only)
+ text->AddBlankLine();
+ text->AddLine(
+ StringPrintf("%s%s %s() const%s",
+ declaration_only ? "virtual " : "",
+ type.c_str(),
+ name.c_str(),
+ declaration_only ? " = 0;" : " override {"));
+ if (!declaration_only) {
+ text->AddLineWithOffset(
+ StringPrintf("return property_set_->%s.value();", name.c_str()),
+ kBlockOffset);
+ text->AddLine("}");
+ }
+ }
+}
+
+// static
+void ProxyGenerator::AddMethodProxy(const Interface::Method& method,
+ const string& interface_name,
+ bool declaration_only,
+ IndentedText* text) {
+ IndentedText block;
+ DbusSignature signature;
+ block.AddBlankLine();
+ block.AddComments(method.doc_string);
+ block.AddLine(StringPrintf("%sbool %s(",
+ declaration_only ? "virtual " : "",
+ method.name.c_str()));
+ block.PushOffset(kLineContinuationOffset);
+ vector<string> argument_names;
+ int argument_number = 0;
+ for (const auto& argument : method.input_arguments) {
+ string argument_type;
+ CHECK(signature.Parse(argument.type, &argument_type));
+ MakeConstReferenceIfNeeded(&argument_type);
+ string argument_name = GetArgName("in", argument.name, ++argument_number);
+ argument_names.push_back(argument_name);
+ block.AddLine(StringPrintf(
+ "%s %s,", argument_type.c_str(), argument_name.c_str()));
+ }
+ vector<string> out_param_names{"response.get()", "error"};
+ for (const auto& argument : method.output_arguments) {
+ string argument_type;
+ CHECK(signature.Parse(argument.type, &argument_type));
+ string argument_name = GetArgName("out", argument.name, ++argument_number);
+ out_param_names.push_back(argument_name);
+ block.AddLine(StringPrintf(
+ "%s* %s,", argument_type.c_str(), argument_name.c_str()));
+ }
+ block.AddLine("chromeos::ErrorPtr* error,");
+ block.AddLine(
+ StringPrintf("int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)%s",
+ declaration_only ? " = 0;" : " override {"));
+ block.PopOffset();
+ if (!declaration_only) {
+ block.PushOffset(kBlockOffset);
+
+ block.AddLine(
+ "auto response = chromeos::dbus_utils::CallMethodAndBlockWithTimeout(");
+ block.PushOffset(kLineContinuationOffset);
+ block.AddLine("timeout_ms,");
+ block.AddLine("dbus_object_proxy_,");
+ block.AddLine(StringPrintf("\"%s\",", interface_name.c_str()));
+ block.AddLine(StringPrintf("\"%s\",", method.name.c_str()));
+ string last_argument = "error";
+ for (const auto& argument_name : argument_names) {
+ block.AddLine(StringPrintf("%s,", last_argument.c_str()));
+ last_argument = argument_name;
+ }
+ block.AddLine(StringPrintf("%s);", last_argument.c_str()));
+ block.PopOffset();
+
+ block.AddLine("return response && "
+ "chromeos::dbus_utils::ExtractMethodCallResults(");
+ block.PushOffset(kLineContinuationOffset);
+ block.AddLine(chromeos::string_utils::Join(", ", out_param_names) + ");");
+ block.PopOffset();
+ block.PopOffset();
+ block.AddLine("}");
+ }
+ text->AddBlock(block);
+}
+
+// static
+void ProxyGenerator::AddAsyncMethodProxy(const Interface::Method& method,
+ const string& interface_name,
+ bool declaration_only,
+ IndentedText* text) {
+ IndentedText block;
+ DbusSignature signature;
+ block.AddBlankLine();
+ block.AddComments(method.doc_string);
+ block.AddLine(StringPrintf("%svoid %sAsync(",
+ declaration_only ? "virtual " : "",
+ method.name.c_str()));
+ block.PushOffset(kLineContinuationOffset);
+ vector<string> argument_names;
+ int argument_number = 0;
+ for (const auto& argument : method.input_arguments) {
+ string argument_type;
+ CHECK(signature.Parse(argument.type, &argument_type));
+ MakeConstReferenceIfNeeded(&argument_type);
+ string argument_name = GetArgName("in", argument.name, ++argument_number);
+ argument_names.push_back(argument_name);
+ block.AddLine(StringPrintf(
+ "%s %s,", argument_type.c_str(), argument_name.c_str()));
+ }
+ vector<string> out_params;
+ for (const auto& argument : method.output_arguments) {
+ string argument_type;
+ CHECK(signature.Parse(argument.type, &argument_type));
+ MakeConstReferenceIfNeeded(&argument_type);
+ if (!argument.name.empty())
+ base::StringAppendF(&argument_type, " /*%s*/", argument.name.c_str());
+ out_params.push_back(argument_type);
+ }
+ block.AddLine(StringPrintf(
+ "const base::Callback<void(%s)>& success_callback,",
+ chromeos::string_utils::Join(", ", out_params).c_str()));
+ block.AddLine(
+ "const base::Callback<void(chromeos::Error*)>& error_callback,");
+ block.AddLine(
+ StringPrintf("int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)%s",
+ declaration_only ? " = 0;" : " override {"));
+ block.PopOffset();
+ if (!declaration_only) {
+ block.PushOffset(kBlockOffset);
+
+ block.AddLine("chromeos::dbus_utils::CallMethodWithTimeout(");
+ block.PushOffset(kLineContinuationOffset);
+ block.AddLine("timeout_ms,");
+ block.AddLine("dbus_object_proxy_,");
+ block.AddLine(StringPrintf("\"%s\",", interface_name.c_str()));
+ block.AddLine(StringPrintf("\"%s\",", method.name.c_str()));
+ block.AddLine("success_callback,");
+ string last_argument = "error_callback";
+ for (const auto& argument_name : argument_names) {
+ block.AddLine(StringPrintf("%s,", last_argument.c_str()));
+ last_argument = argument_name;
+ }
+ block.AddLine(StringPrintf("%s);", last_argument.c_str()));
+ block.PopOffset();
+
+ block.PopOffset();
+ block.AddLine("}");
+ }
+ text->AddBlock(block);
+}
+
+// static
+void ProxyGenerator::AddMethodMock(const Interface::Method& method,
+ const string& interface_name,
+ IndentedText* text) {
+ IndentedText block;
+ DbusSignature signature;
+ vector<string> arguments;
+ for (const auto& argument : method.input_arguments) {
+ string argument_type;
+ CHECK(signature.Parse(argument.type, &argument_type));
+ MakeConstReferenceIfNeeded(&argument_type);
+ if (!argument.name.empty())
+ base::StringAppendF(&argument_type, " /*in_%s*/", argument.name.c_str());
+ arguments.push_back(argument_type);
+ }
+ for (const auto& argument : method.output_arguments) {
+ string argument_type;
+ CHECK(signature.Parse(argument.type, &argument_type));
+ argument_type += '*';
+ if (!argument.name.empty())
+ base::StringAppendF(&argument_type, " /*out_%s*/", argument.name.c_str());
+ arguments.push_back(argument_type);
+ }
+ arguments.push_back("chromeos::ErrorPtr* /*error*/");
+ arguments.push_back("int /*timeout_ms*/");
+
+ block.AddLineAndPushOffsetTo(
+ StringPrintf("MOCK_METHOD%ju(%s,", arguments.size(), method.name.c_str()),
+ 1, '(');
+ block.AddLineAndPushOffsetTo(
+ StringPrintf("bool(%s,", arguments.front().c_str()), 1, '(');
+ for (size_t i = 1; i < arguments.size() - 1; i++)
+ block.AddLine(StringPrintf("%s,", arguments[i].c_str()));
+ block.AddLine(StringPrintf("%s));", arguments.back().c_str()));
+ block.PopOffset();
+ block.PopOffset();
+ text->AddBlock(block);
+}
+
+// static
+void ProxyGenerator::AddAsyncMethodMock(const Interface::Method& method,
+ const string& interface_name,
+ IndentedText* text) {
+ IndentedText block;
+ DbusSignature signature;
+ vector<string> arguments;
+ for (const auto& argument : method.input_arguments) {
+ string argument_type;
+ CHECK(signature.Parse(argument.type, &argument_type));
+ MakeConstReferenceIfNeeded(&argument_type);
+ if (!argument.name.empty())
+ base::StringAppendF(&argument_type, " /*in_%s*/", argument.name.c_str());
+ arguments.push_back(argument_type);
+ }
+ vector<string> out_params;
+ for (const auto& argument : method.output_arguments) {
+ string argument_type;
+ CHECK(signature.Parse(argument.type, &argument_type));
+ MakeConstReferenceIfNeeded(&argument_type);
+ if (!argument.name.empty())
+ base::StringAppendF(&argument_type, " /*%s*/", argument.name.c_str());
+ out_params.push_back(argument_type);
+ }
+ arguments.push_back(StringPrintf(
+ "const base::Callback<void(%s)>& /*success_callback*/",
+ chromeos::string_utils::Join(", ", out_params).c_str()));
+ arguments.push_back(
+ "const base::Callback<void(chromeos::Error*)>& /*error_callback*/");
+ arguments.push_back("int /*timeout_ms*/");
+
+ block.AddLineAndPushOffsetTo(
+ StringPrintf("MOCK_METHOD%ju(%sAsync,", arguments.size(),
+ method.name.c_str()), 1, '(');
+ block.AddLineAndPushOffsetTo(
+ StringPrintf("void(%s,", arguments.front().c_str()), 1, '(');
+ for (size_t i = 1; i < arguments.size() - 1; i++)
+ block.AddLine(StringPrintf("%s,", arguments[i].c_str()));
+ block.AddLine(StringPrintf("%s));", arguments.back().c_str()));
+ block.PopOffset();
+ block.PopOffset();
+ text->AddBlock(block);
+}
+
+// static
+void ProxyGenerator::AddSignalHandlerRegistrationMock(
+ const Interface::Signal& signal,
+ IndentedText* text) {
+ IndentedText callback_arg_text;
+ AddSignalCallbackArg(signal, true, &callback_arg_text);
+ vector<string> arg_lines = callback_arg_text.GetLines();
+
+ IndentedText block;
+ block.AddLineAndPushOffsetTo(
+ StringPrintf("MOCK_METHOD2(Register%sSignalHandler,",
+ signal.name.c_str()),
+ 1, '(');
+ for (size_t i = 0; i < arg_lines.size(); ++i) {
+ if (i == 0)
+ block.AddLineAndPushOffsetTo("void(" + arg_lines[i], 1, '(');
+ else
+ block.AddLine(arg_lines[i]);
+ }
+ block.AddLine(
+ "dbus::ObjectProxy::OnConnectedCallback /*on_connected_callback*/));");
+ text->AddBlock(block);
+}
+
+// static
+void ProxyGenerator::AddSignalCallbackArg(const Interface::Signal& signal,
+ bool comment_arg_name,
+ IndentedText* block) {
+ DbusSignature signature;
+ string signal_callback = StringPrintf("%ssignal_callback%s",
+ comment_arg_name ? "/*" : "",
+ comment_arg_name ? "*/" : "");
+ if (signal.arguments.empty()) {
+ block->AddLine(StringPrintf("const base::Closure& %s,",
+ signal_callback.c_str()));
+ } else {
+ string last_argument;
+ string prefix{"const base::Callback<void("};
+ for (const auto argument : signal.arguments) {
+ if (!last_argument.empty()) {
+ if (!prefix.empty()) {
+ block->AddLineAndPushOffsetTo(
+ StringPrintf("%s%s,", prefix.c_str(), last_argument.c_str()),
+ 1, '(');
+ prefix.clear();
+ } else {
+ block->AddLine(StringPrintf("%s,", last_argument.c_str()));
+ }
+ }
+ CHECK(signature.Parse(argument.type, &last_argument));
+ MakeConstReferenceIfNeeded(&last_argument);
+ }
+ block->AddLine(StringPrintf("%s%s)>& %s,",
+ prefix.c_str(),
+ last_argument.c_str(),
+ signal_callback.c_str()));
+ if (prefix.empty()) {
+ block->PopOffset();
+ }
+ }
+}
+
+// static
+void ProxyGenerator::ObjectManager::GenerateProxy(
+ const ServiceConfig& config,
+ const std::vector<Interface>& interfaces,
+ IndentedText* text) {
+ if (config.object_manager.name.empty())
+ return;
+
+ NameParser object_manager{config.object_manager.name};
+ object_manager.AddOpenNamespaces(text, false);
+ text->AddBlankLine();
+
+ string class_name = object_manager.type_name + "Proxy";
+ text->AddLine(StringPrintf("class %s : "
+ "public dbus::ObjectManager::Interface {",
+ class_name.c_str()));
+ text->AddLineWithOffset("public:", kScopeOffset);
+ text->PushOffset(kBlockOffset);
+
+ AddConstructor(config, class_name, interfaces, text);
+ AddDestructor(class_name, interfaces, text);
+ AddGetObjectManagerProxy(text);
+ for (const auto& itf : interfaces) {
+ AddInterfaceAccessors(itf, text);
+ }
+ text->PopOffset();
+
+ text->AddLineWithOffset("private:", kScopeOffset);
+ text->PushOffset(kBlockOffset);
+ AddOnPropertyChanged(interfaces, text);
+ AddObjectAdded(config, interfaces, text);
+ AddObjectRemoved(interfaces, text);
+ AddCreateProperties(interfaces, class_name, text);
+ AddDataMembers(config, interfaces, class_name, text);
+
+ text->AddLine(StringPrintf("DISALLOW_COPY_AND_ASSIGN(%s);",
+ class_name.c_str()));
+ text->PopOffset();
+ text->AddLine("};");
+ text->AddBlankLine();
+ object_manager.AddCloseNamespaces(text, false);
+ text->AddBlankLine();
+}
+
+void ProxyGenerator::ObjectManager::AddConstructor(
+ const ServiceConfig& config,
+ const std::string& class_name,
+ const std::vector<Interface>& interfaces,
+ IndentedText* text) {
+ if (config.service_name.empty()) {
+ text->AddLineAndPushOffsetTo(
+ StringPrintf("%s(const scoped_refptr<dbus::Bus>& bus,",
+ class_name.c_str()),
+ 1, '(');
+ text->AddLine("const std::string& service_name)");
+ text->PopOffset();
+ } else {
+ text->AddLine(StringPrintf("%s(const scoped_refptr<dbus::Bus>& bus)",
+ class_name.c_str()));
+ }
+ text->PushOffset(kLineContinuationOffset);
+ text->AddLine(": bus_{bus},");
+ text->PushOffset(kBlockOffset);
+ if (config.service_name.empty()) {
+ text->AddLine("service_name_{service_name},");
+ }
+ text->AddLine("dbus_object_manager_{bus->GetObjectManager(");
+ text->PushOffset(kLineContinuationOffset);
+ if (config.service_name.empty()) {
+ text->AddLine("service_name,");
+ } else {
+ text->AddLine(StringPrintf("\"%s\",", config.service_name.c_str()));
+ }
+ text->AddLine(StringPrintf("dbus::ObjectPath{\"%s\"})} {",
+ config.object_manager.object_path.c_str()));
+ text->PopOffset();
+ text->PopOffset();
+ text->PopOffset();
+ text->PushOffset(kBlockOffset);
+ for (const auto& itf : interfaces) {
+ text->AddLine(
+ StringPrintf("dbus_object_manager_->RegisterInterface(\"%s\", this);",
+ itf.name.c_str()));
+ }
+ text->PopOffset();
+ text->AddLine("}");
+ text->AddBlankLine();
+}
+
+void ProxyGenerator::ObjectManager::AddDestructor(
+ const std::string& class_name,
+ const std::vector<Interface>& interfaces,
+ IndentedText* text) {
+ text->AddLine(StringPrintf("~%s() override {", class_name.c_str()));
+ text->PushOffset(kBlockOffset);
+ for (const auto& itf : interfaces) {
+ text->AddLine(
+ StringPrintf("dbus_object_manager_->UnregisterInterface(\"%s\");",
+ itf.name.c_str()));
+ }
+ text->PopOffset();
+ text->AddLine("}");
+ text->AddBlankLine();
+}
+
+void ProxyGenerator::ObjectManager::AddGetObjectManagerProxy(
+ IndentedText* text) {
+ text->AddLine("dbus::ObjectManager* GetObjectManagerProxy() const {");
+ text->AddLineWithOffset("return dbus_object_manager_;", kBlockOffset);
+ text->AddLine("}");
+ text->AddBlankLine();
+}
+
+void ProxyGenerator::ObjectManager::AddInterfaceAccessors(
+ const Interface& interface,
+ IndentedText* text) {
+ NameParser itf_name{interface.name};
+ string map_name = itf_name.MakeVariableName() + "_instances_";
+
+ // GetProxy().
+ if (interface.path.empty()) {
+ // We have no fixed path, so there could be multiple instances of this itf.
+ text->AddLine(StringPrintf("%s* Get%s(",
+ itf_name.MakeProxyName(true).c_str(),
+ itf_name.MakeProxyName(false).c_str()));
+ text->PushOffset(kLineContinuationOffset);
+ text->AddLine("const dbus::ObjectPath& object_path) {");
+ text->PopOffset();
+ text->PushOffset(kBlockOffset);
+ text->AddLine(StringPrintf("auto p = %s.find(object_path);",
+ map_name.c_str()));
+ text->AddLine(StringPrintf("if (p != %s.end())", map_name.c_str()));
+ text->PushOffset(kBlockOffset);
+ text->AddLine("return p->second.get();");
+ text->PopOffset();
+ text->AddLine("return nullptr;");
+ text->PopOffset();
+ text->AddLine("}");
+ } else {
+ // We have a fixed path, so the object could be considered a "singleton".
+ // Skip the object_path parameter and return the first available instance.
+ text->AddLine(StringPrintf("%s* Get%s() {",
+ itf_name.MakeProxyName(true).c_str(),
+ itf_name.MakeProxyName(false).c_str()));
+ text->PushOffset(kBlockOffset);
+ text->AddLine(StringPrintf("if (%s.empty())", map_name.c_str()));
+ text->AddLineWithOffset("return nullptr;", kBlockOffset);
+ text->AddLine(StringPrintf("return %s.begin()->second.get();",
+ map_name.c_str()));
+ text->PopOffset();
+ text->AddLine("}");
+ }
+
+ // GetInstances().
+ text->AddLine(StringPrintf("std::vector<%s*> Get%sInstances() const {",
+ itf_name.MakeProxyName(true).c_str(),
+ itf_name.type_name.c_str()));
+ text->PushOffset(kBlockOffset);
+ text->AddLine(StringPrintf("std::vector<%s*> values;",
+ itf_name.MakeProxyName(true).c_str()));
+ text->AddLine(StringPrintf("values.reserve(%s.size());", map_name.c_str()));
+ text->AddLine(StringPrintf("for (const auto& pair : %s)", map_name.c_str()));
+ text->AddLineWithOffset("values.push_back(pair.second.get());", kBlockOffset);
+ text->AddLine("return values;");
+ text->PopOffset();
+ text->AddLine("}");
+
+ // SetAddedCallback().
+ text->AddLine(StringPrintf("void Set%sAddedCallback(",
+ itf_name.type_name.c_str()));
+ text->PushOffset(kLineContinuationOffset);
+ text->AddLine(
+ StringPrintf("const base::Callback<void(%s*)>& callback) {",
+ itf_name.MakeProxyName(true).c_str()));
+ text->PopOffset();
+ text->PushOffset(kBlockOffset);
+ text->AddLine(StringPrintf("on_%s_added_ = callback;",
+ itf_name.MakeVariableName().c_str()));
+ text->PopOffset();
+ text->AddLine("}");
+
+ // SetRemovedCallback().
+ text->AddLine(StringPrintf("void Set%sRemovedCallback(",
+ itf_name.type_name.c_str()));
+ text->PushOffset(kLineContinuationOffset);
+ text->AddLine("const base::Callback<void(const dbus::ObjectPath&)>& "
+ "callback) {");
+ text->PopOffset();
+ text->PushOffset(kBlockOffset);
+ text->AddLine(StringPrintf("on_%s_removed_ = callback;",
+ itf_name.MakeVariableName().c_str()));
+ text->PopOffset();
+ text->AddLine("}");
+
+ text->AddBlankLine();
+}
+
+void ProxyGenerator::ObjectManager::AddOnPropertyChanged(
+ const std::vector<Interface>& interfaces,
+ IndentedText* text) {
+ text->AddLineAndPushOffsetTo("void OnPropertyChanged("
+ "const dbus::ObjectPath& object_path,",
+ 1, '(');
+ text->AddLine("const std::string& interface_name,");
+ text->AddLine("const std::string& property_name) {");
+ text->PopOffset();
+ text->PushOffset(kBlockOffset);
+ for (const auto& itf : interfaces) {
+ if (itf.properties.empty())
+ continue;
+
+ NameParser itf_name{itf.name};
+ text->AddLine(StringPrintf("if (interface_name == \"%s\") {",
+ itf.name.c_str()));
+ text->PushOffset(kBlockOffset);
+ string map_name = itf_name.MakeVariableName() + "_instances_";
+ text->AddLine(StringPrintf("auto p = %s.find(object_path);",
+ map_name.c_str()));
+ text->AddLine(StringPrintf("if (p == %s.end())", map_name.c_str()));
+ text->PushOffset(kBlockOffset);
+ text->AddLine("return;");
+ text->PopOffset();
+ text->AddLine("p->second->OnPropertyChanged(property_name);");
+ text->AddLine("return;");
+ text->PopOffset();
+ text->AddLine("}");
+ }
+ text->PopOffset();
+ text->AddLine("}");
+ text->AddBlankLine();
+}
+
+void ProxyGenerator::ObjectManager::AddObjectAdded(
+ const ServiceConfig& config,
+ const std::vector<Interface>& interfaces,
+ IndentedText* text) {
+ text->AddLine("void ObjectAdded(");
+ text->PushOffset(kLineContinuationOffset);
+ text->AddLine("const dbus::ObjectPath& object_path,");
+ text->AddLine("const std::string& interface_name) override {");
+ text->PopOffset();
+ text->PushOffset(kBlockOffset);
+ for (const auto& itf : interfaces) {
+ NameParser itf_name{itf.name};
+ string var_name = itf_name.MakeVariableName();
+ text->AddLine(StringPrintf("if (interface_name == \"%s\") {",
+ itf.name.c_str()));
+ text->PushOffset(kBlockOffset);
+ if (!itf.properties.empty()) {
+ text->AddLine("auto property_set =");
+ text->PushOffset(kLineContinuationOffset);
+ text->AddLine(StringPrintf("static_cast<%s::PropertySet*>(",
+ itf_name.MakeProxyName(true).c_str()));
+ text->PushOffset(kLineContinuationOffset);
+ text->AddLine("dbus_object_manager_->GetProperties(object_path, "
+ "interface_name));");
+ text->PopOffset();
+ text->PopOffset();
+ }
+ text->AddLine(StringPrintf("std::unique_ptr<%s> %s_proxy{",
+ itf_name.MakeProxyName(true).c_str(),
+ var_name.c_str()));
+ text->PushOffset(kBlockOffset);
+ string new_instance = StringPrintf("new %s{bus_",
+ itf_name.MakeProxyName(true).c_str());
+ if (config.service_name.empty()) {
+ new_instance += ", service_name_";
+ }
+ if (itf.path.empty())
+ new_instance += ", object_path";
+ if (!itf.properties.empty())
+ new_instance += ", property_set";
+ new_instance += "}";
+ text->AddLine(new_instance);
+ text->PopOffset();
+ text->AddLine("};");
+ text->AddLine(StringPrintf("auto p = %s_instances_.emplace(object_path, "
+ "std::move(%s_proxy));",
+ var_name.c_str(), var_name.c_str()));
+ text->AddLine(StringPrintf("if (!on_%s_added_.is_null())",
+ var_name.c_str()));
+ text->PushOffset(kBlockOffset);
+ text->AddLine(StringPrintf("on_%s_added_.Run(p.first->second.get());",
+ var_name.c_str()));
+ text->PopOffset();
+ text->AddLine("return;");
+ text->PopOffset();
+ text->AddLine("}");
+ }
+ text->PopOffset();
+ text->AddLine("}");
+ text->AddBlankLine();
+}
+
+void ProxyGenerator::ObjectManager::AddObjectRemoved(
+ const std::vector<Interface>& interfaces,
+ IndentedText* text) {
+ text->AddLine("void ObjectRemoved(");
+ text->PushOffset(kLineContinuationOffset);
+ text->AddLine("const dbus::ObjectPath& object_path,");
+ text->AddLine("const std::string& interface_name) override {");
+ text->PopOffset();
+ text->PushOffset(kBlockOffset);
+ for (const auto& itf : interfaces) {
+ NameParser itf_name{itf.name};
+ string var_name = itf_name.MakeVariableName();
+ text->AddLine(StringPrintf("if (interface_name == \"%s\") {",
+ itf.name.c_str()));
+ text->PushOffset(kBlockOffset);
+ text->AddLine(StringPrintf("auto p = %s_instances_.find(object_path);",
+ var_name.c_str()));
+ text->AddLine(StringPrintf("if (p != %s_instances_.end()) {",
+ var_name.c_str()));
+ text->PushOffset(kBlockOffset);
+ text->AddLine(StringPrintf("if (!on_%s_removed_.is_null())",
+ var_name.c_str()));
+ text->PushOffset(kBlockOffset);
+ text->AddLine(StringPrintf("on_%s_removed_.Run(object_path);",
+ var_name.c_str()));
+ text->PopOffset();
+ text->AddLine(StringPrintf("%s_instances_.erase(p);",
+ var_name.c_str()));
+ text->PopOffset();
+ text->AddLine("}");
+ text->AddLine("return;");
+ text->PopOffset();
+ text->AddLine("}");
+ }
+ text->PopOffset();
+ text->AddLine("}");
+ text->AddBlankLine();
+}
+
+void ProxyGenerator::ObjectManager::AddCreateProperties(
+ const std::vector<Interface>& interfaces,
+ const std::string& class_name,
+ IndentedText* text) {
+ text->AddLine("dbus::PropertySet* CreateProperties(");
+ text->PushOffset(kLineContinuationOffset);
+ text->AddLine("dbus::ObjectProxy* object_proxy,");
+ text->AddLine("const dbus::ObjectPath& object_path,");
+ text->AddLine("const std::string& interface_name) override {");
+ text->PopOffset();
+ text->PushOffset(kBlockOffset);
+ for (const auto& itf : interfaces) {
+ NameParser itf_name{itf.name};
+ text->AddLine(StringPrintf("if (interface_name == \"%s\") {",
+ itf.name.c_str()));
+ text->PushOffset(kBlockOffset);
+ text->AddLine(StringPrintf("return new %s::PropertySet{",
+ itf_name.MakeProxyName(true).c_str()));
+ text->PushOffset(kLineContinuationOffset);
+ text->AddLine("object_proxy,");
+ text->AddLineAndPushOffsetTo(
+ StringPrintf("base::Bind(&%s::OnPropertyChanged,",
+ class_name.c_str()),
+ 1, '(');
+ text->AddLine("weak_ptr_factory_.GetWeakPtr(),");
+ text->AddLine("object_path,");
+ text->AddLine("interface_name)");
+ text->PopOffset();
+ text->PopOffset();
+ text->AddLine("};");
+ text->PopOffset();
+ text->AddLine("}");
+ }
+ text->AddLineAndPushOffsetTo("LOG(FATAL) << \"Creating properties for "
+ "unsupported interface \"", 1, ' ');
+ text->AddLine("<< interface_name;");
+ text->PopOffset();
+ text->AddLine("return nullptr;");
+ text->PopOffset();
+ text->AddLine("}");
+ text->AddBlankLine();
+}
+
+void ProxyGenerator::ObjectManager::AddDataMembers(
+ const ServiceConfig& config,
+ const std::vector<Interface>& interfaces,
+ const std::string& class_name,
+ IndentedText* text) {
+ text->AddLine("scoped_refptr<dbus::Bus> bus_;");
+ if (config.service_name.empty()) {
+ text->AddLine("std::string service_name_;");
+ }
+ text->AddLine("dbus::ObjectManager* dbus_object_manager_;");
+ for (const auto& itf : interfaces) {
+ NameParser itf_name{itf.name};
+ string var_name = itf_name.MakeVariableName();
+ text->AddLineAndPushOffsetTo("std::map<dbus::ObjectPath,", 1, '<');
+ text->AddLine(StringPrintf("std::unique_ptr<%s>> %s_instances_;",
+ itf_name.MakeProxyName(true).c_str(),
+ var_name.c_str()));
+ text->PopOffset();
+ text->AddLine(StringPrintf("base::Callback<void(%s*)> on_%s_added_;",
+ itf_name.MakeProxyName(true).c_str(),
+ var_name.c_str()));
+ text->AddLine(StringPrintf("base::Callback<void(const dbus::ObjectPath&)> "
+ "on_%s_removed_;",
+ var_name.c_str()));
+ }
+ text->AddLine(
+ StringPrintf("base::WeakPtrFactory<%s> weak_ptr_factory_{this};",
+ class_name.c_str()));
+ text->AddBlankLine();
+}
+
+// static
+string ProxyGenerator::GetHandlerNameForSignal(const string& signal) {
+ return StringPrintf("On%sSignal", signal.c_str());
+}
+
+} // namespace chromeos_dbus_bindings
diff --git a/chromeos-dbus-bindings/proxy_generator.h b/chromeos-dbus-bindings/proxy_generator.h
new file mode 100644
index 0000000..ba9a224
--- /dev/null
+++ b/chromeos-dbus-bindings/proxy_generator.h
@@ -0,0 +1,188 @@
+// 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.
+
+#ifndef CHROMEOS_DBUS_BINDINGS_PROXY_GENERATOR_H_
+#define CHROMEOS_DBUS_BINDINGS_PROXY_GENERATOR_H_
+
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+
+#include "chromeos-dbus-bindings/header_generator.h"
+#include "chromeos-dbus-bindings/indented_text.h"
+#include "chromeos-dbus-bindings/interface.h"
+
+namespace base {
+
+class FilePath;
+
+} // namespace base
+
+namespace chromeos_dbus_bindings {
+
+class IndentedText;
+struct Interface;
+
+class ProxyGenerator : public HeaderGenerator {
+ public:
+ static bool GenerateProxies(const ServiceConfig& config,
+ const std::vector<Interface>& interfaces,
+ const base::FilePath& output_file);
+
+ static bool GenerateMocks(const ServiceConfig& config,
+ const std::vector<Interface>& interfaces,
+ const base::FilePath& mock_file,
+ const base::FilePath& proxy_file);
+
+ private:
+ friend class ProxyGeneratorTest;
+
+ // Generates an abstract interface for one D-Bus interface proxy.
+ static void GenerateInterfaceProxyInterface(const ServiceConfig& config,
+ const Interface& interface,
+ IndentedText* text);
+
+ // Generates one interface proxy.
+ static void GenerateInterfaceProxy(const ServiceConfig& config,
+ const Interface& interface,
+ IndentedText* text);
+
+ // Generates one interface mock object.
+ static void GenerateInterfaceMock(const ServiceConfig& config,
+ const Interface& interface,
+ IndentedText* text);
+
+ // Generates the constructor and destructor for the proxy.
+ static void AddConstructor(const ServiceConfig& config,
+ const Interface& interface,
+ const std::string& class_name,
+ IndentedText* text);
+ static void AddDestructor(const std::string& class_name,
+ IndentedText* text);
+
+ // Generates ReleaseObjectProxy() method to release ownership
+ // of the object proxy.
+ static void AddReleaseObjectProxy(IndentedText* text);
+
+ // Generates AddGetObjectPath() method.
+ static void AddGetObjectPath(IndentedText* text);
+
+ // Generates GetObjectProxy() method.
+ static void AddGetObjectProxy(IndentedText* text);
+
+ // Generates SetPropertyChangedCallback/GetProperties() methods.
+ static void AddPropertyPublicMethods(const std::string& class_name,
+ IndentedText* text);
+
+ // Generates OnPropertyChanged() method.
+ static void AddOnPropertyChanged(IndentedText* text);
+
+ // Generates logic permitting users to register handlers for signals.
+ static void AddSignalHandlerRegistration(const Interface::Signal& signal,
+ const std::string& interface_name,
+ bool declaration_only,
+ IndentedText* text);
+
+ // Generates the property set class to contain interface properties.
+ static void AddPropertySet(const ServiceConfig& config,
+ const Interface& interface,
+ IndentedText* text);
+
+ // Generates the property accessors.
+ static void AddProperties(const ServiceConfig& config,
+ const Interface& interface,
+ bool declaration_only,
+ IndentedText* text);
+
+ // Generates a native C++ method which calls a D-Bus method on the proxy.
+ static void AddMethodProxy(const Interface::Method& interface,
+ const std::string& interface_name,
+ bool declaration_only,
+ IndentedText* text);
+
+ // Generates a native C++ method which calls a D-Bus method asynchronously.
+ static void AddAsyncMethodProxy(const Interface::Method& interface,
+ const std::string& interface_name,
+ bool declaration_only,
+ IndentedText* text);
+
+ // Generates a mock for blocking D-Bus method.
+ static void AddMethodMock(const Interface::Method& interface,
+ const std::string& interface_name,
+ IndentedText* text);
+
+ // Generates a mock for asynchronous D-Bus method.
+ static void AddAsyncMethodMock(const Interface::Method& interface,
+ const std::string& interface_name,
+ IndentedText* text);
+
+ // Generates a mock for the signal handler registration method.
+ static void AddSignalHandlerRegistrationMock(
+ const Interface::Signal& signal,
+ IndentedText* text);
+
+ // Generate the signal callback argument of a signal handler.
+ static void AddSignalCallbackArg(const Interface::Signal& signal,
+ bool comment_arg_name,
+ IndentedText* block);
+
+ // Generates the Object Manager proxy class.
+ struct ObjectManager {
+ // Generates the top-level class for Object Manager proxy.
+ static void GenerateProxy(const ServiceConfig& config,
+ const std::vector<Interface>& interfaces,
+ IndentedText* text);
+
+ // Generates Object Manager constructor.
+ static void AddConstructor(const ServiceConfig& config,
+ const std::string& class_name,
+ const std::vector<Interface>& interfaces,
+ IndentedText* text);
+
+ // Generates Object Manager destructor.
+ static void AddDestructor(const std::string& class_name,
+ const std::vector<Interface>& interfaces,
+ IndentedText* text);
+
+ // Generates GetObjectManagerProxy() method.
+ static void AddGetObjectManagerProxy(IndentedText* text);
+
+ // Generates code for interface-specific accessor methods
+ static void AddInterfaceAccessors(const Interface& interface,
+ IndentedText* text);
+
+ // Generates OnPropertyChanged() method.
+ static void AddOnPropertyChanged(const std::vector<Interface>& interfaces,
+ IndentedText* text);
+
+ // Generates ObjectAdded() method.
+ static void AddObjectAdded(const ServiceConfig& config,
+ const std::vector<Interface>& interfaces,
+ IndentedText* text);
+
+ // Generates ObjectRemoved() method.
+ static void AddObjectRemoved(const std::vector<Interface>& interfaces,
+ IndentedText* text);
+
+ // Generates CreateProperties() method.
+ static void AddCreateProperties(const std::vector<Interface>& interfaces,
+ const std::string& class_name,
+ IndentedText* text);
+
+ // Generates data members of the class.
+ static void AddDataMembers(const ServiceConfig& config,
+ const std::vector<Interface>& interfaces,
+ const std::string& class_name,
+ IndentedText* text);
+ };
+ // Generates the signal handler name for a given signal name.
+ static std::string GetHandlerNameForSignal(const std::string& signal);
+
+ DISALLOW_COPY_AND_ASSIGN(ProxyGenerator);
+};
+
+} // namespace chromeos_dbus_bindings
+
+#endif // CHROMEOS_DBUS_BINDINGS_PROXY_GENERATOR_H_
diff --git a/chromeos-dbus-bindings/proxy_generator_mock_unittest.cc b/chromeos-dbus-bindings/proxy_generator_mock_unittest.cc
new file mode 100644
index 0000000..a07a8c9
--- /dev/null
+++ b/chromeos-dbus-bindings/proxy_generator_mock_unittest.cc
@@ -0,0 +1,197 @@
+// Copyright 2015 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/proxy_generator.h"
+
+#include <string>
+#include <vector>
+
+#include <base/files/file_path.h>
+#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+#include <gtest/gtest.h>
+
+#include "chromeos-dbus-bindings/interface.h"
+#include "chromeos-dbus-bindings/test_utils.h"
+
+using std::string;
+using std::vector;
+using testing::Test;
+
+namespace chromeos_dbus_bindings {
+
+namespace {
+
+const char kDBusTypeArryOfObjects[] = "ao";
+const char kDBusTypeArryOfStrings[] = "as";
+const char kDBusTypeBool[] = "b";
+const char kDBusTypeByte[] = "y";
+const char kDBusTypeInt32[] = "i";
+const char kDBusTypeInt64[] = "x";
+const char kDBusTypeString[] = "s";
+
+const char kExpectedContent[] = R"literal_string(
+#include <string>
+#include <vector>
+
+#include <base/callback_forward.h>
+#include <base/macros.h>
+#include <chromeos/any.h>
+#include <chromeos/errors/error.h>
+#include <chromeos/variant_dictionary.h>
+#include <gmock/gmock.h>
+
+#include "proxies.h"
+
+namespace org {
+namespace chromium {
+
+// Mock object for TestInterfaceProxyInterface.
+class TestInterfaceProxyMock final : public TestInterfaceProxyInterface {
+ public:
+ TestInterfaceProxyMock() = default;
+
+ MOCK_METHOD5(Elements,
+ bool(const std::string& /*in_space_walk*/,
+ const std::vector<dbus::ObjectPath>& /*in_ramblin_man*/,
+ std::string*,
+ chromeos::ErrorPtr* /*error*/,
+ int /*timeout_ms*/));
+ MOCK_METHOD5(ElementsAsync,
+ void(const std::string& /*in_space_walk*/,
+ const std::vector<dbus::ObjectPath>& /*in_ramblin_man*/,
+ const base::Callback<void(const std::string&)>& /*success_callback*/,
+ const base::Callback<void(chromeos::Error*)>& /*error_callback*/,
+ int /*timeout_ms*/));
+ MOCK_METHOD3(ReturnToPatagonia,
+ bool(int64_t*,
+ chromeos::ErrorPtr* /*error*/,
+ int /*timeout_ms*/));
+ MOCK_METHOD3(ReturnToPatagoniaAsync,
+ void(const base::Callback<void(int64_t)>& /*success_callback*/,
+ const base::Callback<void(chromeos::Error*)>& /*error_callback*/,
+ int /*timeout_ms*/));
+ MOCK_METHOD3(NiceWeatherForDucks,
+ bool(bool,
+ chromeos::ErrorPtr* /*error*/,
+ int /*timeout_ms*/));
+ MOCK_METHOD4(NiceWeatherForDucksAsync,
+ void(bool,
+ const base::Callback<void()>& /*success_callback*/,
+ const base::Callback<void(chromeos::Error*)>& /*error_callback*/,
+ int /*timeout_ms*/));
+ MOCK_METHOD2(ExperimentNumberSix,
+ bool(chromeos::ErrorPtr* /*error*/,
+ int /*timeout_ms*/));
+ MOCK_METHOD3(ExperimentNumberSixAsync,
+ void(const base::Callback<void()>& /*success_callback*/,
+ const base::Callback<void(chromeos::Error*)>& /*error_callback*/,
+ int /*timeout_ms*/));
+ MOCK_METHOD2(RegisterCloserSignalHandler,
+ void(const base::Closure& /*signal_callback*/,
+ dbus::ObjectProxy::OnConnectedCallback /*on_connected_callback*/));
+ MOCK_METHOD2(RegisterTheCurseOfKaZarSignalHandler,
+ void(const base::Callback<void(const std::vector<std::string>&,
+ uint8_t)>& /*signal_callback*/,
+ dbus::ObjectProxy::OnConnectedCallback /*on_connected_callback*/));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestInterfaceProxyMock);
+};
+} // namespace chromium
+} // namespace org
+
+namespace org {
+namespace chromium {
+
+// Mock object for TestInterface2ProxyInterface.
+class TestInterface2ProxyMock final : public TestInterface2ProxyInterface {
+ public:
+ TestInterface2ProxyMock() = default;
+
+ MOCK_METHOD4(GetPersonInfo,
+ bool(std::string* /*out_name*/,
+ int32_t* /*out_age*/,
+ chromeos::ErrorPtr* /*error*/,
+ int /*timeout_ms*/));
+ MOCK_METHOD3(GetPersonInfoAsync,
+ void(const base::Callback<void(const std::string& /*name*/, int32_t /*age*/)>& /*success_callback*/,
+ const base::Callback<void(chromeos::Error*)>& /*error_callback*/,
+ int /*timeout_ms*/));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestInterface2ProxyMock);
+};
+} // namespace chromium
+} // namespace org
+)literal_string";
+
+} // namespace
+
+class ProxyGeneratorMockTest : public Test {
+ public:
+ void SetUp() override {
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ }
+
+ protected:
+ base::FilePath CreateInputFile(const string& contents) {
+ base::FilePath path;
+ EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(), &path));
+ int written = base::WriteFile(path, contents.c_str(), contents.size());
+ EXPECT_EQ(contents.size(), static_cast<size_t>(written));
+ return path;
+ }
+
+ base::ScopedTempDir temp_dir_;
+};
+
+TEST_F(ProxyGeneratorMockTest, GenerateMocks) {
+ Interface interface;
+ interface.name = "org.chromium.TestInterface";
+ interface.path = "/org/chromium/Test";
+ interface.methods.emplace_back(
+ "Elements",
+ vector<Interface::Argument>{
+ {"space_walk", kDBusTypeString},
+ {"ramblin_man", kDBusTypeArryOfObjects}},
+ vector<Interface::Argument>{{"", kDBusTypeString}});
+ interface.methods.emplace_back(
+ "ReturnToPatagonia",
+ vector<Interface::Argument>{},
+ vector<Interface::Argument>{{"", kDBusTypeInt64}});
+ interface.methods.emplace_back(
+ "NiceWeatherForDucks",
+ vector<Interface::Argument>{{"", kDBusTypeBool}},
+ vector<Interface::Argument>{});
+ interface.methods.emplace_back("ExperimentNumberSix");
+ interface.signals.emplace_back("Closer");
+ interface.signals.emplace_back(
+ "TheCurseOfKaZar",
+ vector<Interface::Argument>{
+ {"", kDBusTypeArryOfStrings},
+ {"", kDBusTypeByte}});
+ interface.methods.back().doc_string = "Comment line1\nline2";
+ Interface interface2;
+ interface2.name = "org.chromium.TestInterface2";
+ interface2.methods.emplace_back(
+ "GetPersonInfo",
+ vector<Interface::Argument>{},
+ vector<Interface::Argument>{
+ {"name", kDBusTypeString},
+ {"age", kDBusTypeInt32}});
+ vector<Interface> interfaces{interface, interface2};
+ base::FilePath output_path = temp_dir_.path().Append("output.h");
+ base::FilePath proxy_path = temp_dir_.path().Append("proxies.h");
+ ServiceConfig config;
+ EXPECT_TRUE(ProxyGenerator::GenerateMocks(config, interfaces, output_path,
+ proxy_path));
+ string contents;
+ EXPECT_TRUE(base::ReadFileToString(output_path, &contents));
+ // The header guards contain the (temporary) filename, so we search for
+ // the content we need within the string.
+ test_utils::EXPECT_TEXT_CONTAINED(kExpectedContent, contents);
+}
+
+} // namespace chromeos_dbus_bindings
diff --git a/chromeos-dbus-bindings/proxy_generator_unittest.cc b/chromeos-dbus-bindings/proxy_generator_unittest.cc
new file mode 100644
index 0000000..75b7e9f
--- /dev/null
+++ b/chromeos-dbus-bindings/proxy_generator_unittest.cc
@@ -0,0 +1,1389 @@
+// 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/proxy_generator.h"
+
+#include <string>
+#include <vector>
+
+#include <base/files/file_path.h>
+#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+#include <gtest/gtest.h>
+
+#include "chromeos-dbus-bindings/interface.h"
+#include "chromeos-dbus-bindings/test_utils.h"
+
+using std::string;
+using std::vector;
+using testing::Test;
+
+namespace chromeos_dbus_bindings {
+
+namespace {
+
+const char kDBusTypeArryOfObjects[] = "ao";
+const char kDBusTypeArryOfStrings[] = "as";
+const char kDBusTypeBool[] = "b";
+const char kDBusTypeByte[] = "y";
+const char kDBusTypeInt32[] = "i";
+const char kDBusTypeInt64[] = "x";
+const char kDBusTypeString[] = "s";
+
+const char kExpectedContent[] = R"literal_string(
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/callback.h>
+#include <base/logging.h>
+#include <base/macros.h>
+#include <base/memory/ref_counted.h>
+#include <chromeos/any.h>
+#include <chromeos/dbus/dbus_method_invoker.h>
+#include <chromeos/dbus/dbus_property.h>
+#include <chromeos/dbus/dbus_signal_handler.h>
+#include <chromeos/errors/error.h>
+#include <chromeos/variant_dictionary.h>
+#include <dbus/bus.h>
+#include <dbus/message.h>
+#include <dbus/object_manager.h>
+#include <dbus/object_path.h>
+#include <dbus/object_proxy.h>
+
+namespace org {
+namespace chromium {
+
+// Abstract interface proxy for org::chromium::TestInterface.
+class TestInterfaceProxyInterface {
+ public:
+ virtual ~TestInterfaceProxyInterface() = default;
+
+ virtual bool Elements(
+ const std::string& in_space_walk,
+ const std::vector<dbus::ObjectPath>& in_ramblin_man,
+ std::string* out_3,
+ chromeos::ErrorPtr* error,
+ int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+ virtual void ElementsAsync(
+ const std::string& in_space_walk,
+ const std::vector<dbus::ObjectPath>& in_ramblin_man,
+ const base::Callback<void(const std::string&)>& success_callback,
+ const base::Callback<void(chromeos::Error*)>& error_callback,
+ int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+ virtual bool ReturnToPatagonia(
+ int64_t* out_1,
+ chromeos::ErrorPtr* error,
+ int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+ virtual void ReturnToPatagoniaAsync(
+ const base::Callback<void(int64_t)>& success_callback,
+ const base::Callback<void(chromeos::Error*)>& error_callback,
+ int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+ virtual bool NiceWeatherForDucks(
+ bool in_1,
+ chromeos::ErrorPtr* error,
+ int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+ virtual void NiceWeatherForDucksAsync(
+ bool in_1,
+ const base::Callback<void()>& success_callback,
+ const base::Callback<void(chromeos::Error*)>& error_callback,
+ int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+ // Comment line1
+ // line2
+ virtual bool ExperimentNumberSix(
+ chromeos::ErrorPtr* error,
+ int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+ // Comment line1
+ // line2
+ virtual void ExperimentNumberSixAsync(
+ const base::Callback<void()>& success_callback,
+ const base::Callback<void(chromeos::Error*)>& error_callback,
+ int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+ virtual void RegisterCloserSignalHandler(
+ const base::Closure& signal_callback,
+ dbus::ObjectProxy::OnConnectedCallback on_connected_callback) = 0;
+
+ virtual void RegisterTheCurseOfKaZarSignalHandler(
+ const base::Callback<void(const std::vector<std::string>&,
+ uint8_t)>& signal_callback,
+ dbus::ObjectProxy::OnConnectedCallback on_connected_callback) = 0;
+};
+
+} // namespace chromium
+} // namespace org
+
+namespace org {
+namespace chromium {
+
+// Interface proxy for org::chromium::TestInterface.
+class TestInterfaceProxy final : public TestInterfaceProxyInterface {
+ public:
+ TestInterfaceProxy(
+ const scoped_refptr<dbus::Bus>& bus,
+ const std::string& service_name) :
+ bus_{bus},
+ service_name_{service_name},
+ dbus_object_proxy_{
+ bus_->GetObjectProxy(service_name_, object_path_)} {
+ }
+
+ ~TestInterfaceProxy() override {
+ }
+
+ void RegisterCloserSignalHandler(
+ const base::Closure& signal_callback,
+ dbus::ObjectProxy::OnConnectedCallback on_connected_callback) override {
+ chromeos::dbus_utils::ConnectToSignal(
+ dbus_object_proxy_,
+ "org.chromium.TestInterface",
+ "Closer",
+ signal_callback,
+ on_connected_callback);
+ }
+
+ void RegisterTheCurseOfKaZarSignalHandler(
+ const base::Callback<void(const std::vector<std::string>&,
+ uint8_t)>& signal_callback,
+ dbus::ObjectProxy::OnConnectedCallback on_connected_callback) override {
+ chromeos::dbus_utils::ConnectToSignal(
+ dbus_object_proxy_,
+ "org.chromium.TestInterface",
+ "TheCurseOfKaZar",
+ signal_callback,
+ on_connected_callback);
+ }
+
+ void ReleaseObjectProxy(const base::Closure& callback) {
+ bus_->RemoveObjectProxy(service_name_, object_path_, callback);
+ }
+
+ const dbus::ObjectPath& GetObjectPath() const {
+ return object_path_;
+ }
+
+ dbus::ObjectProxy* GetObjectProxy() const { return dbus_object_proxy_; }
+
+ bool Elements(
+ const std::string& in_space_walk,
+ const std::vector<dbus::ObjectPath>& in_ramblin_man,
+ std::string* out_3,
+ chromeos::ErrorPtr* error,
+ int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+ auto response = chromeos::dbus_utils::CallMethodAndBlockWithTimeout(
+ timeout_ms,
+ dbus_object_proxy_,
+ "org.chromium.TestInterface",
+ "Elements",
+ error,
+ in_space_walk,
+ in_ramblin_man);
+ return response && chromeos::dbus_utils::ExtractMethodCallResults(
+ response.get(), error, out_3);
+ }
+
+ void ElementsAsync(
+ const std::string& in_space_walk,
+ const std::vector<dbus::ObjectPath>& in_ramblin_man,
+ const base::Callback<void(const std::string&)>& success_callback,
+ const base::Callback<void(chromeos::Error*)>& error_callback,
+ int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+ chromeos::dbus_utils::CallMethodWithTimeout(
+ timeout_ms,
+ dbus_object_proxy_,
+ "org.chromium.TestInterface",
+ "Elements",
+ success_callback,
+ error_callback,
+ in_space_walk,
+ in_ramblin_man);
+ }
+
+ bool ReturnToPatagonia(
+ int64_t* out_1,
+ chromeos::ErrorPtr* error,
+ int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+ auto response = chromeos::dbus_utils::CallMethodAndBlockWithTimeout(
+ timeout_ms,
+ dbus_object_proxy_,
+ "org.chromium.TestInterface",
+ "ReturnToPatagonia",
+ error);
+ return response && chromeos::dbus_utils::ExtractMethodCallResults(
+ response.get(), error, out_1);
+ }
+
+ void ReturnToPatagoniaAsync(
+ const base::Callback<void(int64_t)>& success_callback,
+ const base::Callback<void(chromeos::Error*)>& error_callback,
+ int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+ chromeos::dbus_utils::CallMethodWithTimeout(
+ timeout_ms,
+ dbus_object_proxy_,
+ "org.chromium.TestInterface",
+ "ReturnToPatagonia",
+ success_callback,
+ error_callback);
+ }
+
+ bool NiceWeatherForDucks(
+ bool in_1,
+ chromeos::ErrorPtr* error,
+ int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+ auto response = chromeos::dbus_utils::CallMethodAndBlockWithTimeout(
+ timeout_ms,
+ dbus_object_proxy_,
+ "org.chromium.TestInterface",
+ "NiceWeatherForDucks",
+ error,
+ in_1);
+ return response && chromeos::dbus_utils::ExtractMethodCallResults(
+ response.get(), error);
+ }
+
+ void NiceWeatherForDucksAsync(
+ bool in_1,
+ const base::Callback<void()>& success_callback,
+ const base::Callback<void(chromeos::Error*)>& error_callback,
+ int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+ chromeos::dbus_utils::CallMethodWithTimeout(
+ timeout_ms,
+ dbus_object_proxy_,
+ "org.chromium.TestInterface",
+ "NiceWeatherForDucks",
+ success_callback,
+ error_callback,
+ in_1);
+ }
+
+ // Comment line1
+ // line2
+ bool ExperimentNumberSix(
+ chromeos::ErrorPtr* error,
+ int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+ auto response = chromeos::dbus_utils::CallMethodAndBlockWithTimeout(
+ timeout_ms,
+ dbus_object_proxy_,
+ "org.chromium.TestInterface",
+ "ExperimentNumberSix",
+ error);
+ return response && chromeos::dbus_utils::ExtractMethodCallResults(
+ response.get(), error);
+ }
+
+ // Comment line1
+ // line2
+ void ExperimentNumberSixAsync(
+ const base::Callback<void()>& success_callback,
+ const base::Callback<void(chromeos::Error*)>& error_callback,
+ int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+ chromeos::dbus_utils::CallMethodWithTimeout(
+ timeout_ms,
+ dbus_object_proxy_,
+ "org.chromium.TestInterface",
+ "ExperimentNumberSix",
+ success_callback,
+ error_callback);
+ }
+
+ private:
+ scoped_refptr<dbus::Bus> bus_;
+ std::string service_name_;
+ const dbus::ObjectPath object_path_{"/org/chromium/Test"};
+ dbus::ObjectProxy* dbus_object_proxy_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestInterfaceProxy);
+};
+
+} // namespace chromium
+} // namespace org
+
+namespace org {
+namespace chromium {
+
+// Abstract interface proxy for org::chromium::TestInterface2.
+class TestInterface2ProxyInterface {
+ public:
+ virtual ~TestInterface2ProxyInterface() = default;
+
+ virtual bool GetPersonInfo(
+ std::string* out_name,
+ int32_t* out_age,
+ chromeos::ErrorPtr* error,
+ int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+
+ virtual void GetPersonInfoAsync(
+ const base::Callback<void(const std::string& /*name*/, int32_t /*age*/)>& success_callback,
+ const base::Callback<void(chromeos::Error*)>& error_callback,
+ int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) = 0;
+};
+
+} // namespace chromium
+} // namespace org
+
+namespace org {
+namespace chromium {
+
+// Interface proxy for org::chromium::TestInterface2.
+class TestInterface2Proxy final : public TestInterface2ProxyInterface {
+ public:
+ TestInterface2Proxy(
+ const scoped_refptr<dbus::Bus>& bus,
+ const std::string& service_name,
+ const dbus::ObjectPath& object_path) :
+ bus_{bus},
+ service_name_{service_name},
+ object_path_{object_path},
+ dbus_object_proxy_{
+ bus_->GetObjectProxy(service_name_, object_path_)} {
+ }
+
+ ~TestInterface2Proxy() override {
+ }
+
+ void ReleaseObjectProxy(const base::Closure& callback) {
+ bus_->RemoveObjectProxy(service_name_, object_path_, callback);
+ }
+
+ const dbus::ObjectPath& GetObjectPath() const {
+ return object_path_;
+ }
+
+ dbus::ObjectProxy* GetObjectProxy() const { return dbus_object_proxy_; }
+
+ bool GetPersonInfo(
+ std::string* out_name,
+ int32_t* out_age,
+ chromeos::ErrorPtr* error,
+ int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+ auto response = chromeos::dbus_utils::CallMethodAndBlockWithTimeout(
+ timeout_ms,
+ dbus_object_proxy_,
+ "org.chromium.TestInterface2",
+ "GetPersonInfo",
+ error);
+ return response && chromeos::dbus_utils::ExtractMethodCallResults(
+ response.get(), error, out_name, out_age);
+ }
+
+ void GetPersonInfoAsync(
+ const base::Callback<void(const std::string& /*name*/, int32_t /*age*/)>& success_callback,
+ const base::Callback<void(chromeos::Error*)>& error_callback,
+ int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) override {
+ chromeos::dbus_utils::CallMethodWithTimeout(
+ timeout_ms,
+ dbus_object_proxy_,
+ "org.chromium.TestInterface2",
+ "GetPersonInfo",
+ success_callback,
+ error_callback);
+ }
+
+ private:
+ scoped_refptr<dbus::Bus> bus_;
+ std::string service_name_;
+ dbus::ObjectPath object_path_;
+ dbus::ObjectProxy* dbus_object_proxy_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestInterface2Proxy);
+};
+
+} // namespace chromium
+} // namespace org
+)literal_string";
+
+const char kExpectedContentWithService[] = R"literal_string(
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/callback.h>
+#include <base/logging.h>
+#include <base/macros.h>
+#include <base/memory/ref_counted.h>
+#include <chromeos/any.h>
+#include <chromeos/dbus/dbus_method_invoker.h>
+#include <chromeos/dbus/dbus_property.h>
+#include <chromeos/dbus/dbus_signal_handler.h>
+#include <chromeos/errors/error.h>
+#include <chromeos/variant_dictionary.h>
+#include <dbus/bus.h>
+#include <dbus/message.h>
+#include <dbus/object_manager.h>
+#include <dbus/object_path.h>
+#include <dbus/object_proxy.h>
+
+namespace org {
+namespace chromium {
+
+// Abstract interface proxy for org::chromium::TestInterface.
+class TestInterfaceProxyInterface {
+ public:
+ virtual ~TestInterfaceProxyInterface() = default;
+
+ virtual void RegisterCloserSignalHandler(
+ const base::Closure& signal_callback,
+ dbus::ObjectProxy::OnConnectedCallback on_connected_callback) = 0;
+};
+
+} // namespace chromium
+} // namespace org
+
+namespace org {
+namespace chromium {
+
+// Interface proxy for org::chromium::TestInterface.
+class TestInterfaceProxy final : public TestInterfaceProxyInterface {
+ public:
+ TestInterfaceProxy(const scoped_refptr<dbus::Bus>& bus) :
+ bus_{bus},
+ dbus_object_proxy_{
+ bus_->GetObjectProxy(service_name_, object_path_)} {
+ }
+
+ ~TestInterfaceProxy() override {
+ }
+
+ void RegisterCloserSignalHandler(
+ const base::Closure& signal_callback,
+ dbus::ObjectProxy::OnConnectedCallback on_connected_callback) override {
+ chromeos::dbus_utils::ConnectToSignal(
+ dbus_object_proxy_,
+ "org.chromium.TestInterface",
+ "Closer",
+ signal_callback,
+ on_connected_callback);
+ }
+
+ void ReleaseObjectProxy(const base::Closure& callback) {
+ bus_->RemoveObjectProxy(service_name_, object_path_, callback);
+ }
+
+ const dbus::ObjectPath& GetObjectPath() const {
+ return object_path_;
+ }
+
+ dbus::ObjectProxy* GetObjectProxy() const { return dbus_object_proxy_; }
+
+ private:
+ scoped_refptr<dbus::Bus> bus_;
+ const std::string service_name_{"org.chromium.Test"};
+ const dbus::ObjectPath object_path_{"/org/chromium/Test"};
+ dbus::ObjectProxy* dbus_object_proxy_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestInterfaceProxy);
+};
+
+} // namespace chromium
+} // namespace org
+
+namespace org {
+namespace chromium {
+
+// Abstract interface proxy for org::chromium::TestInterface2.
+class TestInterface2ProxyInterface {
+ public:
+ virtual ~TestInterface2ProxyInterface() = default;
+};
+
+} // namespace chromium
+} // namespace org
+
+namespace org {
+namespace chromium {
+
+// Interface proxy for org::chromium::TestInterface2.
+class TestInterface2Proxy final : public TestInterface2ProxyInterface {
+ public:
+ TestInterface2Proxy(
+ const scoped_refptr<dbus::Bus>& bus,
+ const dbus::ObjectPath& object_path) :
+ bus_{bus},
+ object_path_{object_path},
+ dbus_object_proxy_{
+ bus_->GetObjectProxy(service_name_, object_path_)} {
+ }
+
+ ~TestInterface2Proxy() override {
+ }
+
+ void ReleaseObjectProxy(const base::Closure& callback) {
+ bus_->RemoveObjectProxy(service_name_, object_path_, callback);
+ }
+
+ const dbus::ObjectPath& GetObjectPath() const {
+ return object_path_;
+ }
+
+ dbus::ObjectProxy* GetObjectProxy() const { return dbus_object_proxy_; }
+
+ private:
+ scoped_refptr<dbus::Bus> bus_;
+ const std::string service_name_{"org.chromium.Test"};
+ dbus::ObjectPath object_path_;
+ dbus::ObjectProxy* dbus_object_proxy_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestInterface2Proxy);
+};
+
+} // namespace chromium
+} // namespace org
+)literal_string";
+
+const char kExpectedContentWithObjectManager[] = R"literal_string(
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/callback.h>
+#include <base/logging.h>
+#include <base/macros.h>
+#include <base/memory/ref_counted.h>
+#include <chromeos/any.h>
+#include <chromeos/dbus/dbus_method_invoker.h>
+#include <chromeos/dbus/dbus_property.h>
+#include <chromeos/dbus/dbus_signal_handler.h>
+#include <chromeos/errors/error.h>
+#include <chromeos/variant_dictionary.h>
+#include <dbus/bus.h>
+#include <dbus/message.h>
+#include <dbus/object_manager.h>
+#include <dbus/object_path.h>
+#include <dbus/object_proxy.h>
+
+namespace org {
+namespace chromium {
+class ObjectManagerProxy;
+} // namespace chromium
+} // namespace org
+
+namespace org {
+namespace chromium {
+
+// Abstract interface proxy for org::chromium::Itf1.
+class Itf1ProxyInterface {
+ public:
+ virtual ~Itf1ProxyInterface() = default;
+
+ virtual void RegisterCloserSignalHandler(
+ const base::Closure& signal_callback,
+ dbus::ObjectProxy::OnConnectedCallback on_connected_callback) = 0;
+
+ static const char* DataName() { return "Data"; }
+ virtual const std::string& data() const = 0;
+};
+
+} // namespace chromium
+} // namespace org
+
+namespace org {
+namespace chromium {
+
+// Interface proxy for org::chromium::Itf1.
+class Itf1Proxy final : public Itf1ProxyInterface {
+ public:
+ class PropertySet : public dbus::PropertySet {
+ public:
+ PropertySet(dbus::ObjectProxy* object_proxy,
+ const PropertyChangedCallback& callback)
+ : dbus::PropertySet{object_proxy,
+ "org.chromium.Itf1",
+ callback} {
+ RegisterProperty(DataName(), &data);
+ }
+
+ chromeos::dbus_utils::Property<std::string> data;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PropertySet);
+ };
+
+ Itf1Proxy(
+ const scoped_refptr<dbus::Bus>& bus,
+ const std::string& service_name,
+ PropertySet* property_set) :
+ bus_{bus},
+ service_name_{service_name},
+ property_set_{property_set},
+ dbus_object_proxy_{
+ bus_->GetObjectProxy(service_name_, object_path_)} {
+ }
+
+ ~Itf1Proxy() override {
+ }
+
+ void RegisterCloserSignalHandler(
+ const base::Closure& signal_callback,
+ dbus::ObjectProxy::OnConnectedCallback on_connected_callback) override {
+ chromeos::dbus_utils::ConnectToSignal(
+ dbus_object_proxy_,
+ "org.chromium.Itf1",
+ "Closer",
+ signal_callback,
+ on_connected_callback);
+ }
+
+ void ReleaseObjectProxy(const base::Closure& callback) {
+ bus_->RemoveObjectProxy(service_name_, object_path_, callback);
+ }
+
+ const dbus::ObjectPath& GetObjectPath() const {
+ return object_path_;
+ }
+
+ dbus::ObjectProxy* GetObjectProxy() const { return dbus_object_proxy_; }
+
+ void SetPropertyChangedCallback(
+ const base::Callback<void(Itf1Proxy*, const std::string&)>& callback) {
+ on_property_changed_ = callback;
+ }
+
+ const PropertySet* GetProperties() const { return property_set_; }
+ PropertySet* GetProperties() { return property_set_; }
+
+ const std::string& data() const override {
+ return property_set_->data.value();
+ }
+
+ private:
+ void OnPropertyChanged(const std::string& property_name) {
+ if (!on_property_changed_.is_null())
+ on_property_changed_.Run(this, property_name);
+ }
+
+ scoped_refptr<dbus::Bus> bus_;
+ std::string service_name_;
+ const dbus::ObjectPath object_path_{"/org/chromium/Test/Object"};
+ PropertySet* property_set_;
+ base::Callback<void(Itf1Proxy*, const std::string&)> on_property_changed_;
+ dbus::ObjectProxy* dbus_object_proxy_;
+
+ friend class org::chromium::ObjectManagerProxy;
+ DISALLOW_COPY_AND_ASSIGN(Itf1Proxy);
+};
+
+} // namespace chromium
+} // namespace org
+
+namespace org {
+namespace chromium {
+
+// Abstract interface proxy for org::chromium::Itf2.
+class Itf2ProxyInterface {
+ public:
+ virtual ~Itf2ProxyInterface() = default;
+};
+
+} // namespace chromium
+} // namespace org
+
+namespace org {
+namespace chromium {
+
+// Interface proxy for org::chromium::Itf2.
+class Itf2Proxy final : public Itf2ProxyInterface {
+ public:
+ class PropertySet : public dbus::PropertySet {
+ public:
+ PropertySet(dbus::ObjectProxy* object_proxy,
+ const PropertyChangedCallback& callback)
+ : dbus::PropertySet{object_proxy,
+ "org.chromium.Itf2",
+ callback} {
+ }
+
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PropertySet);
+ };
+
+ Itf2Proxy(
+ const scoped_refptr<dbus::Bus>& bus,
+ const std::string& service_name,
+ const dbus::ObjectPath& object_path) :
+ bus_{bus},
+ service_name_{service_name},
+ object_path_{object_path},
+ dbus_object_proxy_{
+ bus_->GetObjectProxy(service_name_, object_path_)} {
+ }
+
+ ~Itf2Proxy() override {
+ }
+
+ void ReleaseObjectProxy(const base::Closure& callback) {
+ bus_->RemoveObjectProxy(service_name_, object_path_, callback);
+ }
+
+ const dbus::ObjectPath& GetObjectPath() const {
+ return object_path_;
+ }
+
+ dbus::ObjectProxy* GetObjectProxy() const { return dbus_object_proxy_; }
+
+ private:
+ scoped_refptr<dbus::Bus> bus_;
+ std::string service_name_;
+ dbus::ObjectPath object_path_;
+ dbus::ObjectProxy* dbus_object_proxy_;
+
+ DISALLOW_COPY_AND_ASSIGN(Itf2Proxy);
+};
+
+} // namespace chromium
+} // namespace org
+
+namespace org {
+namespace chromium {
+
+class ObjectManagerProxy : public dbus::ObjectManager::Interface {
+ public:
+ ObjectManagerProxy(const scoped_refptr<dbus::Bus>& bus,
+ const std::string& service_name)
+ : bus_{bus},
+ service_name_{service_name},
+ dbus_object_manager_{bus->GetObjectManager(
+ service_name,
+ dbus::ObjectPath{"/org/chromium/Test"})} {
+ dbus_object_manager_->RegisterInterface("org.chromium.Itf1", this);
+ dbus_object_manager_->RegisterInterface("org.chromium.Itf2", this);
+ }
+
+ ~ObjectManagerProxy() override {
+ dbus_object_manager_->UnregisterInterface("org.chromium.Itf1");
+ dbus_object_manager_->UnregisterInterface("org.chromium.Itf2");
+ }
+
+ dbus::ObjectManager* GetObjectManagerProxy() const {
+ return dbus_object_manager_;
+ }
+
+ org::chromium::Itf1Proxy* GetItf1Proxy() {
+ if (itf1_instances_.empty())
+ return nullptr;
+ return itf1_instances_.begin()->second.get();
+ }
+ std::vector<org::chromium::Itf1Proxy*> GetItf1Instances() const {
+ std::vector<org::chromium::Itf1Proxy*> values;
+ values.reserve(itf1_instances_.size());
+ for (const auto& pair : itf1_instances_)
+ values.push_back(pair.second.get());
+ return values;
+ }
+ void SetItf1AddedCallback(
+ const base::Callback<void(org::chromium::Itf1Proxy*)>& callback) {
+ on_itf1_added_ = callback;
+ }
+ void SetItf1RemovedCallback(
+ const base::Callback<void(const dbus::ObjectPath&)>& callback) {
+ on_itf1_removed_ = callback;
+ }
+
+ org::chromium::Itf2Proxy* GetItf2Proxy(
+ const dbus::ObjectPath& object_path) {
+ auto p = itf2_instances_.find(object_path);
+ if (p != itf2_instances_.end())
+ return p->second.get();
+ return nullptr;
+ }
+ std::vector<org::chromium::Itf2Proxy*> GetItf2Instances() const {
+ std::vector<org::chromium::Itf2Proxy*> values;
+ values.reserve(itf2_instances_.size());
+ for (const auto& pair : itf2_instances_)
+ values.push_back(pair.second.get());
+ return values;
+ }
+ void SetItf2AddedCallback(
+ const base::Callback<void(org::chromium::Itf2Proxy*)>& callback) {
+ on_itf2_added_ = callback;
+ }
+ void SetItf2RemovedCallback(
+ const base::Callback<void(const dbus::ObjectPath&)>& callback) {
+ on_itf2_removed_ = callback;
+ }
+
+ private:
+ void OnPropertyChanged(const dbus::ObjectPath& object_path,
+ const std::string& interface_name,
+ const std::string& property_name) {
+ if (interface_name == "org.chromium.Itf1") {
+ auto p = itf1_instances_.find(object_path);
+ if (p == itf1_instances_.end())
+ return;
+ p->second->OnPropertyChanged(property_name);
+ return;
+ }
+ }
+
+ void ObjectAdded(
+ const dbus::ObjectPath& object_path,
+ const std::string& interface_name) override {
+ if (interface_name == "org.chromium.Itf1") {
+ auto property_set =
+ static_cast<org::chromium::Itf1Proxy::PropertySet*>(
+ dbus_object_manager_->GetProperties(object_path, interface_name));
+ std::unique_ptr<org::chromium::Itf1Proxy> itf1_proxy{
+ new org::chromium::Itf1Proxy{bus_, service_name_, property_set}
+ };
+ auto p = itf1_instances_.emplace(object_path, std::move(itf1_proxy));
+ if (!on_itf1_added_.is_null())
+ on_itf1_added_.Run(p.first->second.get());
+ return;
+ }
+ if (interface_name == "org.chromium.Itf2") {
+ std::unique_ptr<org::chromium::Itf2Proxy> itf2_proxy{
+ new org::chromium::Itf2Proxy{bus_, service_name_, object_path}
+ };
+ auto p = itf2_instances_.emplace(object_path, std::move(itf2_proxy));
+ if (!on_itf2_added_.is_null())
+ on_itf2_added_.Run(p.first->second.get());
+ return;
+ }
+ }
+
+ void ObjectRemoved(
+ const dbus::ObjectPath& object_path,
+ const std::string& interface_name) override {
+ if (interface_name == "org.chromium.Itf1") {
+ auto p = itf1_instances_.find(object_path);
+ if (p != itf1_instances_.end()) {
+ if (!on_itf1_removed_.is_null())
+ on_itf1_removed_.Run(object_path);
+ itf1_instances_.erase(p);
+ }
+ return;
+ }
+ if (interface_name == "org.chromium.Itf2") {
+ auto p = itf2_instances_.find(object_path);
+ if (p != itf2_instances_.end()) {
+ if (!on_itf2_removed_.is_null())
+ on_itf2_removed_.Run(object_path);
+ itf2_instances_.erase(p);
+ }
+ return;
+ }
+ }
+
+ dbus::PropertySet* CreateProperties(
+ dbus::ObjectProxy* object_proxy,
+ const dbus::ObjectPath& object_path,
+ const std::string& interface_name) override {
+ if (interface_name == "org.chromium.Itf1") {
+ return new org::chromium::Itf1Proxy::PropertySet{
+ object_proxy,
+ base::Bind(&ObjectManagerProxy::OnPropertyChanged,
+ weak_ptr_factory_.GetWeakPtr(),
+ object_path,
+ interface_name)
+ };
+ }
+ if (interface_name == "org.chromium.Itf2") {
+ return new org::chromium::Itf2Proxy::PropertySet{
+ object_proxy,
+ base::Bind(&ObjectManagerProxy::OnPropertyChanged,
+ weak_ptr_factory_.GetWeakPtr(),
+ object_path,
+ interface_name)
+ };
+ }
+ LOG(FATAL) << "Creating properties for unsupported interface "
+ << interface_name;
+ return nullptr;
+ }
+
+ scoped_refptr<dbus::Bus> bus_;
+ std::string service_name_;
+ dbus::ObjectManager* dbus_object_manager_;
+ std::map<dbus::ObjectPath,
+ std::unique_ptr<org::chromium::Itf1Proxy>> itf1_instances_;
+ base::Callback<void(org::chromium::Itf1Proxy*)> on_itf1_added_;
+ base::Callback<void(const dbus::ObjectPath&)> on_itf1_removed_;
+ std::map<dbus::ObjectPath,
+ std::unique_ptr<org::chromium::Itf2Proxy>> itf2_instances_;
+ base::Callback<void(org::chromium::Itf2Proxy*)> on_itf2_added_;
+ base::Callback<void(const dbus::ObjectPath&)> on_itf2_removed_;
+ base::WeakPtrFactory<ObjectManagerProxy> weak_ptr_factory_{this};
+
+ DISALLOW_COPY_AND_ASSIGN(ObjectManagerProxy);
+};
+
+} // namespace chromium
+} // namespace org
+)literal_string";
+
+const char kExpectedContentWithObjectManagerAndServiceName[] = R"literal_string(
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/callback.h>
+#include <base/logging.h>
+#include <base/macros.h>
+#include <base/memory/ref_counted.h>
+#include <chromeos/any.h>
+#include <chromeos/dbus/dbus_method_invoker.h>
+#include <chromeos/dbus/dbus_property.h>
+#include <chromeos/dbus/dbus_signal_handler.h>
+#include <chromeos/errors/error.h>
+#include <chromeos/variant_dictionary.h>
+#include <dbus/bus.h>
+#include <dbus/message.h>
+#include <dbus/object_manager.h>
+#include <dbus/object_path.h>
+#include <dbus/object_proxy.h>
+
+namespace org {
+namespace chromium {
+class ObjectManagerProxy;
+} // namespace chromium
+} // namespace org
+
+namespace org {
+namespace chromium {
+
+// Abstract interface proxy for org::chromium::Itf1.
+class Itf1ProxyInterface {
+ public:
+ virtual ~Itf1ProxyInterface() = default;
+
+ virtual void RegisterCloserSignalHandler(
+ const base::Closure& signal_callback,
+ dbus::ObjectProxy::OnConnectedCallback on_connected_callback) = 0;
+};
+
+} // namespace chromium
+} // namespace org
+
+namespace org {
+namespace chromium {
+
+// Interface proxy for org::chromium::Itf1.
+class Itf1Proxy final : public Itf1ProxyInterface {
+ public:
+ class PropertySet : public dbus::PropertySet {
+ public:
+ PropertySet(dbus::ObjectProxy* object_proxy,
+ const PropertyChangedCallback& callback)
+ : dbus::PropertySet{object_proxy,
+ "org.chromium.Itf1",
+ callback} {
+ }
+
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PropertySet);
+ };
+
+ Itf1Proxy(const scoped_refptr<dbus::Bus>& bus) :
+ bus_{bus},
+ dbus_object_proxy_{
+ bus_->GetObjectProxy(service_name_, object_path_)} {
+ }
+
+ ~Itf1Proxy() override {
+ }
+
+ void RegisterCloserSignalHandler(
+ const base::Closure& signal_callback,
+ dbus::ObjectProxy::OnConnectedCallback on_connected_callback) override {
+ chromeos::dbus_utils::ConnectToSignal(
+ dbus_object_proxy_,
+ "org.chromium.Itf1",
+ "Closer",
+ signal_callback,
+ on_connected_callback);
+ }
+
+ void ReleaseObjectProxy(const base::Closure& callback) {
+ bus_->RemoveObjectProxy(service_name_, object_path_, callback);
+ }
+
+ const dbus::ObjectPath& GetObjectPath() const {
+ return object_path_;
+ }
+
+ dbus::ObjectProxy* GetObjectProxy() const { return dbus_object_proxy_; }
+
+ private:
+ scoped_refptr<dbus::Bus> bus_;
+ const std::string service_name_{"org.chromium.Test"};
+ const dbus::ObjectPath object_path_{"/org/chromium/Test/Object"};
+ dbus::ObjectProxy* dbus_object_proxy_;
+
+ DISALLOW_COPY_AND_ASSIGN(Itf1Proxy);
+};
+
+} // namespace chromium
+} // namespace org
+
+namespace org {
+namespace chromium {
+
+// Abstract interface proxy for org::chromium::Itf2.
+class Itf2ProxyInterface {
+ public:
+ virtual ~Itf2ProxyInterface() = default;
+};
+
+} // namespace chromium
+} // namespace org
+
+namespace org {
+namespace chromium {
+
+// Interface proxy for org::chromium::Itf2.
+class Itf2Proxy final : public Itf2ProxyInterface {
+ public:
+ class PropertySet : public dbus::PropertySet {
+ public:
+ PropertySet(dbus::ObjectProxy* object_proxy,
+ const PropertyChangedCallback& callback)
+ : dbus::PropertySet{object_proxy,
+ "org.chromium.Itf2",
+ callback} {
+ }
+
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PropertySet);
+ };
+
+ Itf2Proxy(
+ const scoped_refptr<dbus::Bus>& bus,
+ const dbus::ObjectPath& object_path) :
+ bus_{bus},
+ object_path_{object_path},
+ dbus_object_proxy_{
+ bus_->GetObjectProxy(service_name_, object_path_)} {
+ }
+
+ ~Itf2Proxy() override {
+ }
+
+ void ReleaseObjectProxy(const base::Closure& callback) {
+ bus_->RemoveObjectProxy(service_name_, object_path_, callback);
+ }
+
+ const dbus::ObjectPath& GetObjectPath() const {
+ return object_path_;
+ }
+
+ dbus::ObjectProxy* GetObjectProxy() const { return dbus_object_proxy_; }
+
+ private:
+ scoped_refptr<dbus::Bus> bus_;
+ const std::string service_name_{"org.chromium.Test"};
+ dbus::ObjectPath object_path_;
+ dbus::ObjectProxy* dbus_object_proxy_;
+
+ DISALLOW_COPY_AND_ASSIGN(Itf2Proxy);
+};
+
+} // namespace chromium
+} // namespace org
+
+namespace org {
+namespace chromium {
+
+class ObjectManagerProxy : public dbus::ObjectManager::Interface {
+ public:
+ ObjectManagerProxy(const scoped_refptr<dbus::Bus>& bus)
+ : bus_{bus},
+ dbus_object_manager_{bus->GetObjectManager(
+ "org.chromium.Test",
+ dbus::ObjectPath{"/org/chromium/Test"})} {
+ dbus_object_manager_->RegisterInterface("org.chromium.Itf1", this);
+ dbus_object_manager_->RegisterInterface("org.chromium.Itf2", this);
+ }
+
+ ~ObjectManagerProxy() override {
+ dbus_object_manager_->UnregisterInterface("org.chromium.Itf1");
+ dbus_object_manager_->UnregisterInterface("org.chromium.Itf2");
+ }
+
+ dbus::ObjectManager* GetObjectManagerProxy() const {
+ return dbus_object_manager_;
+ }
+
+ org::chromium::Itf1Proxy* GetItf1Proxy() {
+ if (itf1_instances_.empty())
+ return nullptr;
+ return itf1_instances_.begin()->second.get();
+ }
+ std::vector<org::chromium::Itf1Proxy*> GetItf1Instances() const {
+ std::vector<org::chromium::Itf1Proxy*> values;
+ values.reserve(itf1_instances_.size());
+ for (const auto& pair : itf1_instances_)
+ values.push_back(pair.second.get());
+ return values;
+ }
+ void SetItf1AddedCallback(
+ const base::Callback<void(org::chromium::Itf1Proxy*)>& callback) {
+ on_itf1_added_ = callback;
+ }
+ void SetItf1RemovedCallback(
+ const base::Callback<void(const dbus::ObjectPath&)>& callback) {
+ on_itf1_removed_ = callback;
+ }
+
+ org::chromium::Itf2Proxy* GetItf2Proxy(
+ const dbus::ObjectPath& object_path) {
+ auto p = itf2_instances_.find(object_path);
+ if (p != itf2_instances_.end())
+ return p->second.get();
+ return nullptr;
+ }
+ std::vector<org::chromium::Itf2Proxy*> GetItf2Instances() const {
+ std::vector<org::chromium::Itf2Proxy*> values;
+ values.reserve(itf2_instances_.size());
+ for (const auto& pair : itf2_instances_)
+ values.push_back(pair.second.get());
+ return values;
+ }
+ void SetItf2AddedCallback(
+ const base::Callback<void(org::chromium::Itf2Proxy*)>& callback) {
+ on_itf2_added_ = callback;
+ }
+ void SetItf2RemovedCallback(
+ const base::Callback<void(const dbus::ObjectPath&)>& callback) {
+ on_itf2_removed_ = callback;
+ }
+
+ private:
+ void OnPropertyChanged(const dbus::ObjectPath& object_path,
+ const std::string& interface_name,
+ const std::string& property_name) {
+ }
+
+ void ObjectAdded(
+ const dbus::ObjectPath& object_path,
+ const std::string& interface_name) override {
+ if (interface_name == "org.chromium.Itf1") {
+ std::unique_ptr<org::chromium::Itf1Proxy> itf1_proxy{
+ new org::chromium::Itf1Proxy{bus_}
+ };
+ auto p = itf1_instances_.emplace(object_path, std::move(itf1_proxy));
+ if (!on_itf1_added_.is_null())
+ on_itf1_added_.Run(p.first->second.get());
+ return;
+ }
+ if (interface_name == "org.chromium.Itf2") {
+ std::unique_ptr<org::chromium::Itf2Proxy> itf2_proxy{
+ new org::chromium::Itf2Proxy{bus_, object_path}
+ };
+ auto p = itf2_instances_.emplace(object_path, std::move(itf2_proxy));
+ if (!on_itf2_added_.is_null())
+ on_itf2_added_.Run(p.first->second.get());
+ return;
+ }
+ }
+
+ void ObjectRemoved(
+ const dbus::ObjectPath& object_path,
+ const std::string& interface_name) override {
+ if (interface_name == "org.chromium.Itf1") {
+ auto p = itf1_instances_.find(object_path);
+ if (p != itf1_instances_.end()) {
+ if (!on_itf1_removed_.is_null())
+ on_itf1_removed_.Run(object_path);
+ itf1_instances_.erase(p);
+ }
+ return;
+ }
+ if (interface_name == "org.chromium.Itf2") {
+ auto p = itf2_instances_.find(object_path);
+ if (p != itf2_instances_.end()) {
+ if (!on_itf2_removed_.is_null())
+ on_itf2_removed_.Run(object_path);
+ itf2_instances_.erase(p);
+ }
+ return;
+ }
+ }
+
+ dbus::PropertySet* CreateProperties(
+ dbus::ObjectProxy* object_proxy,
+ const dbus::ObjectPath& object_path,
+ const std::string& interface_name) override {
+ if (interface_name == "org.chromium.Itf1") {
+ return new org::chromium::Itf1Proxy::PropertySet{
+ object_proxy,
+ base::Bind(&ObjectManagerProxy::OnPropertyChanged,
+ weak_ptr_factory_.GetWeakPtr(),
+ object_path,
+ interface_name)
+ };
+ }
+ if (interface_name == "org.chromium.Itf2") {
+ return new org::chromium::Itf2Proxy::PropertySet{
+ object_proxy,
+ base::Bind(&ObjectManagerProxy::OnPropertyChanged,
+ weak_ptr_factory_.GetWeakPtr(),
+ object_path,
+ interface_name)
+ };
+ }
+ LOG(FATAL) << "Creating properties for unsupported interface "
+ << interface_name;
+ return nullptr;
+ }
+
+ scoped_refptr<dbus::Bus> bus_;
+ dbus::ObjectManager* dbus_object_manager_;
+ std::map<dbus::ObjectPath,
+ std::unique_ptr<org::chromium::Itf1Proxy>> itf1_instances_;
+ base::Callback<void(org::chromium::Itf1Proxy*)> on_itf1_added_;
+ base::Callback<void(const dbus::ObjectPath&)> on_itf1_removed_;
+ std::map<dbus::ObjectPath,
+ std::unique_ptr<org::chromium::Itf2Proxy>> itf2_instances_;
+ base::Callback<void(org::chromium::Itf2Proxy*)> on_itf2_added_;
+ base::Callback<void(const dbus::ObjectPath&)> on_itf2_removed_;
+ base::WeakPtrFactory<ObjectManagerProxy> weak_ptr_factory_{this};
+
+ DISALLOW_COPY_AND_ASSIGN(ObjectManagerProxy);
+};
+
+} // namespace chromium
+} // namespace org
+)literal_string";
+} // namespace
+
+class ProxyGeneratorTest : public Test {
+ public:
+ void SetUp() override {
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ }
+
+ protected:
+ base::FilePath CreateInputFile(const string& contents) {
+ base::FilePath path;
+ EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(), &path));
+ int written = base::WriteFile(path, contents.c_str(), contents.size());
+ EXPECT_EQ(contents.size(), static_cast<size_t>(written));
+ return path;
+ }
+
+ base::ScopedTempDir temp_dir_;
+};
+
+TEST_F(ProxyGeneratorTest, GenerateAdaptors) {
+ Interface interface;
+ interface.name = "org.chromium.TestInterface";
+ interface.path = "/org/chromium/Test";
+ interface.methods.emplace_back(
+ "Elements",
+ vector<Interface::Argument>{
+ {"space_walk", kDBusTypeString},
+ {"ramblin_man", kDBusTypeArryOfObjects}},
+ vector<Interface::Argument>{{"", kDBusTypeString}});
+ interface.methods.emplace_back(
+ "ReturnToPatagonia",
+ vector<Interface::Argument>{},
+ vector<Interface::Argument>{{"", kDBusTypeInt64}});
+ interface.methods.emplace_back(
+ "NiceWeatherForDucks",
+ vector<Interface::Argument>{{"", kDBusTypeBool}},
+ vector<Interface::Argument>{});
+ interface.methods.emplace_back("ExperimentNumberSix");
+ interface.signals.emplace_back("Closer");
+ interface.signals.emplace_back(
+ "TheCurseOfKaZar",
+ vector<Interface::Argument>{
+ {"", kDBusTypeArryOfStrings},
+ {"", kDBusTypeByte}});
+ interface.methods.back().doc_string = "Comment line1\nline2";
+ Interface interface2;
+ interface2.name = "org.chromium.TestInterface2";
+ interface2.methods.emplace_back(
+ "GetPersonInfo",
+ vector<Interface::Argument>{},
+ vector<Interface::Argument>{
+ {"name", kDBusTypeString},
+ {"age", kDBusTypeInt32}});
+ vector<Interface> interfaces{interface, interface2};
+ base::FilePath output_path = temp_dir_.path().Append("output.h");
+ ServiceConfig config;
+ EXPECT_TRUE(ProxyGenerator::GenerateProxies(config, interfaces, output_path));
+ string contents;
+ EXPECT_TRUE(base::ReadFileToString(output_path, &contents));
+ // The header guards contain the (temporary) filename, so we search for
+ // the content we need within the string.
+ test_utils::EXPECT_TEXT_CONTAINED(kExpectedContent, contents);
+}
+
+TEST_F(ProxyGeneratorTest, GenerateAdaptorsWithServiceName) {
+ Interface interface;
+ interface.name = "org.chromium.TestInterface";
+ interface.path = "/org/chromium/Test";
+ interface.signals.emplace_back("Closer");
+ Interface interface2;
+ interface2.name = "org.chromium.TestInterface2";
+ vector<Interface> interfaces{interface, interface2};
+ base::FilePath output_path = temp_dir_.path().Append("output2.h");
+ ServiceConfig config;
+ config.service_name = "org.chromium.Test";
+ EXPECT_TRUE(ProxyGenerator::GenerateProxies(config, interfaces, output_path));
+ string contents;
+ EXPECT_TRUE(base::ReadFileToString(output_path, &contents));
+ // The header guards contain the (temporary) filename, so we search for
+ // the content we need within the string.
+ test_utils::EXPECT_TEXT_CONTAINED(kExpectedContentWithService, contents);
+}
+
+TEST_F(ProxyGeneratorTest, GenerateAdaptorsWithObjectManager) {
+ Interface interface;
+ interface.name = "org.chromium.Itf1";
+ interface.path = "/org/chromium/Test/Object";
+ interface.signals.emplace_back("Closer");
+ interface.properties.emplace_back("Data", "s", "read");
+ Interface interface2;
+ interface2.name = "org.chromium.Itf2";
+ vector<Interface> interfaces{interface, interface2};
+ base::FilePath output_path = temp_dir_.path().Append("output3.h");
+ ServiceConfig config;
+ config.object_manager.name = "org.chromium.ObjectManager";
+ config.object_manager.object_path = "/org/chromium/Test";
+ EXPECT_TRUE(ProxyGenerator::GenerateProxies(config, interfaces, output_path));
+ string contents;
+ EXPECT_TRUE(base::ReadFileToString(output_path, &contents));
+ // The header guards contain the (temporary) filename, so we search for
+ // the content we need within the string.
+ test_utils::EXPECT_TEXT_CONTAINED(
+ kExpectedContentWithObjectManager, contents);
+}
+
+TEST_F(ProxyGeneratorTest, GenerateAdaptorsWithObjectManagerAndServiceName) {
+ Interface interface;
+ interface.name = "org.chromium.Itf1";
+ interface.path = "/org/chromium/Test/Object";
+ interface.signals.emplace_back("Closer");
+ Interface interface2;
+ interface2.name = "org.chromium.Itf2";
+ vector<Interface> interfaces{interface, interface2};
+ base::FilePath output_path = temp_dir_.path().Append("output4.h");
+ ServiceConfig config;
+ config.service_name = "org.chromium.Test";
+ config.object_manager.name = "org.chromium.ObjectManager";
+ config.object_manager.object_path = "/org/chromium/Test";
+ EXPECT_TRUE(ProxyGenerator::GenerateProxies(config, interfaces, output_path));
+ string contents;
+ EXPECT_TRUE(base::ReadFileToString(output_path, &contents));
+ // The header guards contain the (temporary) filename, so we search for
+ // the content we need within the string.
+ test_utils::EXPECT_TEXT_CONTAINED(
+ kExpectedContentWithObjectManagerAndServiceName, contents);
+}
+
+} // namespace chromeos_dbus_bindings
diff --git a/chromeos-dbus-bindings/test_utils.cc b/chromeos-dbus-bindings/test_utils.cc
new file mode 100644
index 0000000..5d019df
--- /dev/null
+++ b/chromeos-dbus-bindings/test_utils.cc
@@ -0,0 +1,81 @@
+// Copyright 2015 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/test_utils.h"
+
+#include <string>
+#include <vector>
+
+#include <base/files/file_path.h>
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <chromeos/process.h>
+#include <gtest/gtest.h>
+
+using std::string;
+using std::vector;
+
+namespace {
+
+// Return the diff between the texts |a| and |b|.
+string GetUnifiedDiff(const string& a, const string& b) {
+ base::FilePath path_a, path_b;
+ if (!base::CreateTemporaryFile(&path_a) ||
+ !base::CreateTemporaryFile(&path_b)) {
+ return "Error creating temporary file";
+ }
+ WriteFile(path_a, a.data(), a.size());
+ WriteFile(path_b, b.data(), b.size());
+
+ chromeos::ProcessImpl proc;
+ proc.AddArg("diff");
+ proc.AddArg("-u");
+ proc.AddArg(path_a.value());
+ proc.AddArg(path_b.value());
+ proc.SetSearchPath(true);
+ proc.RedirectUsingPipe(STDOUT_FILENO, false);
+ proc.Start();
+
+ int fd = proc.GetPipe(STDOUT_FILENO);
+ vector<char> buffer(32 * 1024);
+ string output;
+ while (true) {
+ int rc = read(fd, buffer.data(), buffer.size());
+ if (rc < 0) {
+ PLOG(ERROR) << "Reading from diff.";
+ break;
+ } else if (rc == 0) {
+ break;
+ } else {
+ output.append(buffer.data(), rc);
+ }
+ }
+ proc.Wait();
+
+ base::DeleteFile(path_a, false);
+ base::DeleteFile(path_b, false);
+ return output;
+}
+
+} // namespace
+
+namespace chromeos_dbus_bindings {
+namespace test_utils {
+
+void ExpectTextContained(const tracked_objects::Location& from_here,
+ const string& expected_str,
+ const string& expected_expr,
+ const string& actual_str,
+ const string& actual_expr) {
+ if (string::npos != actual_str.find(expected_str))
+ return;
+
+ ADD_FAILURE_AT(from_here.file_name(), from_here.line_number())
+ << "Expected to find " << expected_expr << " within " << actual_expr
+ << ".\nHere is the diff:\n"
+ << GetUnifiedDiff(expected_str, actual_str);
+}
+
+} // namespace test_utils
+} // namespace chromeos_dbus_bindings
diff --git a/chromeos-dbus-bindings/test_utils.h b/chromeos-dbus-bindings/test_utils.h
new file mode 100644
index 0000000..5619bc1
--- /dev/null
+++ b/chromeos-dbus-bindings/test_utils.h
@@ -0,0 +1,33 @@
+// Copyright 2015 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.
+
+#ifndef CHROMEOS_DBUS_BINDINGS_TEST_UTILS_H_
+#define CHROMEOS_DBUS_BINDINGS_TEST_UTILS_H_
+
+#include <string>
+
+#include <base/location.h>
+
+namespace chromeos_dbus_bindings {
+namespace test_utils {
+
+// Helper macro to call ExpectTextContained().
+#define EXPECT_TEXT_CONTAINED(expected, actual) \
+ ExpectTextContained(FROM_HERE, expected, #expected, actual, #actual)
+
+// Checks that the text |actual_str| is contained in the text |expected_str| and
+// fails the current test if not. If the |actual_str| text is not contained, a
+// meaningful line diff between |actual_str| and |expected_str| is displayed in
+// stderr. Use this function instead of EXPECT_EQ() when the compared values are
+// long texts.
+void ExpectTextContained(const tracked_objects::Location& from_here,
+ const std::string& expected_str,
+ const std::string& expected_expr,
+ const std::string& actual_str,
+ const std::string& actual_expr);
+
+} // namespace test_utils
+} // namespace chromeos_dbus_bindings
+
+#endif // CHROMEOS_DBUS_BINDINGS_TEST_UTILS_H_
diff --git a/chromeos-dbus-bindings/testrunner.cc b/chromeos-dbus-bindings/testrunner.cc
new file mode 100644
index 0000000..0777904
--- /dev/null
+++ b/chromeos-dbus-bindings/testrunner.cc
@@ -0,0 +1,16 @@
+// Copyright (c) 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 <base/at_exit.h>
+#include <base/command_line.h>
+#include <chromeos/syslog_logging.h>
+#include <gtest/gtest.h>
+
+int main(int argc, char** argv) {
+ base::AtExitManager exit_manager;
+ base::CommandLine::Init(argc, argv);
+ chromeos::InitLog(chromeos::kLogToStderr);
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/chromeos-dbus-bindings/xml_interface_parser.cc b/chromeos-dbus-bindings/xml_interface_parser.cc
new file mode 100644
index 0000000..cb07e31
--- /dev/null
+++ b/chromeos-dbus-bindings/xml_interface_parser.cc
@@ -0,0 +1,351 @@
+// 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/files/file_path.h>
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+#include <chromeos/strings/string_utils.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::kPropertyTag[] = "property";
+const char XmlInterfaceParser::kAnnotationTag[] = "annotation";
+const char XmlInterfaceParser::kDocStringTag[] = "tp:docstring";
+const char XmlInterfaceParser::kNameAttribute[] = "name";
+const char XmlInterfaceParser::kTypeAttribute[] = "type";
+const char XmlInterfaceParser::kValueAttribute[] = "value";
+const char XmlInterfaceParser::kDirectionAttribute[] = "direction";
+const char XmlInterfaceParser::kAccessAttribute[] = "access";
+const char XmlInterfaceParser::kArgumentDirectionIn[] = "in";
+const char XmlInterfaceParser::kArgumentDirectionOut[] = "out";
+
+const char XmlInterfaceParser::kTrue[] = "true";
+const char XmlInterfaceParser::kFalse[] = "false";
+
+const char XmlInterfaceParser::kMethodConst[] =
+ "org.chromium.DBus.Method.Const";
+const char XmlInterfaceParser::kMethodAsync[] =
+ "org.freedesktop.DBus.GLib.Async";
+const char XmlInterfaceParser::kMethodIncludeDBusMessage[] =
+ "org.chromium.DBus.Method.IncludeDBusMessage";
+
+const char XmlInterfaceParser::kMethodKind[] = "org.chromium.DBus.Method.Kind";
+const char XmlInterfaceParser::kMethodKindSimple[] = "simple";
+const char XmlInterfaceParser::kMethodKindNormal[] = "normal";
+const char XmlInterfaceParser::kMethodKindAsync[] = "async";
+const char XmlInterfaceParser::kMethodKindRaw[] = "raw";
+
+namespace {
+
+string GetElementPath(const vector<string>& path) {
+ return chromeos::string_utils::Join("/", path);
+}
+
+} // anonymous namespace
+
+bool XmlInterfaceParser::ParseXmlInterfaceFile(
+ const std::string& contents,
+ const std::vector<std::string>& ignore_interfaces) {
+ auto parser = XML_ParserCreate(nullptr);
+ XML_SetUserData(parser, this);
+ XML_SetElementHandler(parser,
+ &XmlInterfaceParser::HandleElementStart,
+ &XmlInterfaceParser::HandleElementEnd);
+ XML_SetCharacterDataHandler(parser, &XmlInterfaceParser::HandleCharData);
+ 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());
+
+ if (!ignore_interfaces.empty()) {
+ // Remove interfaces whose names are in |ignore_interfaces| list.
+ auto condition = [&ignore_interfaces](const Interface& itf) {
+ return std::find(ignore_interfaces.begin(), ignore_interfaces.end(),
+ itf.name) != ignore_interfaces.end();
+ };
+ auto p = std::remove_if(interfaces_.begin(), interfaces_.end(), condition);
+ interfaces_.erase(p, interfaces_.end());
+ }
+ return true;
+}
+
+void XmlInterfaceParser::OnOpenElement(
+ const string& element_name, const XmlAttributeMap& attributes) {
+ string prev_element;
+ if (!element_path_.empty())
+ prev_element = element_path_.back();
+ element_path_.push_back(element_name);
+ if (element_name == kNodeTag) {
+ CHECK(prev_element.empty() || prev_element == kNodeTag)
+ << "Unexpected tag " << element_name << " inside " << prev_element;
+ // 'name' attribute is optional for <node> element.
+ string name;
+ GetElementAttribute(attributes, element_path_, kNameAttribute, &name);
+ base::TrimWhitespaceASCII(name, base::TRIM_ALL, &name);
+ node_names_.push_back(name);
+ } else if (element_name == kInterfaceTag) {
+ CHECK_EQ(kNodeTag, prev_element)
+ << "Unexpected tag " << element_name << " inside " << prev_element;
+ string interface_name = GetValidatedElementName(attributes, element_path_);
+ Interface itf(interface_name,
+ std::vector<Interface::Method>{},
+ std::vector<Interface::Signal>{},
+ std::vector<Interface::Property>{});
+ itf.path = node_names_.back();
+ interfaces_.push_back(std::move(itf));
+ } else if (element_name == kMethodTag) {
+ CHECK_EQ(kInterfaceTag, prev_element)
+ << "Unexpected tag " << element_name << " inside " << prev_element;
+ interfaces_.back().methods.push_back(
+ Interface::Method(GetValidatedElementName(attributes, element_path_)));
+ } else if (element_name == kSignalTag) {
+ CHECK_EQ(kInterfaceTag, prev_element)
+ << "Unexpected tag " << element_name << " inside " << prev_element;
+ interfaces_.back().signals.push_back(
+ Interface::Signal(GetValidatedElementName(attributes, element_path_)));
+ } else if (element_name == kPropertyTag) {
+ CHECK_EQ(kInterfaceTag, prev_element)
+ << "Unexpected tag " << element_name << " inside " << prev_element;
+ interfaces_.back().properties.push_back(ParseProperty(attributes,
+ element_path_));
+ } else if (element_name == kArgumentTag) {
+ if (prev_element == kMethodTag) {
+ AddMethodArgument(attributes);
+ } else if (prev_element == kSignalTag) {
+ AddSignalArgument(attributes);
+ } else {
+ LOG(FATAL) << "Unexpected tag " << element_name
+ << " inside " << prev_element;
+ }
+ } else if (element_name == kAnnotationTag) {
+ string name = GetValidatedElementAttribute(attributes, element_path_,
+ kNameAttribute);
+ // Value is optional. Default to empty string if omitted.
+ string value;
+ GetElementAttribute(attributes, element_path_, kValueAttribute, &value);
+ if (prev_element == kInterfaceTag) {
+ // Parse interface annotations...
+ } else if (prev_element == kMethodTag) {
+ // Parse method annotations...
+ Interface::Method& method = interfaces_.back().methods.back();
+ if (name == kMethodConst) {
+ CHECK(value == kTrue || value == kFalse);
+ method.is_const = (value == kTrue);
+ } else if (name == kMethodIncludeDBusMessage) {
+ CHECK(value == kTrue || value == kFalse);
+ method.include_dbus_message = (value == kTrue);
+ } else if (name == kMethodAsync) {
+ // Support GLib.Async annotation as well.
+ method.kind = Interface::Method::Kind::kAsync;
+ } else if (name == kMethodKind) {
+ if (value == kMethodKindSimple) {
+ method.kind = Interface::Method::Kind::kSimple;
+ } else if (value == kMethodKindNormal) {
+ method.kind = Interface::Method::Kind::kNormal;
+ } else if (value == kMethodKindAsync) {
+ method.kind = Interface::Method::Kind::kAsync;
+ } else if (value == kMethodKindRaw) {
+ method.kind = Interface::Method::Kind::kRaw;
+ } else {
+ LOG(FATAL) << "Invalid method kind: " << value;
+ }
+ }
+ } else if (prev_element == kSignalTag) {
+ // Parse signal annotations...
+ } else if (prev_element == kPropertyTag) {
+ // Parse property annotations...
+ } else {
+ LOG(FATAL) << "Unexpected tag " << element_name
+ << " inside " << prev_element;
+ }
+ } else if (element_name == kDocStringTag) {
+ CHECK(!prev_element.empty() && prev_element != kNodeTag)
+ << "Unexpected tag " << element_name << " inside " << prev_element;
+ }
+}
+
+void XmlInterfaceParser::OnCharData(const std::string& content) {
+ // Handle the text data only for <tp:docstring> element.
+ if (element_path_.back() != kDocStringTag)
+ return;
+
+ CHECK_GT(element_path_.size(), 1u);
+ string* doc_string_ptr = nullptr;
+ string target_element = element_path_[element_path_.size() - 2];
+ if (target_element == kInterfaceTag) {
+ doc_string_ptr = &(interfaces_.back().doc_string);
+ } else if (target_element == kMethodTag) {
+ doc_string_ptr = &(interfaces_.back().methods.back().doc_string);
+ } else if (target_element == kSignalTag) {
+ doc_string_ptr = &(interfaces_.back().signals.back().doc_string);
+ } else if (target_element == kPropertyTag) {
+ doc_string_ptr = &(interfaces_.back().properties.back().doc_string);
+ }
+
+ // If <tp:docstring> is attached to elements we don't care about, do nothing.
+ if (doc_string_ptr == nullptr)
+ return;
+
+ (*doc_string_ptr) += content;
+}
+
+
+void XmlInterfaceParser::AddMethodArgument(const XmlAttributeMap& attributes) {
+ string argument_direction;
+ vector<string> path = element_path_;
+ path.push_back(kArgumentTag);
+ bool is_direction_paramter_present = GetElementAttribute(
+ attributes, path, kDirectionAttribute, &argument_direction);
+ vector<Interface::Argument>* argument_list = nullptr;
+ if (!is_direction_paramter_present ||
+ argument_direction == kArgumentDirectionIn) {
+ argument_list = &interfaces_.back().methods.back().input_arguments;
+ } else if (argument_direction == kArgumentDirectionOut) {
+ argument_list = &interfaces_.back().methods.back().output_arguments;
+ } else {
+ LOG(FATAL) << "Unknown method argument direction " << argument_direction;
+ }
+ argument_list->push_back(ParseArgument(attributes, element_path_));
+}
+
+void XmlInterfaceParser::AddSignalArgument(const XmlAttributeMap& attributes) {
+ interfaces_.back().signals.back().arguments.push_back(
+ ParseArgument(attributes, element_path_));
+}
+
+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();
+ if (element_name == kNodeTag) {
+ CHECK(!node_names_.empty());
+ node_names_.pop_back();
+ }
+}
+
+// static
+bool XmlInterfaceParser::GetElementAttribute(
+ const XmlAttributeMap& attributes,
+ const vector<string>& element_path,
+ const string& element_key,
+ string* element_value) {
+ if (attributes.find(element_key) == attributes.end()) {
+ return false;
+ }
+ *element_value = attributes.find(element_key)->second;
+ VLOG(1) << "Got " << GetElementPath(element_path) << " element with "
+ << element_key << " = " << *element_value;
+ return true;
+}
+
+// static
+string XmlInterfaceParser::GetValidatedElementAttribute(
+ const XmlAttributeMap& attributes,
+ const vector<string>& element_path,
+ const string& element_key) {
+ string element_value;
+ CHECK(GetElementAttribute(attributes,
+ element_path,
+ element_key,
+ &element_value))
+ << GetElementPath(element_path) << " does not contain a " << element_key
+ << " attribute";
+ CHECK(!element_value.empty()) << GetElementPath(element_path) << " "
+ << element_key << " attribute is empty";
+ return element_value;
+}
+
+// static
+string XmlInterfaceParser::GetValidatedElementName(
+ const XmlAttributeMap& attributes,
+ const vector<string>& element_path) {
+ return GetValidatedElementAttribute(attributes, element_path, kNameAttribute);
+}
+
+// static
+Interface::Argument XmlInterfaceParser::ParseArgument(
+ const XmlAttributeMap& attributes, const vector<string>& element_path) {
+ vector<string> path = element_path;
+ path.push_back(kArgumentTag);
+ string argument_name;
+ // Since the "name" field is optional, use the un-validated variant.
+ GetElementAttribute(attributes, path, kNameAttribute, &argument_name);
+
+ string argument_type = GetValidatedElementAttribute(
+ attributes, path, kTypeAttribute);
+ return Interface::Argument(argument_name, argument_type);
+}
+
+// static
+Interface::Property XmlInterfaceParser::ParseProperty(
+ const XmlAttributeMap& attributes,
+ const std::vector<std::string>& element_path) {
+ vector<string> path = element_path;
+ path.push_back(kPropertyTag);
+ string property_name = GetValidatedElementName(attributes, path);
+ string property_type = GetValidatedElementAttribute(attributes, path,
+ kTypeAttribute);
+ string property_access = GetValidatedElementAttribute(attributes, path,
+ kAccessAttribute);
+ return Interface::Property(property_name, property_type, property_access);
+}
+
+// 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);
+}
+
+// static
+void XmlInterfaceParser::HandleCharData(void* user_data,
+ const char *content,
+ int length) {
+ auto parser = reinterpret_cast<XmlInterfaceParser*>(user_data);
+ parser->OnCharData(string(content, length));
+}
+
+} // namespace chromeos_dbus_bindings
diff --git a/chromeos-dbus-bindings/xml_interface_parser.h b/chromeos-dbus-bindings/xml_interface_parser.h
new file mode 100644
index 0000000..15ad857
--- /dev/null
+++ b/chromeos-dbus-bindings/xml_interface_parser.h
@@ -0,0 +1,136 @@
+// 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.
+
+#ifndef CHROMEOS_DBUS_BINDINGS_XML_INTERFACE_PARSER_H_
+#define CHROMEOS_DBUS_BINDINGS_XML_INTERFACE_PARSER_H_
+
+#include <expat.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+
+#include "chromeos-dbus-bindings/interface.h"
+
+namespace base {
+
+class FilePath;
+
+} // namespace base
+
+namespace chromeos_dbus_bindings {
+
+class XmlInterfaceParser {
+ public:
+ using XmlAttributeMap = std::map<std::string, std::string>;
+
+ XmlInterfaceParser() = default;
+ virtual ~XmlInterfaceParser() = default;
+
+ bool ParseXmlInterfaceFile(const std::string& contents,
+ const std::vector<std::string>& ignore_interfaces);
+ const std::vector<Interface>& interfaces() const { return interfaces_; }
+
+ private:
+ friend class XmlInterfaceParserTest;
+
+ // XML tag names.
+ static const char kArgumentTag[];
+ static const char kInterfaceTag[];
+ static const char kMethodTag[];
+ static const char kNodeTag[];
+ static const char kSignalTag[];
+ static const char kPropertyTag[];
+ static const char kAnnotationTag[];
+ static const char kDocStringTag[];
+
+ // XML attribute names.
+ static const char kNameAttribute[];
+ static const char kTypeAttribute[];
+ static const char kDirectionAttribute[];
+ static const char kAccessAttribute[];
+ static const char kValueAttribute[];
+
+ // XML argument directions.
+ static const char kArgumentDirectionIn[];
+ static const char kArgumentDirectionOut[];
+
+ // XML annotations.
+ static const char kTrue[];
+ static const char kFalse[];
+
+ static const char kMethodConst[];
+ static const char kMethodAsync[];
+ static const char kMethodIncludeDBusMessage[];
+
+ static const char kMethodKind[];
+ static const char kMethodKindSimple[];
+ static const char kMethodKindNormal[];
+ static const char kMethodKindAsync[];
+ static const char kMethodKindRaw[];
+
+ // Element callbacks on |this| called by HandleElementStart() and
+ // HandleElementEnd(), respectively.
+ void OnOpenElement(const std::string& element_name,
+ const XmlAttributeMap& attributes);
+ void OnCloseElement(const std::string& element_name);
+ void OnCharData(const std::string& content);
+
+ // Methods for appending individual argument elements to the parser.
+ void AddMethodArgument(const XmlAttributeMap& attributes);
+ void AddSignalArgument(const XmlAttributeMap& attributes);
+
+ // Finds the |element_key| element in |attributes|. Returns true and sets
+ // |element_value| on success. Returns false otherwise.
+ static bool GetElementAttribute(const XmlAttributeMap& attributes,
+ const std::vector<std::string>& element_path,
+ const std::string& element_key,
+ std::string* element_value);
+
+ // Asserts that a non-empty |element_key| attribute appears in |attributes|.
+ // Returns the name on success, triggers a CHECK() otherwise.
+ static std::string GetValidatedElementAttribute(
+ const XmlAttributeMap& attributes,
+ const std::vector<std::string>& element_path,
+ const std::string& element_key);
+
+ // Calls GetValidatedElementAttribute() for the "name" property.
+ static std::string GetValidatedElementName(
+ const XmlAttributeMap& attributes,
+ const std::vector<std::string>& element_path);
+
+ // Method for extracting signal/method tag attributes to a struct.
+ static Interface::Argument ParseArgument(
+ const XmlAttributeMap& attributes,
+ const std::vector<std::string>& element_path);
+
+ // Method for extracting property tag attributes to a struct.
+ static Interface::Property ParseProperty(
+ const XmlAttributeMap& attributes,
+ const std::vector<std::string>& element_path);
+
+ // Expat element callback functions.
+ static void HandleElementStart(void* user_data,
+ const XML_Char* element,
+ const XML_Char** attr);
+ static void HandleElementEnd(void* user_data, const XML_Char* element);
+ static void HandleCharData(void* user_data, const char *content, int length);
+
+ // The output of the parse.
+ std::vector<Interface> interfaces_;
+
+ // A stack of <node> names used to track the object paths for interfaces.
+ std::vector<std::string> node_names_;
+
+ // Tracks where in the element traversal our parse has taken us.
+ std::vector<std::string> element_path_;
+
+ DISALLOW_COPY_AND_ASSIGN(XmlInterfaceParser);
+};
+
+} // namespace chromeos_dbus_bindings
+
+#endif // CHROMEOS_DBUS_BINDINGS_XML_INTERFACE_PARSER_H_
diff --git a/chromeos-dbus-bindings/xml_interface_parser_unittest.cc b/chromeos-dbus-bindings/xml_interface_parser_unittest.cc
new file mode 100644
index 0000000..69173ec
--- /dev/null
+++ b/chromeos-dbus-bindings/xml_interface_parser_unittest.cc
@@ -0,0 +1,129 @@
+// 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 <base/files/file_path.h>
+#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+#include <gtest/gtest.h>
+
+#include "chromeos-dbus-bindings/interface.h"
+
+using std::string;
+using std::vector;
+using testing::Test;
+
+namespace chromeos_dbus_bindings {
+
+namespace {
+
+const char kBadInterfaceFileContents0[] = "This has no resemblance to XML";
+const char kBadInterfaceFileContents1[] = "<node>";
+const char kGoodInterfaceFileContents[] = R"literal_string(
+<node name="/org/chromium/Test">
+ <interface name="fi.w1.wpa_supplicant1.Interface">
+ <method name="Scan">
+ <arg name="args" type="a{sv}" direction="in"/>
+ <annotation name="org.chromium.DBus.Method.Kind" value="async"/>
+ <annotation name="org.chromium.DBus.Method.IncludeDBusMessage"
+ value="true"/>
+ </method>
+ <method name="GetBlob">
+ <arg name="name" type="s"/>
+ <arg name="data" type="ay" direction="out"/>
+ <annotation name="org.chromium.DBus.Method.Const" value="true"/>
+ </method>
+ <property name="Capabilities" type="a{sv}" access="read"/>
+ <signal name="BSSRemoved">
+ <arg name="BSS" type="o"/>
+ </signal>
+ </interface>
+ <interface name="DummyInterface" />
+ <node name="/"/>
+ <node/>
+</node>
+)literal_string";
+const char kInterfaceName[] = "fi.w1.wpa_supplicant1.Interface";
+const char kScanMethod[] = "Scan";
+const char kArgsArgument[] = "args";
+const char kArrayStringVariantType[] = "a{sv}";
+const char kGetBlobMethod[] = "GetBlob";
+const char kNameArgument[] = "name";
+const char kDataArgument[] = "data";
+const char kStringType[] = "s";
+const char kArrayByteType[] = "ay";
+const char kBssRemovedSignal[] = "BSSRemoved";
+const char kBssArgument[] = "BSS";
+const char kObjectType[] = "o";
+const char kCapabilitiesProperty[] = "Capabilities";
+const char kReadAccess[] = "read";
+} // namespace
+
+class XmlInterfaceParserTest : public Test {
+ protected:
+ XmlInterfaceParser parser_;
+};
+
+TEST_F(XmlInterfaceParserTest, BadInputFile) {
+ EXPECT_FALSE(parser_.ParseXmlInterfaceFile(kBadInterfaceFileContents0, {}));
+ EXPECT_FALSE(parser_.ParseXmlInterfaceFile(kBadInterfaceFileContents1, {}));
+}
+
+TEST_F(XmlInterfaceParserTest, GoodInputFile) {
+ EXPECT_TRUE(parser_.ParseXmlInterfaceFile(kGoodInterfaceFileContents,
+ {"DummyInterface"}));
+ const vector<Interface>& interfaces = parser_.interfaces();
+ ASSERT_EQ(1u, interfaces.size());
+ const Interface& interface = interfaces.back();
+
+ EXPECT_EQ(kInterfaceName, interface.name);
+ EXPECT_EQ("/org/chromium/Test", interface.path);
+ ASSERT_EQ(2u, interface.methods.size());
+ ASSERT_EQ(1u, interface.signals.size());
+
+ // <method name="Scan">
+ EXPECT_EQ(kScanMethod, interface.methods[0].name);
+ EXPECT_EQ(Interface::Method::Kind::kAsync, interface.methods[0].kind);
+ EXPECT_FALSE(interface.methods[0].is_const);
+ EXPECT_TRUE(interface.methods[0].include_dbus_message);
+ ASSERT_EQ(1u, interface.methods[0].input_arguments.size());
+
+ // <arg name="args" type="a{sv}" direction="in"/>
+ EXPECT_EQ(kArgsArgument, interface.methods[0].input_arguments[0].name);
+ EXPECT_EQ(kArrayStringVariantType,
+ interface.methods[0].input_arguments[0].type);
+ EXPECT_EQ(0u, interface.methods[0].output_arguments.size());
+
+ // <method name="GetBlob">
+ EXPECT_EQ(kGetBlobMethod, interface.methods[1].name);
+ EXPECT_EQ(Interface::Method::Kind::kNormal, interface.methods[1].kind);
+ EXPECT_TRUE(interface.methods[1].is_const);
+ EXPECT_FALSE(interface.methods[1].include_dbus_message);
+ EXPECT_EQ(1u, interface.methods[1].input_arguments.size());
+ EXPECT_EQ(1u, interface.methods[1].output_arguments.size());
+
+ // <arg name="name" type="s"/> (direction="in" is implicit)
+ EXPECT_EQ(kNameArgument, interface.methods[1].input_arguments[0].name);
+ EXPECT_EQ(kStringType, interface.methods[1].input_arguments[0].type);
+
+ // <arg name="data" type="ay" direction="out"/>
+ EXPECT_EQ(kDataArgument, interface.methods[1].output_arguments[0].name);
+ EXPECT_EQ(kArrayByteType, interface.methods[1].output_arguments[0].type);
+
+ // <signal name="BSSRemoved">
+ EXPECT_EQ(kBssRemovedSignal, interface.signals[0].name);
+ EXPECT_EQ(1u, interface.signals[0].arguments.size());
+
+ // <arg name="BSS" type="o"/>
+ EXPECT_EQ(kBssArgument, interface.signals[0].arguments[0].name);
+ EXPECT_EQ(kObjectType, interface.signals[0].arguments[0].type);
+
+ // <property name="Capabilities" type="s" access="read"/>
+ EXPECT_EQ(kCapabilitiesProperty, interface.properties[0].name);
+ EXPECT_EQ(kArrayStringVariantType, interface.properties[0].type);
+ EXPECT_EQ(kReadAccess, interface.properties[0].access);
+}
+
+} // namespace chromeos_dbus_bindings