Replace tpk manifest parser to XmlParser 04/36404/5
authorYoumin Ha <youmin.ha@samsung.com>
Thu, 5 Mar 2015 08:05:41 +0000 (17:05 +0900)
committerPawel Sikorski <p.sikorski@samsung.com>
Fri, 13 Mar 2015 10:18:31 +0000 (03:18 -0700)
Old manifest parser is replaced with the new general-purpose XmlParser.
With the XmlParser, the element hierarchy is fully preserved.
In addition, all exceptions are removed in this commit.

Change-Id: Id03384dc0b3c661bd876966bfbb3883b631cc68d
Signed-Off-By: Youmin Ha <youmin.ha@samsung.com>
15 files changed:
src/tpk/CMakeLists.txt
src/tpk/exception.h [deleted file]
src/tpk/main.cc
src/tpk/manifest_parser.cc [deleted file]
src/tpk/manifest_parser.h [deleted file]
src/tpk/step/step_parse.cc
src/tpk/step/step_parse.h
src/tpk/task.cc
src/tpk/task.h
src/tpk/xml_nodes.h [deleted file]
src/unit_tests/CMakeLists.txt
src/unit_tests/test_samples/tpk-sample-manifest.xml [new file with mode: 0644]
src/unit_tests/xml_parser_unittest.cc [new file with mode: 0644]
src/xml_parser/xml_parser.cc [new file with mode: 0644]
src/xml_parser/xml_parser.h [new file with mode: 0644]

index c2c4689..da3fa9e 100644 (file)
@@ -9,7 +9,7 @@ SET(SRCS
     task.cc
     step/step_parse.cc
     step/step_symbolic_link.cc
-    manifest_parser.cc
+    ../xml_parser/xml_parser.cc
 )
 ADD_EXECUTABLE(${TARGET_TPK} ${SRCS})
 
diff --git a/src/tpk/exception.h b/src/tpk/exception.h
deleted file mode 100644 (file)
index 34c1d80..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/* Copyright 2015 Samsung Electronics, license APACHE-2.0, see LICENSE file */
-#ifndef TPK_EXCEPTION_H_
-#define TPK_EXCEPTION_H_
-
-#include <execinfo.h>
-#include <string.h>
-#include <unistd.h>
-#include <exception>
-#include <iostream>
-#include "utils/logging.h"
-
-namespace tpk {
-
-class Exception: public std::exception {
-SCOPE_LOG_TAG(UncaughtException)
-
- public:
-  Exception() {
-    this->message_ = nullptr;
-    StoreStackTrace();
-  }
-  explicit Exception(const char *message) {
-    this->message_ = strdup(message);
-    StoreStackTrace();
-  }
-  ~Exception() {
-    if (message_) free(message_);
-  }
-  virtual void StoreStackTrace() {
-    stack_size_ = backtrace(stack_, kMaxStackSize);
-  }
-  virtual void PrintStackTrace() {
-    if (message_) {
-      LOG(ERROR) << message_;
-    }
-    // Exclude top 1 stack entry (storeStackTrace)
-    backtrace_symbols_fd(stack_+1, stack_size_-1, STDERR_FILENO);
-  }
-
-
- protected:
-  char* message_;
-
-
- private:
-  static const size_t kMaxStackSize = 100;
-  size_t stack_size_;
-  void *stack_[kMaxStackSize];
-};
-
-}  // namespace tpk
-#endif  // TPK_EXCEPTION_H_
index 30e935f..7137b43 100644 (file)
@@ -1,19 +1,17 @@
 /* Copyright 2015 Samsung Electronics, license APACHE-2.0, see LICENSE file */
 #include <iostream>
-#include <cerrno>
 #include "tpk/task.h"
