[libxml++] Node: Add functions eval_to_[boolean|number|string]().



commit 478a5bd59ca4e932cb3d4d43853a350b0fa76692
Author: Kjell Ahlstedt <kjell ahlstedt bredband net>
Date:   Fri Apr 20 13:27:50 2012 +0200

    Node: Add functions eval_to_[boolean|number|string]().
    
    * examples/dom_xpath/example.xml: Add an element with numeric value.
    * examples/dom_xpath/main.cc: Add calls to the new functions.
    * libxml++/nodes/node.[h|cc]:
    Add the functions eval_to_[boolean|number|string](). Bug #316244.

 ChangeLog                      |    9 +++
 examples/dom_xpath/example.xml |    7 +-
 examples/dom_xpath/main.cc     |   86 +++++++++++++++++++++++++---
 libxml++/nodes/node.cc         |  120 ++++++++++++++++++++++++++++++++++++++++
 libxml++/nodes/node.h          |  109 ++++++++++++++++++++++++++++++++++--
 5 files changed, 311 insertions(+), 20 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 694af7f..4279a7d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2012-04-20  Kjell Ahlstedt  <kjell ahlstedt bredband net>
+ 
+	Node: Add functions eval_to_[boolean|number|string]().
+
+	* examples/dom_xpath/example.xml: Add an element with numeric value.
+	* examples/dom_xpath/main.cc: Add calls to the new functions.
+	* libxml++/nodes/node.[h|cc]:
+	Add the functions eval_to_[boolean|number|string](). Bug #316244.
+
 2012-04-19  Kjell Ahlstedt  <kjell ahlstedt bredband net>
 
 	Node: Make the previous fix thread-safe.
diff --git a/examples/dom_xpath/example.xml b/examples/dom_xpath/example.xml
index 7e10580..6dfa280 100644
--- a/examples/dom_xpath/example.xml
+++ b/examples/dom_xpath/example.xml
@@ -11,8 +11,9 @@
     <title>Another section</title>
     <section id="section3">
       <title>A sub-section</title>
-      <para>This paragraph refers to <xref linkend="section1"/>,
-        <xref linkend="section2"/>, and <xref linkend="section4"/>.</para>
+      <para>This paragraph refers to <literal>3</literal> sections:
+        <xref linkend="section1"/>, <xref linkend="section2"/>, and
+        <xref linkend="section4"/>.</para>
     </section>
   </section>
-</article>
\ No newline at end of file
+</article>
diff --git a/examples/dom_xpath/main.cc b/examples/dom_xpath/main.cc
index f8722e0..e713a2b 100644
--- a/examples/dom_xpath/main.cc
+++ b/examples/dom_xpath/main.cc
@@ -24,23 +24,78 @@
 #endif
 
 #include <libxml++/libxml++.h>
-
+#include <stdlib.h>
 #include <iostream>
 
