1 // Copyright 2014 The Chromium OS Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chromeos-dbus-bindings/xml_interface_parser.h" 6 7 #include <utility> 8 9 #include <base/files/file_path.h> 10 #include <base/files/file_util.h> 11 #include <base/logging.h> 12 #include <base/strings/string_util.h> 13 #include <brillo/strings/string_utils.h> 14 15 using std::string; 16 using std::vector; 17 18 namespace chromeos_dbus_bindings { 19 20 // static 21 const char XmlInterfaceParser::kArgumentTag[] = "arg"; 22 const char XmlInterfaceParser::kInterfaceTag[] = "interface"; 23 const char XmlInterfaceParser::kMethodTag[] = "method"; 24 const char XmlInterfaceParser::kNodeTag[] = "node"; 25 const char XmlInterfaceParser::kSignalTag[] = "signal"; 26 const char XmlInterfaceParser::kPropertyTag[] = "property"; 27 const char XmlInterfaceParser::kAnnotationTag[] = "annotation"; 28 const char XmlInterfaceParser::kDocStringTag[] = "tp:docstring"; 29 const char XmlInterfaceParser::kNameAttribute[] = "name"; 30 const char XmlInterfaceParser::kTypeAttribute[] = "type"; 31 const char XmlInterfaceParser::kValueAttribute[] = "value"; 32 const char XmlInterfaceParser::kDirectionAttribute[] = "direction"; 33 const char XmlInterfaceParser::kAccessAttribute[] = "access"; 34 const char XmlInterfaceParser::kArgumentDirectionIn[] = "in"; 35 const char XmlInterfaceParser::kArgumentDirectionOut[] = "out"; 36 37 const char XmlInterfaceParser::kTrue[] = "true"; 38 const char XmlInterfaceParser::kFalse[] = "false"; 39 40 const char XmlInterfaceParser::kMethodConst[] = 41 "org.chromium.DBus.Method.Const"; 42 const char XmlInterfaceParser::kMethodAsync[] = 43 "org.freedesktop.DBus.GLib.Async"; 44 const char XmlInterfaceParser::kMethodIncludeDBusMessage[] = 45 "org.chromium.DBus.Method.IncludeDBusMessage"; 46 47 const char XmlInterfaceParser::kMethodKind[] = "org.chromium.DBus.Method.Kind"; 48 const char XmlInterfaceParser::kMethodKindSimple[] = "simple"; 49 const char XmlInterfaceParser::kMethodKindNormal[] = "normal"; 50 const char XmlInterfaceParser::kMethodKindAsync[] = "async"; 51 const char XmlInterfaceParser::kMethodKindRaw[] = "raw"; 52 53 namespace { 54 55 string GetElementPath(const vector<string>& path) { 56 return brillo::string_utils::Join("/", path); 57 } 58 59 } // anonymous namespace 60 61 bool XmlInterfaceParser::ParseXmlInterfaceFile( 62 const std::string& contents, 63 const std::vector<std::string>& ignore_interfaces) { 64 auto parser = XML_ParserCreate(nullptr); 65 XML_SetUserData(parser, this); 66 XML_SetElementHandler(parser, 67 &XmlInterfaceParser::HandleElementStart, 68 &XmlInterfaceParser::HandleElementEnd); 69 XML_SetCharacterDataHandler(parser, &XmlInterfaceParser::HandleCharData); 70 const int kIsFinal = XML_TRUE; 71 72 element_path_.clear(); 73 XML_Status res = XML_Parse(parser, 74 contents.c_str(), 75 contents.size(), 76 kIsFinal); 77 XML_ParserFree(parser); 78 79 if (res != XML_STATUS_OK) { 80 LOG(ERROR) << "XML parse failure"; 81 return false; 82 } 83 84 CHECK(element_path_.empty()); 85 86 if (!ignore_interfaces.empty()) { 87 // Remove interfaces whose names are in |ignore_interfaces| list. 88 auto condition = [&ignore_interfaces](const Interface& itf) { 89 return std::find(ignore_interfaces.begin(), ignore_interfaces.end(), 90 itf.name) != ignore_interfaces.end(); 91 }; 92 auto p = std::remove_if(interfaces_.begin(), interfaces_.end(), condition); 93 interfaces_.erase(p, interfaces_.end()); 94 } 95 return true; 96 } 97 98 void XmlInterfaceParser::OnOpenElement( 99 const string& element_name, const XmlAttributeMap& attributes) { 100 string prev_element; 101 if (!element_path_.empty()) 102 prev_element = element_path_.back(); 103 element_path_.push_back(element_name); 104 if (element_name == kNodeTag) { 105 CHECK(prev_element.empty() || prev_element == kNodeTag) 106 << "Unexpected tag " << element_name << " inside " << prev_element; 107 // 'name' attribute is optional for <node> element. 108 string name; 109 GetElementAttribute(attributes, element_path_, kNameAttribute, &name); 110 base::TrimWhitespaceASCII(name, base::TRIM_ALL, &name); 111 node_names_.push_back(name); 112 } else if (element_name == kInterfaceTag) { 113 CHECK_EQ(kNodeTag, prev_element) 114 << "Unexpected tag " << element_name << " inside " << prev_element; 115 string interface_name = GetValidatedElementName(attributes, element_path_); 116 Interface itf(interface_name, 117 std::vector<Interface::Method>{}, 118 std::vector<Interface::Signal>{}, 119 std::vector<Interface::Property>{}); 120 itf.path = node_names_.back(); 121 interfaces_.push_back(std::move(itf)); 122 } else if (element_name == kMethodTag) { 123 CHECK_EQ(kInterfaceTag, prev_element) 124 << "Unexpected tag " << element_name << " inside " << prev_element; 125 interfaces_.back().methods.push_back( 126 Interface::Method(GetValidatedElementName(attributes, element_path_))); 127 } else if (element_name == kSignalTag) { 128 CHECK_EQ(kInterfaceTag, prev_element) 129 << "Unexpected tag " << element_name << " inside " << prev_element; 130 interfaces_.back().signals.push_back( 131 Interface::Signal(GetValidatedElementName(attributes, element_path_))); 132 } else if (element_name == kPropertyTag) { 133 CHECK_EQ(kInterfaceTag, prev_element) 134 << "Unexpected tag " << element_name << " inside " << prev_element; 135 interfaces_.back().properties.push_back(ParseProperty(attributes, 136 element_path_)); 137 } else if (element_name == kArgumentTag) { 138 if (prev_element == kMethodTag) { 139 AddMethodArgument(attributes); 140 } else if (prev_element == kSignalTag) { 141 AddSignalArgument(attributes); 142 } else { 143 LOG(FATAL) << "Unexpected tag " << element_name 144 << " inside " << prev_element; 145 } 146 } else if (element_name == kAnnotationTag) { 147 string name = GetValidatedElementAttribute(attributes, element_path_, 148 kNameAttribute); 149 // Value is optional. Default to empty string if omitted. 150 string value; 151 GetElementAttribute(attributes, element_path_, kValueAttribute, &value); 152 if (prev_element == kInterfaceTag) { 153 // Parse interface annotations... 154 } else if (prev_element == kMethodTag) { 155 // Parse method annotations... 156 Interface::Method& method = interfaces_.back().methods.back(); 157 if (name == kMethodConst) { 158 CHECK(value == kTrue || value == kFalse); 159 method.is_const = (value == kTrue); 160 } else if (name == kMethodIncludeDBusMessage) { 161 CHECK(value == kTrue || value == kFalse); 162 method.include_dbus_message = (value == kTrue); 163 } else if (name == kMethodAsync) { 164 // Support GLib.Async annotation as well. 165 method.kind = Interface::Method::Kind::kAsync; 166 } else if (name == kMethodKind) { 167 if (value == kMethodKindSimple) { 168 method.kind = Interface::Method::Kind::kSimple; 169 } else if (value == kMethodKindNormal) { 170 method.kind = Interface::Method::Kind::kNormal; 171 } else if (value == kMethodKindAsync) { 172 method.kind = Interface::Method::Kind::kAsync; 173 } else if (value == kMethodKindRaw) { 174 method.kind = Interface::Method::Kind::kRaw; 175 } else { 176 LOG(FATAL) << "Invalid method kind: " << value; 177 } 178 } 179 } else if (prev_element == kSignalTag) { 180 // Parse signal annotations... 181 } else if (prev_element == kPropertyTag) { 182 // Parse property annotations... 183 } else { 184 LOG(FATAL) << "Unexpected tag " << element_name 185 << " inside " << prev_element; 186 } 187 } else if (element_name == kDocStringTag) { 188 CHECK(!prev_element.empty() && prev_element != kNodeTag) 189 << "Unexpected tag " << element_name << " inside " << prev_element; 190 } 191 } 192 193 void XmlInterfaceParser::OnCharData(const std::string& content) { 194 // Handle the text data only for <tp:docstring> element. 195 if (element_path_.back() != kDocStringTag) 196 return; 197 198 CHECK_GT(element_path_.size(), 1u); 199 string* doc_string_ptr = nullptr; 200 string target_element = element_path_[element_path_.size() - 2]; 201 if (target_element == kInterfaceTag) { 202 doc_string_ptr = &(interfaces_.back().doc_string); 203 } else if (target_element == kMethodTag) { 204 doc_string_ptr = &(interfaces_.back().methods.back().doc_string); 205 } else if (target_element == kSignalTag) { 206 doc_string_ptr = &(interfaces_.back().signals.back().doc_string); 207 } else if (target_element == kPropertyTag) { 208 doc_string_ptr = &(interfaces_.back().properties.back().doc_string); 209 } 210 211 // If <tp:docstring> is attached to elements we don't care about, do nothing. 212 if (doc_string_ptr == nullptr) 213 return; 214 215 (*doc_string_ptr) += content; 216 } 217 218 219 void XmlInterfaceParser::AddMethodArgument(const XmlAttributeMap& attributes) { 220 string argument_direction; 221 vector<string> path = element_path_; 222 path.push_back(kArgumentTag); 223 bool is_direction_paramter_present = GetElementAttribute( 224 attributes, path, kDirectionAttribute, &argument_direction); 225 vector<Interface::Argument>* argument_list = nullptr; 226 if (!is_direction_paramter_present || 227 argument_direction == kArgumentDirectionIn) { 228 argument_list = &interfaces_.back().methods.back().input_arguments; 229 } else if (argument_direction == kArgumentDirectionOut) { 230 argument_list = &interfaces_.back().methods.back().output_arguments; 231 } else { 232 LOG(FATAL) << "Unknown method argument direction " << argument_direction; 233 } 234 argument_list->push_back(ParseArgument(attributes, element_path_)); 235 } 236 237 void XmlInterfaceParser::AddSignalArgument(const XmlAttributeMap& attributes) { 238 interfaces_.back().signals.back().arguments.push_back( 239 ParseArgument(attributes, element_path_)); 240 } 241 242 void XmlInterfaceParser::OnCloseElement(const string& element_name) { 243 VLOG(1) << "Close Element " << element_name; 244 CHECK(!element_path_.empty()); 245 CHECK_EQ(element_path_.back(), element_name); 246 element_path_.pop_back(); 247 if (element_name == kNodeTag) { 248 CHECK(!node_names_.empty()); 249 node_names_.pop_back(); 250 } 251 } 252 253 // static 254 bool XmlInterfaceParser::GetElementAttribute( 255 const XmlAttributeMap& attributes, 256 const vector<string>& element_path, 257 const string& element_key, 258 string* element_value) { 259 if (attributes.find(element_key) == attributes.end()) { 260 return false; 261 } 262 *element_value = attributes.find(element_key)->second; 263 VLOG(1) << "Got " << GetElementPath(element_path) << " element with " 264 << element_key << " = " << *element_value; 265 return true; 266 } 267 268 // static 269 string XmlInterfaceParser::GetValidatedElementAttribute( 270 const XmlAttributeMap& attributes, 271 const vector<string>& element_path, 272 const string& element_key) { 273 string element_value; 274 CHECK(GetElementAttribute(attributes, 275 element_path, 276 element_key, 277 &element_value)) 278 << GetElementPath(element_path) << " does not contain a " << element_key 279 << " attribute"; 280 CHECK(!element_value.empty()) << GetElementPath(element_path) << " " 281 << element_key << " attribute is empty"; 282 return element_value; 283 } 284 285 // static 286 string XmlInterfaceParser::GetValidatedElementName( 287 const XmlAttributeMap& attributes, 288 const vector<string>& element_path) { 289 return GetValidatedElementAttribute(attributes, element_path, kNameAttribute); 290 } 291 292 // static 293 Interface::Argument XmlInterfaceParser::ParseArgument( 294 const XmlAttributeMap& attributes, const vector<string>& element_path) { 295 vector<string> path = element_path; 296 path.push_back(kArgumentTag); 297 string argument_name; 298 // Since the "name" field is optional, use the un-validated variant. 299 GetElementAttribute(attributes, path, kNameAttribute, &argument_name); 300 301 string argument_type = GetValidatedElementAttribute( 302 attributes, path, kTypeAttribute); 303 return Interface::Argument(argument_name, argument_type); 304 } 305 306 // static 307 Interface::Property XmlInterfaceParser::ParseProperty( 308 const XmlAttributeMap& attributes, 309 const std::vector<std::string>& element_path) { 310 vector<string> path = element_path; 311 path.push_back(kPropertyTag); 312 string property_name = GetValidatedElementName(attributes, path); 313 string property_type = GetValidatedElementAttribute(attributes, path, 314 kTypeAttribute); 315 string property_access = GetValidatedElementAttribute(attributes, path, 316 kAccessAttribute); 317 return Interface::Property(property_name, property_type, property_access); 318 } 319 320 // static 321 void XmlInterfaceParser::HandleElementStart(void* user_data, 322 const XML_Char* element, 323 const XML_Char** attr) { 324 XmlAttributeMap attributes; 325 if (attr != nullptr) { 326 for (size_t n = 0; attr[n] != nullptr && attr[n+1] != nullptr; n += 2) { 327 auto key = attr[n]; 328 auto value = attr[n + 1]; 329 attributes.insert(std::make_pair(key, value)); 330 } 331 } 332 auto parser = reinterpret_cast<XmlInterfaceParser*>(user_data); 333 parser->OnOpenElement(element, attributes); 334 } 335 336 // static 337 void XmlInterfaceParser::HandleElementEnd(void* user_data, 338 const XML_Char* element) { 339 auto parser = reinterpret_cast<XmlInterfaceParser*>(user_data); 340 parser->OnCloseElement(element); 341 } 342 343 // static 344 void XmlInterfaceParser::HandleCharData(void* user_data, 345 const char *content, 346 int length) { 347 auto parser = reinterpret_cast<XmlInterfaceParser*>(user_data); 348 parser->OnCharData(string(content, length)); 349 } 350 351 } // namespace chromeos_dbus_bindings 352