-#include "tpk/exception.h"
+#include "utils/logging.h"
 
 
 int main(const int argc, char* argv[]) {
-  try {
-    // Create a task to do
-    tpk::Task t(argc, argv);
-
-    // Do the task
-    t.Run();
-  } catch(tpk::Exception &e) {
-    e.PrintStackTrace();
+  tpk::Task t;
+  if (!t.Init(argc, argv)) {
+    std::cerr << "Task init failure" << std::endl;
+    return -1;
+  }
+  if (!t.Run()) {
+    std::cerr << "Task run failure" << std::endl;
     return -1;
   }
   return 0;
diff --git a/src/tpk/manifest_parser.cc b/src/tpk/manifest_parser.cc
deleted file mode 100644 (file)
index 74d8ae3..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-/* Copyright 2015 Samsung Electronics, license APACHE-2.0, see LICENSE file */
-
-#include "tpk/manifest_parser.h"
-#include <libxml/xmlreader.h>
-#include <iostream>
-#include <vector>
-#include "tpk/xml_nodes.h"
-
-namespace tpk {
-
-
-template <typename T>
-void ManifestParser::CheckAndSetNode(xmlTextReaderPtr reader,
-    const char* name,
-    const T& xmlNode) {
-  T& node = const_cast<T&>(xmlNode);
-  if (xmlStrEqual(reinterpret_cast<const xmlChar*>(name),
-                      xmlTextReaderConstName(reader)) &&
-          xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
-    node.Init(reader);
-  }
-}
-
-
-template <typename T>
-void ManifestParser::CheckAndAppendNode(xmlTextReaderPtr reader,
-    const char* name,
-    const std::vector<T*> &v) {
-  if (xmlStrEqual(reinterpret_cast<const xmlChar*>(name),
-                      xmlTextReaderConstName(reader)) &&
-          xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
-    T *p = new T();
-    p->Init(reader);
-    std::vector<T*>& _v = const_cast<std::vector<T*>&>(v);
-    _v.push_back(p);
-  }
-}
-
-
-void ManifestParser::ProcessNode(xmlTextReaderPtr reader) {
-  // For each node, find required node names and get data
-  CheckAndSetNode(reader, "manifest", manifest);
-  CheckAndSetNode(reader, "ui-application", manifest.ui_application);
-  CheckAndSetNode(reader, "icon", manifest.ui_application.icon);
-  CheckAndSetNode(reader, "label", manifest.ui_application.label);
-  CheckAndSetNode(reader, "privileges", manifest.privileges);
-  CheckAndAppendNode(reader, "privilege", manifest.privileges.v_privilege);
-}
-
-
-void ManifestParser::StreamFile(const char* filePath) {
-  xmlTextReaderPtr reader;
-  int ret;
-
-  // TODO(youmin.ha@samsung.com): add DTD validation
-  reader = xmlReaderForFile(filePath, NULL, 0);
-  if (reader == nullptr) {
-    throw FileOpenFailureException();
-  } else {
-    ret = xmlTextReaderRead(reader);
-    while (ret == 1) {
-      ProcessNode(reader);
-      ret = xmlTextReaderRead(reader);
-    }
-    xmlFreeTextReader(reader);
-    if (ret != 0) {
-      throw ParseFailureException();
-    }
-  }
-}
-
-
-/* constructor
- */
-ManifestParser::ManifestParser(const char* filePath) {
-  LIBXML_TEST_VERSION
-
-  StreamFile(filePath);
-
-  xmlCleanupParser();
-  xmlMemoryDump();
-}
-
-
-/* destructor
- */
-ManifestParser::~ManifestParser() {
-}
-
-
-}  // namespace tpk
diff --git a/src/tpk/manifest_parser.h b/src/tpk/manifest_parser.h
deleted file mode 100644 (file)
index c982340..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/* Copyright 2015 Samsung Electronics, license APACHE-2.0, see LICENSE file */
-#ifndef TPK_MANIFEST_PARSER_H_
-#define TPK_MANIFEST_PARSER_H_
-
-#include <boost/filesystem.hpp>
-#include <vector>
-#include "tpk/xml_nodes.h"
-#include "tpk/exception.h"
-
-
-namespace tpk {
-
-/* Internal exceptions */
-class FileOpenFailureException : public Exception {};
-class ParseFailureException : public Exception {};
-
-
-class ManifestParser {
- public:
-  explicit ManifestParser(const char *manifestFilePath);
-  ~ManifestParser();
-  XmlNodeManifest manifest;
-
-
- private:
-  template <typename T> void CheckAndSetNode(
-      xmlTextReaderPtr reader, const char* name, const T &xmlNode);
-  template <typename T> void CheckAndAppendNode(
-      xmlTextReaderPtr reader, const char* name, const std::vector<T*> &v);
-  void ProcessNode(xmlTextReaderPtr reader);
-  void StreamFile(const char* filePath);
-};
-
-}  // namespace tpk
-#endif  // TPK_MANIFEST_PARSER_H_
index b47cef1..2a85ca1 100644 (file)
@@ -1,21 +1,39 @@
 /* Copyright 2015 Samsung Electronics, license APACHE-2.0, see LICENSE file */
 #include "tpk/step/step_parse.h"
 #include <boost/filesystem.hpp>
+#include <memory>
 #include <string>
 #include <vector>
 #include "common/context_installer.h"
 #include "common/step/step.h"
-#include "tpk/manifest_parser.h"
-#include "tpk/xml_nodes.h"
 #include "utils/logging.h"
+#include "xml_parser/xml_parser.h"
 
 using std::vector;
+using std::string;
+using xml_parser::XmlParser;
+using xml_parser::XmlTree;
+using xml_parser::XmlElement;
+
 
 namespace tpk {
 namespace step {
 
 namespace {
   const char kManifestFileName[] = "tizen-manifest.xml";
+
+  XmlElement* Get1stChild(XmlTree *tree,
+      XmlElement* parent, const string element_name) {
+    vector<XmlElement*> v = tree->Children(parent, element_name);
+    if (!v.size() < 1) {
+      LOG(ERROR) << element_name << " is not found as a child of " <<
+          parent->name();
+      return nullptr;
+    }
+    return v[0];  // Always return only the 1st child
+  }
+
+
 }  // namespace
 
 SCOPE_LOG_TAG(StepParse)
@@ -24,24 +42,24 @@ SCOPE_LOG_TAG(StepParse)
 typedef common_installer::Step::Status Status;
 using boost::filesystem::path;
 
-
-/* Internal exceptions */
-class FileNotFoundException : public std::exception {};
-
-
 /* process()
  * Parse tizen-manifest.xml and get the data from it
  * Store the data into the context_
  */
 Status StepParse::process() {
-  try {
-    boost::filesystem::path mPath  = GetManifestFilePath(
-        context_->unpacked_dir_path());
-    ManifestParser m(mPath.c_str());
-    SetContextByManifestParser(m);
-  } catch (FileNotFoundException &e) {
+  std::unique_ptr<boost::filesystem::path> mPath(
+      GetManifestFilePath(context_->unpacked_dir_path()));
+  if (!mPath) {
     return Status::ERROR;
-  } catch (FileOpenFailureException &e) {
+  }
+
+  XmlParser parser;
+  std::unique_ptr<XmlTree> tree(parser.ParseAndGetNewTree(mPath->c_str()));
+  if (tree == nullptr) {
+    LOG(ERROR) << "Failure on parsing xml";
+    return Status::ERROR;
+  }
+  if (!SetContextByManifestParser(tree.get())) {
     return Status::ERROR;
   }
   return Status::OK;
@@ -50,15 +68,15 @@ Status StepParse::process() {
 
 /* in parse() : Get manifest file path from the package unzipped directory
  */
-boost::filesystem::path StepParse::GetManifestFilePath(
+boost::filesystem::path* StepParse::GetManifestFilePath(
     const boost::filesystem::path& dir) {
-  path mPath(dir);
-  mPath /= kManifestFileName;
+  path* mPath = new path(dir);
+  *mPath /= kManifestFileName;
 
-  LOG(INFO) << "manifest file path: " << mPath;
-  if (!boost::filesystem::exists(mPath)) {
+  LOG(INFO) << "manifest file path: " << mPath->string();
+  if (!boost::filesystem::exists(*mPath)) {
     LOG(ERROR) << kManifestFileName << " not found from the package";
-    throw FileNotFoundException();
+    return nullptr;
   }
   return mPath;  // object copy
 }
@@ -66,27 +84,49 @@ boost::filesystem::path StepParse::GetManifestFilePath(
 
 /* Read manifest xml, and set up context_ object
  */
-void StepParse::SetContextByManifestParser(const ManifestParser &m) {
-  const XmlNodeManifest& manifest = m.manifest;
-  LOG(DEBUG) << "Parse manifest xml values:";
-  LOG(DEBUG) << "xmlns(" << manifest.xmlns << ") api_version(" <<
-      manifest.api_version << ") package(" << manifest.package <<
-      ") version(" << manifest.version << ")";
+bool StepParse::SetContextByManifestParser(XmlTree* tree) {
+  // Get required elements
+  XmlElement* manifest,
+      * ui_application, * label;
+
+  // manifest
+  if (nullptr == (manifest = tree->GetRootElement())) return false;
+
+  LOG(DEBUG) << "Getting manifest xml data";
+  LOG(DEBUG) << "manifest: xmlns='" << manifest->attr("xmlns") <<
+                "' api_version='" << manifest->attr("api_version") <<
+                "' package='" << manifest->attr("package") <<
+                "' versionr='" << manifest->attr("version") << "'";
+
+  // ui_application
+  if (nullptr == (ui_application = Get1stChild(tree,
+          manifest, "ui-application"))) return false;
+  if (nullptr == (label = Get1stChild(tree, ui_application, "label")))
+    return false;
 
   // set context_
-  context_->config_data()->set_application_name(
-      std::string(reinterpret_cast<char*>(manifest.ui_application.label.data)));
-  context_->config_data()->set_required_version(
-      std::string(reinterpret_cast<char*>(manifest.api_version)));
-
-  context_->set_pkgid(std::string(reinterpret_cast<char*>(manifest.package)));
+  context_->config_data()->set_application_name(label->content());
+  context_->config_data()->set_required_version(manifest->attr("api_version"));
+  context_->set_pkgid(manifest->attr("package"));
 
   // set context_->manifest_data()
-  SetPkgInfoManifest(context_->manifest_data(), manifest);
+  return SetPkgInfoManifest(context_->manifest_data(), tree, manifest);
 }
 
-void StepParse::SetPkgInfoManifest(manifest_x* m,
-    const XmlNodeManifest &manifest) {
+bool StepParse::SetPkgInfoManifest(manifest_x* m,
+    XmlTree* tree,
+    XmlElement* manifest) {
+  // Get required elements
+  XmlElement* ui_application, * label, * icon, * description;
+  if (nullptr == (ui_application = Get1stChild(tree,
+          manifest, "ui-application"))) return false;
+  if (nullptr == (label = Get1stChild(tree, ui_application, "label")))
+    return false;
+  if (nullptr == (icon = Get1stChild(tree, ui_application, "icon")))
+    return false;
+  if (nullptr == (description = Get1stChild(tree,
+          ui_application, "description"))) return false;
+
   // Common values
   m->label =  static_cast<label_x*>
     (calloc(1, sizeof(label_x)));
@@ -98,31 +138,30 @@ void StepParse::SetPkgInfoManifest(manifest_x* m,
   m->privileges->privilege = nullptr;
 
   // Basic values
-  m->package = strdup(reinterpret_cast<char*>(manifest.package));
+  m->package = strdup(manifest->attr("package").c_str());
   m->type = strdup("tpk");
-  m->version = strdup(reinterpret_cast<char*>(manifest.version));
-  m->label->name = strdup(
-      reinterpret_cast<char*>(manifest.ui_application.label.name));
-  // TODO(youmin.ha@samsung.com): get name from XML if exists
-  m->description->name = nullptr;
-  m->mainapp_id = strdup(
-      reinterpret_cast<char*>(manifest.ui_application.appid));
+  m->version = strdup(manifest->attr("version").c_str());
+  m->label->name = strdup(label->content().c_str());
+  m->description->name = strdup(description->content().c_str());
+  m->mainapp_id = strdup(ui_application->attr("appid").c_str());
 
   // Privileges
-  vector<XmlNodePrivilege *> vp =
-      const_cast<XmlNodeManifest &>(manifest).privileges.getPrivilegeVector();
-  vector<XmlNodePrivilege *>::iterator it;
-  for (it = vp.begin(); it != vp.end(); it++) {
+  XmlElement* privileges;
+  if (nullptr == (privileges = Get1stChild(tree, manifest, "privileges"))) {
+    return false;
+  }
+  vector<XmlElement*> v_priv = tree->Children(privileges, "privilege");
+  for (auto& privilege : v_priv) {
     privilege_x *p =
         static_cast<privilege_x *>(calloc(1, sizeof(privilege_x)));
     // privilege data text
-    p->text = strdup(reinterpret_cast<char*>((*it)->data));
+    p->text = strdup(privilege->content().c_str());
     LISTADD(m->privileges->privilege, p);
-    LOG(INFO) << "add privilege: " << p->text;
+    LOG(INFO) << "Add a privilege: " << p->text;
   }
 
   // Other app data (null initialization)
-  m->serviceapplication = nullptr;  // ignore service application
+  m->serviceapplication = nullptr;  // NOTE: ignore service application
   m->uiapplication = static_cast<uiapplication_x*>
     (calloc (1, sizeof(uiapplication_x)));
   m->uiapplication->icon = static_cast<icon_x*>
@@ -133,18 +172,15 @@ void StepParse::SetPkgInfoManifest(manifest_x* m,
     (calloc(1, sizeof(description_x)));
   m->uiapplication->appcontrol = nullptr;
 
-  m->uiapplication->appid = strdup(
-      reinterpret_cast<char*>(manifest.ui_application.appid));
-  m->uiapplication->exec = strdup(
-      reinterpret_cast<char*>(manifest.ui_application.exec));
-  m->uiapplication->type = strdup(
-      reinterpret_cast<char*>(manifest.ui_application.type));
-
-  m->uiapplication->label->name = strdup(
-      reinterpret_cast<char*>(manifest.ui_application.label.data));
-  m->uiapplication->icon->name = strdup(
-      reinterpret_cast<char*>(manifest.ui_application.icon.data));
+  m->uiapplication->appid = strdup(ui_application->attr("appid").c_str());
+  m->uiapplication->exec = strdup(ui_application->attr("exec").c_str());
+  m->uiapplication->type = strdup(ui_application->attr("type").c_str());
+
+  m->uiapplication->label->name = strdup(label->content().c_str());
+  m->uiapplication->icon->name = strdup(icon->content().c_str());
   m->uiapplication->next = nullptr;
+
+  return true;
 }
 
 }  // namespace step
index 0bb9d56..84e48cb 100644 (file)
@@ -4,7 +4,7 @@
 
 #include <boost/filesystem.hpp>
 #include "common/step/step.h"
-#include "tpk/manifest_parser.h"
+#include "xml_parser/xml_parser.h"
 
 namespace tpk {
 namespace step {
@@ -19,10 +19,12 @@ class StepParse : public common_installer::Step {
 
 
  private:
-  boost::filesystem::path
-    GetManifestFilePath(const boost::filesystem::path& dir);
-  void SetContextByManifestParser(const ManifestParser &m);
-  void SetPkgInfoManifest(manifest_x* m, const XmlNodeManifest &manifest);
+  boost::filesystem::path* GetManifestFilePath(
+      const boost::filesystem::path& dir);
+  bool SetContextByManifestParser(xml_parser::XmlTree* tree);
+  bool SetPkgInfoManifest(manifest_x* m,
+      xml_parser::XmlTree* tree,
+      xml_parser::XmlElement* manifest);
 };
 
 }  // namespace step
index bedd1de..d02545f 100644 (file)
@@ -18,7 +18,7 @@
 #include "common/step/step_unzip.h"
 #include "tpk/step/step_parse.h"
 #include "tpk/step/step_symbolic_link.h"
-#include "tpk/exception.h"
+#include "utils/logging.h"
 #endif
 
 
@@ -28,20 +28,15 @@ namespace {
   const char kPkgType[] = "tpk";
 }  // namespace
 
-
 namespace tpk {
 
+SCOPE_LOG_TAG(TpkTask)
+
 /* Constructor
  */
-Task::Task(const int argc, char** argv) {
-  pi_ = pkgmgr_installer_new();
-  if (!pi_) {
-    throw Exception("Not enough memory");
-  }
-  if (!!pkgmgr_installer_receive_request(pi_, argc, argv)) {
-    throw Exception("Invalid Argument");
-  }
-  request_ = pkgmgr_installer_get_request_type(pi_);
+Task::Task() :
+  pi_(nullptr),
+  request_(PKGMGR_REQ_INVALID) {
 }
 
 
@@ -50,28 +45,46 @@ Task::Task(const int argc, char** argv) {
 ::tpk::Task::~Task() {
   if (pi_) {
     pkgmgr_installer_free(pi_);
-    pi_ = NULL;
+    pi_ = nullptr;
   }
 }
 
 
-void Task::Run(void) {
+bool Task::Init(int argc, char** argv) {
+  pi_ = pkgmgr_installer_new();
+  if (!pi_) {
+    LOG(ERROR) << "Failed to run pkgmgr_installer_new()";
+    return false;
+  }
+  if (!!pkgmgr_installer_receive_request(pi_, argc, argv)) {
+    LOG(ERROR) << "Invalid argument";
+    pkgmgr_installer_free(pi_);
+    pi_ = nullptr;
+    return false;
+  }
+  request_ = pkgmgr_installer_get_request_type(pi_);
+
+  return true;
+}
+
+
+bool Task::Run() {
+  bool ret = false;
   switch (request_) {
     case PKGMGR_REQ_INSTALL:
-      Install();
+      ret = Install();
       break;
     case PKGMGR_REQ_UNINSTALL:
-      Uninstall();
+      ret = Uninstall();
       break;
     case PKGMGR_REQ_REINSTALL:
-      Reinstall();
+      ret = Reinstall();
       break;
-    default:
-      throw Exception("Unsupported request");
   }
+  return ret;
 }
 
-void Task::Install(void) {
+bool Task::Install() {
   ci::AppInstaller ai(pi_, kPkgType);
 
   ai.AddStep<ci::unzip::StepUnzip>();
@@ -84,10 +97,10 @@ void Task::Install(void) {
   ai.AddStep<ci::generate_xml::StepGenerateXml>();
   ai.AddStep<ci::record::StepRecord>();
 
-  ai.Run();
+  return ai.Run();
 }
 
-void Task::Uninstall(void) {
+bool Task::Uninstall() {
   ci::AppInstaller ai(pi_, kPkgType);
 
   ai.AddStep<ci::parse::StepParse>();
@@ -96,10 +109,11 @@ void Task::Uninstall(void) {
   ai.AddStep<ci::unregister::StepUnregister>();
   ai.AddStep<ci::remove::StepRemove>();
 
-  ai.Run();
+  return ai.Run();
 }
 
-void Task::Reinstall(void) {
+bool Task::Reinstall() {
+  return false;
 }
 
 }  // namespace tpk
index 2730716..8af9e0f 100644 (file)
@@ -12,15 +12,16 @@ namespace tpk {
 
 class Task {
  public:
-  Task(int argc, char** argv);
+  Task();
   ~Task();
-  void Run();
+  bool Init(int argc, char** argv);
+  bool Run();
 
 
  private:
-  void Install();
-  void Uninstall();
-  void Reinstall();
+  bool Install();
+  bool Uninstall();
+  bool Reinstall();
 
   pkgmgr_installer* pi_;
   int request_;
diff --git a/src/tpk/xml_nodes.h b/src/tpk/xml_nodes.h
deleted file mode 100644 (file)
index 77afcc5..0000000
+++ /dev/null
@@ -1,235 +0,0 @@
-/* Copyright 2015 Samsung Electronics, license APACHE-2.0, see LICENSE file */
-#ifndef TPK_XML_NODES_H_
-#define TPK_XML_NODES_H_
-
-#include <libxml/globals.h>
-#include <libxml/xmlstring.h>
-#include <libxml/xmlreader.h>
-#include <vector>
-#include "tpk/exception.h"
-#include "utils/logging.h"
-
-using std::vector;
-
-namespace tpk {
-
-class InvalidNodeTypeException: public tpk::Exception {};
-class InvalidNodeNameException: public tpk::Exception {};
-
-
-class XmlNode {
-  SCOPE_LOG_TAG(XmlNode)
-
- public:
-  int depth;
-  int node_type;
-  xmlChar* name;    // The name of node
-  xmlChar* data;    // Internal text of the node
-
-  XmlNode()
-      : depth(-1),
-        node_type(XML_READER_TYPE_NONE),
-        name(NULL),
-        data(NULL) {
-  }
-
-  virtual ~XmlNode() {
-    Free();
-  }
-
-  virtual void ReadBasicValues(xmlTextReaderPtr reader) {
-    depth = xmlTextReaderDepth(reader);
-    node_type = xmlTextReaderNodeType(reader);
-    data = xmlTextReaderReadString(reader);
-  }
-
-  virtual void Init(xmlTextReaderPtr reader) {
-    name = xmlTextReaderName(reader);
-    if (!xmlStrEqual(name, node_name_)) {
-      LOG(ERROR) << "Invalid node!  actual name=" << name
-          << ", expected name=" << node_name_ << std::endl;
-      xmlFree(name);
-      throw InvalidNodeNameException();
-    }
-    ReadBasicValues(reader);
-  }
-
-  virtual void Free() {
-    xmlFree(name);
-    xmlFree(data);
-  }
-
-  virtual void SetExpectedNodeName(const char* nodeName) {
-    node_name_ = const_cast<xmlChar*>(
-        reinterpret_cast<const xmlChar*>(nodeName));
-  }
-
-
- private:
-  xmlChar* node_name_;
-};
-
-
-class XmlNodeIcon : public XmlNode {
- public:
-  XmlNodeIcon() {
-    XmlNode::SetExpectedNodeName("icon");
-  }
-
-  virtual void Init(xmlTextReaderPtr reader) {
-    XmlNode::Init(reader);
-  }
-};
-
-
-class XmlNodeLabel : public XmlNode {
- public:
-  XmlNodeLabel() {
-    XmlNode::SetExpectedNodeName("label");
-  }
-  virtual void Init(xmlTextReaderPtr reader) {
-    XmlNode::Init(reader);
-  }
-};
-
-class XmlNodeProfile : public XmlNode {
- public:
-  XmlNodeProfile() {
-    XmlNode::SetExpectedNodeName("profile");
-  }
-
-  virtual void Init(xmlTextReaderPtr reader) {
-    XmlNode::Init(reader);
-  }
-};
-
-class XmlNodeUiApplication : public XmlNode {
- public:
-  xmlChar *appid;
-  xmlChar *exec;
-  xmlChar *type;
-  xmlChar *multiple;
-  xmlChar *taskmanage;
-  xmlChar *nodisplay;
-
-  XmlNodeIcon icon;
-  XmlNodeLabel label;
-
-  XmlNodeUiApplication()
-      : appid(NULL),
-        exec(NULL),
-        type(NULL),
-        multiple(NULL),
-        taskmanage(NULL),
-        nodisplay(NULL) {
-    XmlNode::SetExpectedNodeName("ui-application");
-  }
-
-  void Init(xmlTextReaderPtr reader) {
-    XmlNode::Init(reader);
-
-    appid = xmlTextReaderGetAttribute(reader,
-        const_cast<xmlChar*>(reinterpret_cast<const xmlChar*>("appid")));
-    exec = xmlTextReaderGetAttribute(reader,
-        const_cast<xmlChar*>(reinterpret_cast<const xmlChar*>("exec")));
-    type = xmlTextReaderGetAttribute(reader,
-        const_cast<xmlChar*>(reinterpret_cast<const xmlChar*>("type")));
-    multiple = xmlTextReaderGetAttribute(reader,
-        const_cast<xmlChar*>(reinterpret_cast<const xmlChar*>("multiple")));
-    taskmanage = xmlTextReaderGetAttribute(reader,
-        const_cast<xmlChar*>(reinterpret_cast<const xmlChar*>("taskmanage")));
-    nodisplay = xmlTextReaderGetAttribute(reader,
-        const_cast<xmlChar*>(reinterpret_cast<const xmlChar*>("nodisplay")));
-  }
-  virtual ~XmlNodeUiApplication() {
-    xmlFree(appid);
-    xmlFree(exec);
-    xmlFree(type);
-    xmlFree(multiple);
-    xmlFree(taskmanage);
-    xmlFree(nodisplay);
-  }
-};
-
-
-class XmlNodePrivilege : public XmlNode {
- public:
-  XmlNodePrivilege() {
-    XmlNode::SetExpectedNodeName("privilege");
-  }
-  virtual void Init(xmlTextReaderPtr reader) {
-    XmlNode::Init(reader);
-  }
-};
-
-class XmlNodePrivileges: public XmlNode {
- public:
-  vector<XmlNodePrivilege *> v_privilege;
-
-  XmlNodePrivileges() {
-    XmlNode::SetExpectedNodeName("privileges");
-  }
-  ~XmlNodePrivileges() {
-    // clear v_privilege
-    vector<XmlNodePrivilege *>::iterator it;
-    for (auto& ptr : v_privilege) {
-      delete ptr;
-    }
-  }
-  virtual void Init(xmlTextReaderPtr reader) {
-    XmlNode::Init(reader);
-  }
-  virtual void addPrivilege(xmlTextReaderPtr reader) {
-    if (xmlStrEqual(xmlTextReaderConstName(reader),
-          reinterpret_cast<const xmlChar *>("privilege"))) {
-      XmlNodePrivilege *p = new XmlNodePrivilege();
-      p->Init(reader);
-      v_privilege.push_back(p);
-    }
-  }
-  vector<XmlNodePrivilege *> getPrivilegeVector(void) {
-    return v_privilege;
-  }
-};
-
-class XmlNodeManifest : public XmlNode {
- public:
-  xmlChar *xmlns;
-  xmlChar *api_version;
-  xmlChar *package;
-  xmlChar *version;
-  XmlNodeProfile profile;
-  XmlNodeUiApplication ui_application;
-  XmlNodePrivileges privileges;
-
-  XmlNodeManifest()
-      : xmlns(NULL),
-        api_version(NULL),
-        package(NULL),
-        version(NULL) {
-    XmlNode::SetExpectedNodeName("manifest");
-  }
-
-  void Init(xmlTextReaderPtr reader) {
-    XmlNode::Init(reader);
-
-    xmlns = xmlTextReaderGetAttribute(reader,
-        reinterpret_cast<const xmlChar*>("xmlns"));
-    api_version = xmlTextReaderGetAttribute(reader,
-        reinterpret_cast<const xmlChar*>("api-version"));
-    package = xmlTextReaderGetAttribute(reader,
-        reinterpret_cast<const xmlChar*>("package"));
-    version = xmlTextReaderGetAttribute(reader,
-        reinterpret_cast<const xmlChar*>("version"));
-  }
-
-  ~XmlNodeManifest() {
-    xmlFree(xmlns);
-    xmlFree(api_version);
-    xmlFree(package);
-    xmlFree(version);
-  }
-};
-
-}  // namespace tpk
-#endif  // TPK_XML_NODES_H_
index 7ea624a..608cc5a 100644 (file)
@@ -6,8 +6,11 @@ SET(TESTS
   manifest_handler_unittest
   manifest_util_unittest
   widget_manifest_parser_unittest
+  xml_parser_unittest
 )
 
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../)
+
 # Executables
 ADD_EXECUTABLE(values_unittest
                utils_values_unittest.cc)
@@ -19,6 +22,8 @@ ADD_EXECUTABLE(manifest_util_unittest
                widget_manifest_parser_manifest_util_unittest.cc)
 ADD_EXECUTABLE(widget_manifest_parser_unittest
                widget_manifest_parser_unittest.cc)
+ADD_EXECUTABLE(xml_parser_unittest
+               xml_parser_unittest.cc ../xml_parser/xml_parser.cc)
 
 INSTALL(DIRECTORY test_samples/ DESTINATION ${SHAREDIR}/app-installers-ut/test_samples)
 
@@ -41,6 +46,7 @@ target_link_libraries(manifest_unittest PUBLIC ${TARGET_LIBNAME_WIDGET_MANIFEST_
 target_link_libraries(manifest_handler_unittest PUBLIC ${TARGET_LIBNAME_WIDGET_MANIFEST_PARSER} ${GTEST_MAIN_LIBRARIES})
 target_link_libraries(manifest_util_unittest PUBLIC ${TARGET_LIBNAME_WIDGET_MANIFEST_PARSER} ${GTEST_MAIN_LIBRARIES})
 target_link_libraries(widget_manifest_parser_unittest PUBLIC ${TARGET_LIBNAME_WIDGET_MANIFEST_PARSER} ${GTEST_MAIN_LIBRARIES})
+target_link_libraries(xml_parser_unittest PUBLIC ${TARGET_LIBNAME_XML_PARSER} ${GTEST_MAIN_LIBRARIES})
 
 FOREACH(test ${TESTS})
 INSTALL(TARGETS ${test} DESTINATION ${BINDIR}/app-installers-ut)
diff --git a/src/unit_tests/test_samples/tpk-sample-manifest.xml b/src/unit_tests/test_samples/tpk-sample-manifest.xml
new file mode 100644 (file)
index 0000000..c661101
--- /dev/null
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<manifest xmlns="http://tizen.org/ns/packages" api-version="2.3" package="org.tizen.testapp" version="1.0.0">
+    <author email="tester@samsung.com" href="www.tizen.org">tester</author>
+    <description>This is default description</description>
+    <description xml:lang="en-us">This is test description</description>
+    <ui-application appid="org.tizen.testapp" exec="testapp" multiple="false" nodisplay="false" taskmanage="true" type="capp">
+        <label>testapp</label>
+        <label xml:lang="en-us">Test</label>
+        <icon>testapp.png</icon>
+        <app-control>
+            <mime name="EditMime"/>
+            <operation name="http://tizen.org/appcontrol/operation/edit"/>
+            <uri name="EditUri"/>
+        </app-control>
+        <app-control>
+            <operation name="http://tizen.org/appcontrol/operation/view"/>
+            <uri name="ViewUri"/>
+            <mime name="ViewMime"/>
+        </app-control>
+        <metadata key="metakey1" value="metaval1"/>
+        <metadata key="metakey2" value="metaval2"/>
+        <datacontrol access="ReadOnly" providerid="http://testapp.com/datacontrol/provider/testapp" type="Sql"/>
+        <datacontrol access="ReadOnly" providerid="http://testapp.com/datacontrol/provider/testapp" type="Map"/>
+    </ui-application>
+    <account>
+        <account-provider appid="org.tizen.testapp" multiple-accounts-support="false" providerid="com.samsung">
+            <icon section="account">testapp.png</icon>
+            <icon section="account-small">testapp.png</icon>
+            <label xml:lang="en-gb">account icon</label>
+            <label>Samsung</label>
+            <capability>http://tizen.org/account/capability/calendar</capability>
+            <capability>http://tizen.org/account/capability/photo</capability>
+        </account-provider>
+    </account>
+    <privileges>
+        <privilege>http://tizen.org/privilege/appmanager.launch</privilege>
+        <privilege>http://tizen.org/privilege/packagemanager.info</privilege>
+    </privileges>
+    <feature name="http://tizen.org/feature/camera.front.flash">true</feature>
+    <feature name="http://tizen.org/feature/camera">true</feature>
+    <feature name="http://tizen.org/feature/camera.back.flash">true</feature>
+    <feature name="http://tizen.org/feature/camera.front">true</feature>
+</manifest>
diff --git a/src/unit_tests/xml_parser_unittest.cc b/src/unit_tests/xml_parser_unittest.cc
new file mode 100644 (file)
index 0000000..e9dc18e
--- /dev/null
@@ -0,0 +1,66 @@
+// Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+// Use of this source code is governed by an apache 2.0 license that can be
+// found in the LICENSE file.
+
+#include <gtest/gtest.h>
+#include <boost/filesystem/path.hpp>
+#include <iostream>
+#include <memory>
+#include "xml_parser/xml_parser.h"
+
+namespace {
+
+using std::cout;
+using std::cerr;
+using std::endl;
+using std::unique_ptr;
+using xml_parser::XmlElement;
+using xml_parser::XmlTree;
+using xml_parser::XmlParser;
+
+namespace bf = boost::filesystem;
+
+class TestXmlParser : public testing::Test {
+ protected:
+  virtual void SetUp() {
+  }
+  virtual void TearDown() {
+  }
+};
+
+// Tests manifest parser with proper manifest
+TEST_F(TestXmlParser, ReadManifestXml) {
+  const char* filename = "/usr/share/app-installers-ut/test_samples/tpk-sample-manifest.xml";
+  XmlParser p;
+
+  unique_ptr<XmlTree> t(p.ParseAndGetNewTree(filename));
+  ASSERT_TRUE(t != nullptr);
+
+  XmlElement* root = t->GetRootElement();
+  ASSERT_TRUE(root->name() == "manifest");
+  ASSERT_TRUE(root->attr("api-version") == "2.3");
+
+  XmlElement* ui_application = t->Children(root, "ui-application")[0];
+  ASSERT_TRUE(ui_application->attr("appid") == "org.tizen.testapp");
+
+  XmlElement* app_control0 = t->Children(ui_application, "app-control")[0];
+  XmlElement* operation = t->Children(app_control0, "operation")[0];
+  ASSERT_TRUE(operation->attr("name") ==
+      "http://tizen.org/appcontrol/operation/edit");
+
+  XmlElement* privileges = t->Children(root, "privileges")[0];
+  XmlElement* privilege1 = t->Children(privileges, "privilege")[1];
+  ASSERT_TRUE(privilege1->content() ==
+      "http://tizen.org/privilege/packagemanager.info");
+
+  XmlElement* feature3 = t->Children(root, "feature")[3];
+  ASSERT_TRUE(feature3->attr("name") ==
+      "http://tizen.org/feature/camera.front");
+  ASSERT_TRUE(feature3->content() == "true");
+
+  // null_string equality test(should be true)
+  ASSERT_TRUE(feature3->attr("nonexist_attr") == XmlElement::null_string());
+
+}
+
+}  // namespace
diff --git a/src/xml_parser/xml_parser.cc b/src/xml_parser/xml_parser.cc
new file mode 100644 (file)
index 0000000..24fe3e9
--- /dev/null
@@ -0,0 +1,169 @@
+/* Copyright 2015 Samsung Electronics, license APACHE-2.0, see LICENSE file */
+#include "xml_parser/xml_parser.h"
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/xmlstring.h>
+#include <iostream>
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+using std::map;
+using std::string;
+using std::vector;
+
+namespace xml_parser {
+
+namespace {  // static namespace
+
+string xmlChar2string(const xmlChar *str) {
+  return string(const_cast<char*>(reinterpret_cast<const char*>(str)));
+}
+
+}  // namespace
+
+
+// class XmlElement
+XmlElement::XmlElement(xmlNode *node) {
+  name_ = xmlChar2string(node->name);
+  xmlChar* content = xmlNodeGetContent(node);  // NOTE: Needs to be freed
+  content_ = xmlChar2string(content);
+  xmlFree(content);
+  SetAttrMap(node);
+  node_ = node;
+}
+
+XmlElement::~XmlElement() {
+  attr_map_.clear();
+}
+
+const string& XmlElement::attr(const string& attrName) {
+  map<string, string>::iterator it = attr_map_.find(attrName);
+  if (it != attr_map_.end()) {
+    return it->second;
+  }
+  return XmlElement::null_string();
+}
+
+void XmlElement::SetAttrMap(xmlNode* node) {
+  xmlAttr* attr;
+  const xmlChar* name;
+  xmlChar* value;
+
+  for (attr=node->properties; attr != nullptr; attr=attr->next) {
+    name = attr->name;
+    value = xmlGetProp(node, name);  // NOTE: Needs to be freed
+
+    attr_map_.insert(map<string, string>::value_type(
+          xmlChar2string(name), xmlChar2string(value)));
+
+    xmlFree(value);
+  }
+}
+
+const string& XmlElement::null_string() {
+  // NOTE: check-coding-rule says that const/static string is to be changed to
+  // const char[], but here we need a string object reference, instead of
+  // C-style const char.
+  static const string nullstr = string("");
+  return nullstr;
+}
+
+
+// class XmlTree
+XmlTree::XmlTree(xmlDoc* doc) : doc_(doc), root_(nullptr) {
+}
+
+XmlTree::~XmlTree() {
+  xmlFreeDoc(doc_);
+
+  // Clear element_map_
+  for (auto& it : elements_map_) {
+    delete it.second;
+  }
+  elements_map_.clear();
+}
+
+void XmlTree::StoreElement(xmlNode* node, XmlElement *el) {
+  elements_map_.insert(std::pair<xmlNode*, XmlElement*>(node, el));
+}
+
+XmlElement* XmlTree::FindElement(xmlNode* node) {
+  map<xmlNode*, XmlElement*>::iterator it = elements_map_.find(node);
+  if (it != elements_map_.end()) {
+    return it->second;
+  }
+  return nullptr;
+}
+
+XmlElement* XmlTree::GetRootElement() {
+  if (root_) return root_;
+
+  xmlNode* root_node = xmlDocGetRootElement(doc_);
+  if (root_node == nullptr) {
+    return nullptr;
+  }
+
+  // Get information of the node
+  XmlElement* root = new XmlElement(root_node);
+  StoreElement(root_node, root);
+  root_ = root;
+
+  return root_;
+}
+
+vector<XmlElement*> XmlTree::Children(
+    XmlElement* parent,
+    const std::string& name) {
+  xmlNode* child;
+  XmlElement *el;
+  vector<XmlElement*> children;
+
+  for (child = xmlFirstElementChild(parent->node());
+      nullptr != child;
+      child = xmlNextElementSibling(child)) {
+    if (name == xmlChar2string(child->name)) {
+      // Found a matched node
+      // Search map
+      el = FindElement(child);
+      if (!el) {  // Not in elements_map_
+        el = new XmlElement(child);
+        StoreElement(child, el);
+      }
+      // Add to children
+      children.push_back(el);
+    }
+  }
+  return children;
+}
+
+
+
+// class XmlParser
+XmlParser::XmlParser() {
+}
+
+XmlParser::~XmlParser() {
+  // NOTE: These libxml2 cleanup funcs must be called after all libxml2 funcs
+  // are compelte.
+
+  // xmlCleanupParser();
+  // xmlMemoryDump();
+}
+
+XmlTree* XmlParser::ParseAndGetNewTree(const char* xmlFilePath) {
+  LIBXML_TEST_VERSION
+
+  XmlTree* t = nullptr;
+  xmlDocPtr doc = xmlReadFile(xmlFilePath, NULL, 0);
+  if (doc) {
+    t = new XmlTree(doc);
+  } else {
+    // TODO(youmin.ha@samsung.com): Error log
+  }
+  return t;
+  // NOTE: doc will be freed by XmlTree.
+}
+
+}  // namespace xml_parser
diff --git a/src/xml_parser/xml_parser.h b/src/xml_parser/xml_parser.h
new file mode 100644 (file)
index 0000000..a009632
--- /dev/null
@@ -0,0 +1,63 @@
+/* Copyright 2015 Samsung Electronics, license APACHE-2.0, see LICENSE file */
+#ifndef XML_PARSER_XML_PARSER_H_
+#define XML_PARSER_XML_PARSER_H_
+
+#include <libxml/tree.h>
+#include <map>
+#include <string>
+#include <vector>
+
+namespace xml_parser {
+
+class XmlElement {
+ public:
+  static const std::string& null_string();
+
+  explicit XmlElement(xmlNode *node);
+  ~XmlElement();
+  const std::string& name() { return name_; }
+  const std::string& content() { return content_; }
+  const std::string& attr(const std::string& attrName);
+  xmlNode* node() { return node_; }
+
+ private:
+  void SetAttrMap(xmlNode* node);
+
+  std::string name_;
+  std::string content_;
+  std::map<std::string, std::string> attr_map_;
+  xmlNode* node_;
+};
+
+class XmlTree {
+ public:
+  explicit XmlTree(xmlDoc* doc);
+  ~XmlTree();
+
+  // Note: Each XmlElement* is available when the XmlTree object is valid.
+  XmlElement* GetRootElement();
+  std::vector<XmlElement*> Children(XmlElement* parent,
+      const std::string& name = "");
+
+ private:
+  void StoreElement(xmlNode* node, XmlElement *el);
+  XmlElement* FindElement(xmlNode* node);
+
+  std::map<xmlNode*, XmlElement*> elements_map_;
+  XmlElement* root_;
+  xmlDoc* doc_;
+};
+
+
+class XmlParser {
+ public:
+  XmlParser();
+  ~XmlParser();
+  XmlTree* ParseAndGetNewTree(const char* xmlFilePath = "");
+
+ private:
+};
+
+}  // namespace xml_parser
+
+#endif  // XML_PARSER_XML_PARSER_H_