+Glib::ustring result_type_to_ustring(xmlpp::XPathResultType result_type)
+{
+  switch (result_type)
+  {
+    case xmlpp::XPATH_RESULT_NODESET: return "nodeset";
+    case xmlpp::XPATH_RESULT_BOOLEAN: return "boolean";
+    case xmlpp::XPATH_RESULT_NUMBER:  return "number";
+    case xmlpp::XPATH_RESULT_STRING:  return "string";
+
+    case xmlpp::XPATH_RESULT_UNDEFINED:
+    default:
+      return "undefined";
+  }
+}
 
 void xpath_test(const xmlpp::Node* node, const Glib::ustring& xpath)
 {
   std::cout << std::endl; //Separate tests by an empty line.
   std::cout << "searching with xpath '" << xpath << "' in root node: " << std::endl;
 
-  xmlpp::NodeSet set = node->find(xpath);
-  
-  std::cout << set.size() << " nodes have been found:" << std::endl;
+  try
+  {
+    xmlpp::NodeSet set = node->find(xpath);
+
+    std::cout << set.size() << " nodes have been found:" << std::endl;
+
+    //Print the structural paths and the values:
+    for(xmlpp::NodeSet::iterator i = set.begin(); i != set.end(); ++i)
+    {
+      std::cout << " " << (*i)->get_path();
+
+      xmlpp::Attribute* attribute = dynamic_cast<xmlpp::Attribute*>(*i);
+      if (attribute)
+        std::cout << ", value=\"" << attribute->get_value() << "\"";
 
-  //Print the structural paths:
-  for(xmlpp::NodeSet::iterator i = set.begin(); i != set.end(); ++i)
+      xmlpp::ContentNode* content_node = dynamic_cast<xmlpp::ContentNode*>(*i);
+      if (content_node)
+        std::cout << ", content=\"" << content_node->get_content() << "\"";
+
+      xmlpp::EntityReference* entity_reference = dynamic_cast<xmlpp::EntityReference*>(*i);
+      if (entity_reference)
+        std::cout << ", text=\"" << entity_reference->get_original_text() << "\"";
+
+      xmlpp::Element* element = dynamic_cast<xmlpp::Element*>(*i);
+      if (element)
+      {
+        xmlpp::TextNode* text_node = element->get_child_text();
+        if (text_node)
+          std::cout << ", child_text=\"" << text_node->get_content() << "\"";
+      }
+      std::cout << std::endl;
+    }
+  }
+  catch (const xmlpp::exception& ex)
+  {
+    std::cout << "Exception caught from find: " << ex.what() << std::endl;
+  }
+
+  try
   {
-    std::cout << " " << (*i)->get_path() << std::endl;
+    xmlpp::XPathResultType result_type;
+    std::cout << "Boolean=" << (node->eval_to_boolean(xpath) ? "true" : "false")
+              << ", Number=" << node->eval_to_number(xpath, &result_type)
+              << ", String=\"" << node->eval_to_string(xpath) << "\"";
+    std::cout << ", Result_type=" << result_type_to_ustring(result_type) << std::endl;
+  }
+  catch (const xmlpp::exception& ex)
+  {
+    std::cout << "Exception caught from eval: " << ex.what() << std::endl;
   }
 }
 
@@ -51,7 +106,7 @@ int main(int argc, char* argv[])
   std::locale::global(std::locale(""));
 
   std::string filepath;
-  if(argc > 1 )
+  if (argc > 1)
     filepath = argv[1]; //Allow the user to specify a different XML file to parse.
   else
     filepath = "example.xml";
@@ -71,6 +126,17 @@ int main(int argc, char* argv[])
         // Find the title node (if there is one):
         xpath_test(root, "title");
 
+        // Find all literal text, in any paragraph:
+        xpath_test(root, "//para/literal");
+
+        // Evaluate some XPath expressions with result types other than nodeset:
+        xpath_test(root, "boolean(//para/literal)");
+        xpath_test(root, "number(//para/literal)+2");
+        xpath_test(root, "concat(string(title),\" !\")");
+
+        // Don't find anything:
+        xpath_test(root, "/wont_find");
+
         std::cout << std::endl;
 
         // And finally test whether intra-document links are well-formed.
@@ -82,6 +148,7 @@ int main(int argc, char* argv[])
         std::cout << "searching for unresolved internal references "
                   << "(see docbook manual):" << std::endl;
 
+        xpath_test(root, "//@id");
         xpath_test(root, "//xref/@linkend");
       }
     }
@@ -91,6 +158,5 @@ int main(int argc, char* argv[])
     std::cout << "Exception caught: " << ex.what() << std::endl;
   }
 
-  return 0;
+  return EXIT_SUCCESS;
 }
-
diff --git a/libxml++/nodes/node.cc b/libxml++/nodes/node.cc
index e34a0be..8f2344a 100644
--- a/libxml++/nodes/node.cc
+++ b/libxml++/nodes/node.cc
@@ -22,6 +22,93 @@
 
 #include <iostream>
 
