From c50e3eb16c4fafb59a3b2eb9c6ad544f88fe7712 Mon Sep 17 00:00:00 2001 From: Hosang Kim Date: Mon, 7 Mar 2022 16:27:04 +0900 Subject: [PATCH] AurumXML: introduce a new class for parsing XML. Change-Id: Ifd1431428dc900f363735ac56a70e18d71d9df5e --- libaurum/inc/AurumXML.h | 112 ++++++++++++++++++++++++ libaurum/meson.build | 1 + libaurum/src/AurumXML.cc | 183 +++++++++++++++++++++++++++++++++++++++ libaurum/src/meson.build | 1 + 4 files changed, 297 insertions(+) create mode 100644 libaurum/inc/AurumXML.h create mode 100644 libaurum/src/AurumXML.cc diff --git a/libaurum/inc/AurumXML.h b/libaurum/inc/AurumXML.h new file mode 100644 index 0000000..a645b2c --- /dev/null +++ b/libaurum/inc/AurumXML.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef _AURUM_XML_H_ +#define _AURUM_XML_H_ + +#include + +#include "Accessible.h" +#include "config.h" +#include "pugixml.hpp" + +using namespace pugi; + +namespace Aurum { + +/** + * @class AurumXML + * + * @ingroup aurum + * + * @brief AurumXML is a representation of a XML document. + * + */ + +class AurumXML { +public: + /** + * @brief Construct a new AurumXML object with root node. + * + * @since_tizen 7.0 + */ + AurumXML(std::shared_ptr root); + + /** + * @brief Destroy the AurumXML object + * + * @since_tizen 7.0 + */ + ~AurumXML(); + + /** + * @brief Creates a XML tree. + * + * @return true if XML tree is created succesfully, false otherwise + * + * @since_tizen 7.0 + */ + bool createXMLtree(); + + /** + * @brief Gets a XPath from specific id. + * + * @param id + * + * @return XPath + * + * @since_tizen 7.0 + */ + std::string getXPath(std::string id); + +private: + /** + * @internal + * + * @brief Traverse application tree for creating XML tree. + * + * @param element xml_node + * + * @param node AccessibleNode + * + * @since_tizen 7.0 + */ + void traverse(xml_node element, std::shared_ptr node); + + /** + * @internal + * + * @brief Get the Optimal XPath string + * + * @param doc xml_document + * + * @param node xml_node + * + * @return XPath + * + * @since_tizen 7.0 + */ + std::string getOptimalXPath(xml_document *doc, xml_node node); + +private: + xml_document *mDoc; + std::shared_ptr mRoot; + std::unordered_map mXPathMap; +}; +} // namespace Aurum + +#endif diff --git a/libaurum/meson.build b/libaurum/meson.build index 115a3d3..5440ee7 100644 --- a/libaurum/meson.build +++ b/libaurum/meson.build @@ -14,6 +14,7 @@ libaurum_install_inc = [ './inc/UiScrollable.h', './inc/A11yEvent.h', './inc/Comparer.h', + './inc/AurumXML.h', './inc/Accessibility/AccessibleNode.h', './inc/Accessibility/AccessibleWatcher.h', './inc/Accessibility/IEventSource.h', diff --git a/libaurum/src/AurumXML.cc b/libaurum/src/AurumXML.cc new file mode 100644 index 0000000..4fac7d2 --- /dev/null +++ b/libaurum/src/AurumXML.cc @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "Aurum.h" + +using namespace Aurum; + +AurumXML::AurumXML(std::shared_ptr root) : mRoot(root) +{ + mDoc = new xml_document(); + if (mRoot) this->createXMLtree(); +} + +AurumXML::~AurumXML() +{ + delete mDoc; +} + +void AurumXML::traverse(xml_node element, std::shared_ptr node) +{ + if (!node) return; + + node->refresh(false); + + std::string name; + if (node->getType().empty()) + name = node->getRole(); + else + name = node->getType(); + + // Remove white spaces. + name.erase(remove(name.begin(), name.end(), ' '), name.end()); + + element.set_name(name.c_str()); + + element.append_attribute("name") = node->getText().c_str(); + element.append_attribute("type") = node->getType().c_str(); + element.append_attribute("id") = node->getId().c_str(); + element.append_attribute("automationid") = node->getAutomationId().c_str(); + element.append_attribute("package") = node->getPkg().c_str(); + + const Rect &size = node->getScreenBoundingBox(); + element.append_attribute("x") = size.mTopLeft.x; + element.append_attribute("y") = size.mTopLeft.y; + element.append_attribute("width") = size.width(); + element.append_attribute("height") = size.height(); + + element.append_attribute("checked") = node->isChecked(); + element.append_attribute("checkable") = node->isCheckable(); + element.append_attribute("clickable") = node->isClickable(); + element.append_attribute("enabled") = node->isEnabled(); + element.append_attribute("focused") = node->isFocused(); + element.append_attribute("focusable") = node->isFocusable(); + element.append_attribute("scrollable") = node->isScrollable(); + element.append_attribute("selected") = node->isSelected(); + element.append_attribute("showing") = node->isShowing(); + element.append_attribute("active") = node->isActive(); + element.append_attribute("visible") = node->isVisible(); + element.append_attribute("selectable") = node->isSelectable(); + + int childCnt = node->getChildCount(); + for (int i = 0; i < childCnt; i++) { + std::shared_ptr childNode = node->getChildAt(i); + if (childNode == nullptr) continue; + + xml_node childElement = element.append_child(""); + traverse(childElement, childNode); + } +} + +bool AurumXML::createXMLtree() +{ + if (!mDoc || !mRoot) return false; + + mDoc->remove_children(); + + xml_node element = mDoc->append_child(""); + + traverse(element, mRoot); + + return true; +} + +std::string AurumXML::getOptimalXPath(xml_document *doc, xml_node node) +{ + std::string xpath; + + int count = 0; + int index = 0; + + const char *automationId = node.attribute("automationid").value(); + if (strlen(automationId) > 0) { + xpath += "//"; + xpath += node.name(); + xpath += "[@automationid=\""; + xpath += automationId; + xpath += "\"]"; + + try { + auto nodes = doc->select_nodes(xpath.c_str()); + if (nodes.size() > 1) { + for (xpath_node_set::const_iterator it = nodes.begin(); + it != nodes.end(); ++it) { + xpath_node no = *it; + count++; + if (no.node() == node) { + index = count; + break; + } + } + xpath.append(1, '[') + .append(std::to_string(index)) + .append(1, ']'); + } + } catch (const xpath_exception &e) { + LOGI("getOptimalXPath Error: %s", e.what()); + } + + return xpath; + } + + xml_node parent = node.parent(); + if (parent) { + xml_node child = parent.first_child(); + for (xml_node el = child; el; el = el.next_sibling()) { + if (el.name() && node.name() && !strcmp(el.name(), node.name())) { + count++; + if (el == node) index = count; + } + } + } + + xpath += "/"; + xpath += node.name(); + + if (count > 1) + xpath.append(1, '[').append(std::to_string(index)).append(1, ']'); + + if (node == doc->first_child()) return xpath; + + return getOptimalXPath(doc, parent).append(xpath); +} + +std::string AurumXML::getXPath(std::string id) +{ + if (mXPathMap.count(id) > 0) return mXPathMap[id]; + + std::string query; + query += "//*[@id=\""; + query += id; + query += "\"]"; + + try { + auto node = mDoc->select_node(query.c_str()).node(); + + if (!node) createXMLtree(); + + if (node) { + std::string xpath = getOptimalXPath(mDoc, node); + mXPathMap[id] = xpath; + return xpath; + } + + } catch (const xpath_exception &e) { + LOGI("getXPath Error: %s", e.what()); + } + + return "NotSupported"; +} \ No newline at end of file diff --git a/libaurum/src/meson.build b/libaurum/src/meson.build index 0a651e5..11af3ea 100644 --- a/libaurum/src/meson.build +++ b/libaurum/src/meson.build @@ -9,6 +9,7 @@ libaurum_src += [ files('Waiter.cc'), files('PartialMatch.cc'), files('A11yEvent.cc'), + files('AurumXML.cc'), ] subdir('Accessibility') -- 2.34.1