+namespace // anonymous
+{
+// Common part of xmlpp::Node::eval_to_[boolean|number|string]
+xmlXPathObject* eval_common(const Glib::ustring& xpath,
+  const xmlpp::Node::PrefixNsMap* namespaces,
+  xmlpp::XPathResultType* result_type, xmlNode* node)
+{
+  xmlXPathContext* ctxt = xmlXPathNewContext(node->doc);
+  ctxt->node = node;
+
+  if (namespaces)
+  {
+    for (xmlpp::Node::PrefixNsMap::const_iterator it = namespaces->begin();
+         it != namespaces->end(); ++it)
+      xmlXPathRegisterNs(ctxt,
+        reinterpret_cast<const xmlChar*>(it->first.c_str()),
+        reinterpret_cast<const xmlChar*>(it->second.c_str()));
+  }
+
+  xmlXPathObject* xpath_value = xmlXPathEvalExpression(
+    reinterpret_cast<const xmlChar*>(xpath.c_str()), ctxt);
+
+  xmlXPathFreeContext(ctxt);
+
+  if (!xpath_value)
+  {
+    if (result_type)
+      *result_type = xmlpp::XPATH_RESULT_UNDEFINED;
+
+    throw xmlpp::exception("Invalid XPath: " + xpath);
+  }
+
+  if (result_type)
+  {
+    if (xpath_value->type == XPATH_NODESET ||
+        xpath_value->type == XPATH_BOOLEAN ||
+        xpath_value->type == XPATH_NUMBER ||
+        xpath_value->type == XPATH_STRING)
+      *result_type = static_cast<xmlpp::XPathResultType>(xpath_value->type);
+    else
+      *result_type = xmlpp::XPATH_RESULT_UNDEFINED;
+  }
+
+  return xpath_value;
+}
+
+// Common part of all overloaded xmlpp::Node::eval_to_boolean() methods.
+bool eval_common_to_boolean(const Glib::ustring& xpath,
+  const xmlpp::Node::PrefixNsMap* namespaces,
+  xmlpp::XPathResultType* result_type, xmlNode* node)
+{
+  xmlXPathObject* xpath_value = eval_common(xpath, namespaces, result_type, node);
+  const int result = xmlXPathCastToBoolean(xpath_value);
+  xmlXPathFreeObject(xpath_value);
+  return static_cast<bool>(result);
+}
+
+// Common part of all overloaded xmlpp::Node::eval_to_number() methods.
+double eval_common_to_number(const Glib::ustring& xpath,
+  const xmlpp::Node::PrefixNsMap* namespaces,
+  xmlpp::XPathResultType* result_type, xmlNode* node)
+{
+  xmlXPathObject* xpath_value = eval_common(xpath, namespaces, result_type, node);
+  const double result = xmlXPathCastToNumber(xpath_value);
+  xmlXPathFreeObject(xpath_value);
+  return result;
+}
+
+// Common part of all overloaded xmlpp::Node::eval_to_string() methods.
+Glib::ustring eval_common_to_string(const Glib::ustring& xpath,
+  const xmlpp::Node::PrefixNsMap* namespaces,
+  xmlpp::XPathResultType* result_type, xmlNode* node)
+{
+  xmlXPathObject* xpath_value = eval_common(xpath, namespaces, result_type, node);
+  xmlChar* result = xmlXPathCastToString(xpath_value);
+  xmlXPathFreeObject(xpath_value);
+  if (result)
+  {
+    const Glib::ustring uresult(reinterpret_cast<const char*>(result));
+    xmlFree(result);
+    return uresult;
+  }
+  return Glib::ustring();
+}
+
+} // anonymous namespace
+
 namespace xmlpp
 {
 
@@ -379,6 +466,39 @@ NodeSet Node::find(const Glib::ustring& xpath,
   return find_impl(ctxt, xpath);
 }
 
+bool Node::eval_to_boolean(const Glib::ustring& xpath, XPathResultType* result_type) const
+{
+  return eval_common_to_boolean(xpath, 0, result_type, impl_);
+}
+
+bool Node::eval_to_boolean(const Glib::ustring& xpath, const PrefixNsMap& namespaces,
+  XPathResultType* result_type) const
+{
+  return eval_common_to_boolean(xpath, &namespaces, result_type, impl_);
+}
+
+double Node::eval_to_number(const Glib::ustring& xpath, XPathResultType* result_type) const
+{
+  return eval_common_to_number(xpath, 0, result_type, impl_);
+}
+
+double Node::eval_to_number(const Glib::ustring& xpath, const PrefixNsMap& namespaces,
+  XPathResultType* result_type) const
+{
+  return eval_common_to_number(xpath, &namespaces, result_type, impl_);
+}
+
+Glib::ustring Node::eval_to_string(const Glib::ustring& xpath, XPathResultType* result_type) const
+{
+  return eval_common_to_string(xpath, 0, result_type, impl_);
+}
+
+Glib::ustring Node::eval_to_string(const Glib::ustring& xpath, const PrefixNsMap& namespaces,
+  XPathResultType* result_type) const
+{
+  return eval_common_to_string(xpath, &namespaces, result_type, impl_);
+}
+
 Glib::ustring Node::get_namespace_prefix() const
 {
   if(impl_->type == XML_DOCUMENT_NODE || impl_->type == XML_ENTITY_DECL)
diff --git a/libxml++/nodes/node.h b/libxml++/nodes/node.h
index 746bab2..b54fdb3 100644
--- a/libxml++/nodes/node.h
+++ b/libxml++/nodes/node.h
@@ -30,6 +30,23 @@ class Attribute;
 class Node;
 typedef std::vector<Node*> NodeSet;
 
+// xmlpp::XPathResultType is similar to xmlXPathObjectType in libxml2.
+/** An XPath expression is evaluated to yield a result, which
+ * has one of the following four basic types:
+ *   - node-set
+ *   - boolean
+ *   - number
+ *   - string
+ */
+enum XPathResultType
+{
+    XPATH_RESULT_UNDEFINED = 0,
+    XPATH_RESULT_NODESET = 1,
+    XPATH_RESULT_BOOLEAN = 2,
+    XPATH_RESULT_NUMBER = 3,
+    XPATH_RESULT_STRING = 4
+};
+
 /** Represents XML Nodes.
  * You should never new or delete Nodes. The Parser will create and manage them for you.
  */
@@ -111,13 +128,13 @@ public:
   Node* get_first_child(const Glib::ustring& name = Glib::ustring());
 
   /** Obtain the list of child nodes. You may optionally obtain a list of only the child nodes which have a certain name.
-   * @param name The names of the child nodes to get. If you do not specigy a name, then the list will contain all nodes, regardless of their names.
+   * @param name The names of the child nodes to get. If you do not specify a name, then the list will contain all nodes, regardless of their names.
    * @returns The list of child nodes.
    */
   NodeList get_children(const Glib::ustring& name = Glib::ustring());
 
   /** Obtain the list of child nodes. You may optionally obtain a list of only the child nodes which have a certain name.
-   * @param name The names of the child nodes to get. If you do not specigy a name, then the list will contain all nodes, regardless of their names.
+   * @param name The names of the child nodes to get. If you do not specify a name, then the list will contain all nodes, regardless of their names.
    * @returns The list of child nodes.
    */
   const NodeList get_children(const Glib::ustring& name = Glib::ustring()) const;
@@ -183,7 +200,7 @@ public:
    */
   Glib::ustring get_path() const;
 
-  /** Find nodes from a XPath expression.
+  /** Find nodes from an XPath expression.
    * @param xpath The XPath of the nodes.
    * @throws exception
    */
@@ -193,13 +210,91 @@ public:
    */
   typedef std::map<Glib::ustring, Glib::ustring> PrefixNsMap;
 
-  /** Find nodes from a XPath expression.
+  /** Find nodes from an XPath expression.
    * @param xpath The XPath of the nodes.
    * @param namespaces A map of namespace prefixes to namespace URIs to be used while finding.
    * @throws exception
    */
   NodeSet find(const Glib::ustring& xpath, const PrefixNsMap& namespaces) const;
 
+  /** Evaluate an XPath expression.
+   * @param xpath The XPath expression.
+   * @param[out] result_type Result type of the XPath expression before conversion
+   *             to boolean. If 0, the result type is not returned.
+   * @returns The value of the XPath expression. If the value is not of type boolean,
+   *          it is converted to boolean.
+   * @throws xmlpp::exception If the XPath expression cannot be evaluated.
+   *
+   * @newin{2,36}
+   */
+  bool eval_to_boolean(const Glib::ustring& xpath, XPathResultType* result_type = 0) const;
+
+
+  /** Evaluate an XPath expression.
+   * @param xpath The XPath expression.
+   * @param namespaces A map of namespace prefixes to namespace URIs to be used while evaluating.
+   * @param[out] result_type Result type of the XPath expression before conversion
+   *             to boolean. If 0, the result type is not returned.
+   * @returns The value of the XPath expression. If the value is not of type boolean,
+   *          it is converted to boolean.
+   * @throws xmlpp::exception If the XPath expression cannot be evaluated.
+   *
+   * @newin{2,36}
+   */
+  bool eval_to_boolean(const Glib::ustring& xpath, const PrefixNsMap& namespaces,
+    XPathResultType* result_type = 0) const;
+
+  /** Evaluate an XPath expression.
+   * @param xpath The XPath expression.
+   * @param[out] result_type Result type of the XPath expression before conversion
+   *             to number. If 0, the result type is not returned.
+   * @returns The value of the XPath expression. If the value is not of type number,
+   *          it is converted to number.
+   * @throws xmlpp::exception If the XPath expression cannot be evaluated.
+   *
+   * @newin{2,36}
+   */
+  double eval_to_number(const Glib::ustring& xpath, XPathResultType* result_type = 0) const;
+
+  /** Evaluate an XPath expression.
+   * @param xpath The XPath expression.
+   * @param namespaces A map of namespace prefixes to namespace URIs to be used while evaluating.
+   * @param[out] result_type Result type of the XPath expression before conversion
+   *             to number. If 0, the result type is not returned.
+   * @returns The value of the XPath expression. If the value is not of type number,
+   *          it is converted to number.
+   * @throws xmlpp::exception If the XPath expression cannot be evaluated.
+   *
+   * @newin{2,36}
+   */
+  double eval_to_number(const Glib::ustring& xpath, const PrefixNsMap& namespaces,
+    XPathResultType* result_type = 0) const;
+
+  /** Evaluate an XPath expression.
+   * @param xpath The XPath expression.
+   * @param[out] result_type Result type of the XPath expression before conversion
+   *             to string. If 0, the result type is not returned.
+   * @returns The value of the XPath expression. If the value is not of type string,
+   *          it is converted to string.
+   * @throws xmlpp::exception If the XPath expression cannot be evaluated.
+   *
+   * @newin{2,36}
+   */
+  Glib::ustring eval_to_string(const Glib::ustring& xpath, XPathResultType* result_type = 0) const;
+
+  /** Evaluate an XPath expression.
+   * @param xpath The XPath expression.
+   * @param namespaces A map of namespace prefixes to namespace URIs to be used while evaluating.
+   * @param[out] result_type Result type of the XPath expression before conversion
+   *             to string. If 0, the result type is not returned.
+   * @returns The value of the XPath expression. If the value is not of type string,
+   *          it is converted to string.
+   * @throws xmlpp::exception If the XPath expression cannot be evaluated.
+   *
+   * @newin{2,36}
+   */
+  Glib::ustring eval_to_string(const Glib::ustring& xpath, const PrefixNsMap& namespaces,
+    XPathResultType* result_type = 0) const;
 
   ///Access the underlying libxml implementation.
   _xmlNode* cobj();
@@ -211,7 +306,7 @@ public:
    *
    * This is only for use by the libxml++ implementation.
    *
-   * @para node A pointer to an xmlNode or a "derived" struct, such as xmlDoc, xmlAttr, etc.
+   * @param node A pointer to an xmlNode or a "derived" struct, such as xmlDoc, xmlAttr, etc.
    */
   static void create_wrapper(_xmlNode* node);
   
@@ -219,9 +314,9 @@ public:
    * recursively destroy the C++ instances for any children.
    *
    * This is only for use by the libxml++ implementation.
-   * @para node A pointer to an xmlNode or a "derived" struct, such as xmlDoc, xmlAttr, etc.
+   * @param node A pointer to an xmlNode or a "derived" struct, such as xmlDoc, xmlAttr, etc.
    */
-  static void free_wrappers(_xmlNode* attr);
+  static void free_wrappers(_xmlNode* node);
   
 protected:
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]