/*

www.sourceforge.net/projects/tinyxpath

Copyright (c) 2002-2004 Yves Berquin (yvesb@users.sourceforge.net)



This software is provided 'as-is', without any express or implied

warranty. In no event will the authors be held liable for any

damages arising from the use of this software.



Permission is granted to anyone to use this software for any

purpose, including commercial applications, and to alter it and

redistribute it freely, subject to the following restrictions:



1. The origin of this software must not be misrepresented; you must

not claim that you wrote the original software. If you use this

software in a product, an acknowledgment in the product documentation

would be appreciated but is not required.



2. Altered source versions must be plainly marked as such, and

must not be misrepresented as being the original software.



3. This notice may not be removed or altered from any source

distribution.

*/



#include <math.h>

#include <assert.h>

#include "xpath_processor.h"

#include "xml_util.h"



using namespace TinyXPath;



#ifdef TINYXPATH_DEBUG

   // only define DUMP_ACTION if TINYXPATH_DEBUG is defined

   #define DUMP_ACTION

#endif



// Asserts instead of exception handling

execution_error::execution_error(int i_)

{

#if defined(_PS3) && defined(_DEBUG)

   assert(false);

#endif

   i=i_;

}



/// Utility class to allow us to delete a TiXmlElement without its children

class TiXmlElementNoDelete : public TiXmlElement

{

public :

   TiXmlElementNoDelete () : TiXmlElement ("") {} // this constructor is never used

   void v_clean_children () 

   {

      firstChild=0; 

      lastChild=0;

   }

} ;



/// Utility class to reset the parent, prev and next pointers of a node

class TiXmlNodeManip : public TiXmlNode 

{

public :

   TiXmlNodeManip () : TiXmlNode (TiXmlNode::UNKNOWN) {}

   void v_reset (const TiXmlNode * XNp_parent, const TiXmlNode * XNp_next, const TiXmlNode * XNp_prev)

   {

      parent = (TiXmlNode *) XNp_parent;

      next = (TiXmlNode *) XNp_next;

      prev = (TiXmlNode *) XNp_prev;

   }

   void v_isolate ()

   {

      parent = next = prev = NULL;

   }

} ;



/// xpath_processor constructor

xpath_processor::xpath_processor (

   const TiXmlNode * XNp_source_tree,  ///< Source XML tree

   const char * cp_xpath_expr)         ///< XPath expression

      : xpath_stream (cp_xpath_expr) 

{

   if (XNp_source_tree && cp_xpath_expr)

      XNp_base = XNp_source_tree;

   else

      XNp_base = NULL;

   XEp_context = NULL;

   o_is_context_by_name = false;

}



/// Compute an XPath expression, and return the number of nodes in the resulting node set.

/// \n Returns 0 if the result is not a node set

unsigned xpath_processor::u_compute_xpath_node_set ()

{

   er_compute_xpath ();

   if (er_result . e_type != e_node_set)

      return 0;

   return er_result . nsp_get_node_set () -> u_get_nb_node_in_set ();

}



/// Get one of the XML nodes from the resulting node set. Can only be used after a call to u_compute_xpath_node_set

void xpath_processor::v_get_xpath_base (

   unsigned u_order,             ///< Order of the node. Must be between 0 and the number of nodes - 1

   const TiXmlBase * & XBp_res,  ///< Output node

   bool & o_attrib)              ///< True if the output node is an attribute, false if it's a TiXmlNode

{

   XBp_res = NULL;

   o_attrib = false;

   if (er_result . e_type != e_node_set)

      return;

   if (u_order >= er_result . nsp_get_node_set () -> u_get_nb_node_in_set ())

      return;

   XBp_res = er_result . nsp_get_node_set () -> XBp_get_base_in_set (u_order);

   o_attrib = er_result . nsp_get_node_set () -> o_is_attrib (u_order);

}



/// Retrieves an XPath node from the node set. This assumes you know it's not an attribute

TiXmlNode * xpath_processor::XNp_get_xpath_node (

   unsigned u_order)    ///< Order of the node. Must be between 0 and the number of nodes - 1

{

   bool o_attrib;

   const TiXmlBase * XBp_res;



   o_attrib = false;

   XBp_res = NULL;

   v_get_xpath_base (u_order, XBp_res, o_attrib);

   if (o_attrib)

      return NULL;

   return (TiXmlNode *) XBp_res;

}



/// Retrieves an XPath attribute from the node set. This assumes you know it's an attribute

TiXmlAttribute * xpath_processor::XAp_get_xpath_attribute (

   unsigned u_order)    ///< Order of the node. Must be between 0 and the number of nodes - 1

{

   bool o_attrib;

   const TiXmlBase * XBp_res;



   o_attrib = false;

   XBp_res = NULL;

   v_get_xpath_base (u_order, XBp_res, o_attrib);

   if (! o_attrib)

      return NULL;

   return (TiXmlAttribute *) XBp_res;

}



void xpath_processor::v_build_root ()

{

   if (XNp_base)

   {

      // backup the parent, prev and next node pointers

      // we will restore them in the destructor

      XNp_caller_parent = XNp_base -> Parent ();

      XNp_caller_prev = XNp_base -> PreviousSibling ();

      XNp_caller_next = XNp_base -> NextSibling ();



      // now, isolate this element from it's parent and siblings

      TiXmlNodeManip * XNp_false_node = (TiXmlNodeManip *) XNp_base;

      XNp_false_node -> v_isolate ();



      // create a new root for it

      XEp_root = new TiXmlElement ("root");

      XEp_root -> LinkEndChild ((TiXmlNode *) XNp_base);



      // set the main node as the context one, if it's an element

      if (XNp_base -> ToElement ())

         XEp_context = XNp_base -> ToElement ();

      v_order_tree ();

   }

   else

      XEp_root = NULL;

}



/// Remove our fake root, and restore the original relationships of the 

/// node given to us as argument

void xpath_processor::v_remove_root ()

{

   if (XEp_root)

   {

      assert (XNp_base);

      // reset the original relationships

      TiXmlNodeManip * XNp_false_node = (TiXmlNodeManip *) XNp_base;

      XNp_false_node -> v_reset (XNp_caller_parent, XNp_caller_next, XNp_caller_prev);



      TiXmlElementNoDelete * XEp_false_root = (TiXmlElementNoDelete *) XEp_root;

      XEp_false_root -> v_clean_children ();

      delete XEp_false_root;

   }

}



/// Compute an XPath expression 

expression_result xpath_processor::er_compute_xpath ()

{

   XPATH_TRY

   {

      v_build_root ();

            

      if (! XEp_root)

         // no correct initialization of the xpath_processor object

         XPATH_THROW execution_error (1);



      // Decode XPath expression

      v_evaluate ();



      // Compute result

      v_execute_stack ();



      /// The executions stack need to contain 1 and only 1 element, otherwize it's not valid

      if (xs_stack . u_get_size () == 1)

      {

         er_result = * xs_stack . erp_top ();

         xs_stack . v_pop ();

         e_error = e_no_error;

      }

      else

      {

         expression_result er_null;

         er_result = er_null;

         e_error = e_error_stack;

      }

   }

   XPATH_CATCH (syntax_error)

   {

      expression_result er_null;

      er_result = er_null;

      e_error = e_error_syntax;

   }

   XPATH_CATCH (syntax_overflow)

   {

      expression_result er_null;

      er_result = er_null;

      e_error = e_error_overflow;

   }

   XPATH_CATCH (execution_error e)

   {

      expression_result er_null;

      er_result = er_null;

      e_error = e_error_execution;

   }



   v_remove_root ();



   return er_result;

}



/// Compute an XPath expression and return the result as a string

TIXML_STRING xpath_processor::S_compute_xpath ()

{

   expression_result er_res;

   TIXML_STRING S_res;



   er_res = er_compute_xpath ();

   S_res = er_res . S_get_string ();   

   return S_res;

}



/// Compute an XPath expression and return the result as an integer

int xpath_processor::i_compute_xpath ()

{

   expression_result er_res;

   int i_res;



   er_res = er_compute_xpath ();

   i_res = er_res . i_get_int ();   

   return i_res;

}



bool xpath_processor::o_compute_xpath ()

{

   expression_result er_res;

   bool o_res;



   er_res = er_compute_xpath ();

   o_res = er_res . o_get_bool ();   

   return o_res;

}



double xpath_processor::d_compute_xpath ()

{

   expression_result er_res;

   double d_res;



   er_res = er_compute_xpath ();

   d_res = er_res . d_get_double ();   

   return d_res;

}



/// Callback from the XPath decoder : a rule has to be applied

void xpath_processor::v_action (

	xpath_construct xc_rule,      ///< XPath Rule 

	unsigned u_sub,               ///< Rule sub number

	unsigned u_variable,          ///< Parameter, depends on the rule

	const char * cp_literal)      ///< Input literal, depends on the rule

{

   as_action_store . v_add (xc_rule, u_sub, u_variable, cp_literal);

   #ifdef TINYXPATH_DEBUG

      printf ("Action %2d : %s (%d,%d,%s)\n", as_action_store . i_get_size () - 1, cp_disp_construct (xc_rule), u_sub, u_variable, cp_literal);

   #endif

}



/// Internal use. Retrieves the current action counter

int xpath_processor::i_get_action_counter ()

{

   // callback for current stack position

   return as_action_store . i_get_size ();

}



/// Internal use. Executes the XPath expression. The executions starts at the end of the as_action_store list

void xpath_processor::v_execute_stack () 

{

   as_action_store . v_set_position (as_action_store . i_get_size () - 1);

   v_execute_one (xpath_expr, false);

}



/// Retrieves one quadruplet from the action placeholder

void xpath_processor::v_pop_one_action (

   xpath_construct & xc_action,  ///< Next rule on placeholder

   unsigned & u_sub,             ///< Sub rule  

   unsigned & u_ref,             ///< Rule optional parameter

   TIXML_STRING & S_literal)     ///< Rule optional string

{

   int i_1, i_2, i_3;



   as_action_store . v_get (as_action_store . i_get_position (), i_1, i_2, i_3, S_literal);

   xc_action = (xpath_construct) i_1;

   u_sub = i_2;

   u_ref = i_3;

   as_action_store . v_dec_position ();

}



/// Executes one XPath rule

void xpath_processor::v_execute_one (

   xpath_construct xc_rule,      ///< Rule number

   bool o_skip_only)             ///< True if we only need to skip rules and not act on the data stack

{

	xpath_construct xc_action;

	unsigned u_sub;      

	unsigned u_variable;

	TIXML_STRING S_literal;



	TIXML_STRING S_name;

   expression_result ** erpp_arg;

   unsigned u_arg;

   bool o_error;



   v_pop_one_action (xc_action, u_sub, u_variable, S_literal);

   // verify it's the rule we were waiting for

   if (xc_action != xc_rule)

      XPATH_THROW execution_error (2);

   switch (xc_action)

   {

      case xpath_expr :

         // [14] Expr ::= OrExpr 

         v_execute_one (xpath_or_expr, o_skip_only);

         break;



      case xpath_or_expr : 

         switch (u_sub)

         {

            case xpath_or_expr_simple :

               v_execute_one (xpath_and_expr, o_skip_only);

               break;

            case xpath_or_expr_or :

               o_error = false;

               erpp_arg = NULL;

               XPATH_TRY

               {

                  v_execute_one (xpath_and_expr, o_skip_only);

                  if (! o_skip_only)

                  {

                     erpp_arg = new expression_result * [2];

                     memset (erpp_arg, 0, 2 * sizeof (expression_result *));

                     erpp_arg [1] = new expression_result (* xs_stack . erp_top ());

                     xs_stack . v_pop ();

                  }

                  v_execute_one (xpath_and_expr, o_skip_only); 

                  if (! o_skip_only)

                  {

                     erpp_arg [0] = new expression_result (* xs_stack . erp_top ());

                     xs_stack . v_pop ();

                     v_function_or (erpp_arg);

                  }

               }

               XPATH_CATCH (execution_error)

               {

                  o_error = true;

               }

               if (erpp_arg)

               {

                  for (u_arg = 0; u_arg < 2; u_arg++)

                  {

                     if (erpp_arg [u_arg])

                        delete erpp_arg [u_arg];

                  }

                  delete [] erpp_arg;

               }

               if (o_error)

                  XPATH_THROW execution_error (3);

               break;

         }

         break;



      case xpath_and_expr :

         switch (u_sub)

         {

            case xpath_and_expr_simple :

               v_execute_one (xpath_equality_expr, o_skip_only);

               break;

            case xpath_and_expr_and :

               o_error = false;

               erpp_arg = NULL;

               XPATH_TRY

               {

                  v_execute_one (xpath_equality_expr, o_skip_only);

                  if (! o_skip_only)

                  {

                     erpp_arg = new expression_result * [2];

                     memset (erpp_arg, 0, 2 * sizeof (expression_result *));

                     erpp_arg [1] = new expression_result (* xs_stack . erp_top ());

                     xs_stack . v_pop ();

                  }

                  v_execute_one (xpath_equality_expr, o_skip_only);

                  if (! o_skip_only)

                  {

                     erpp_arg [0] = new expression_result (* xs_stack . erp_top ());

                     xs_stack . v_pop ();

                     v_function_and (erpp_arg);

                  }

               }

               XPATH_CATCH (execution_error)

               {

                  o_error = true;

               }

               if (erpp_arg)

               {

                  for (u_arg = 0; u_arg < 2; u_arg++)

                  {

                     if (erpp_arg [u_arg])

                        delete erpp_arg [u_arg];

                  }

                  delete [] erpp_arg;

               }

               if (o_error)

                  XPATH_THROW execution_error (4);

               break;

         }

         break;



      case xpath_equality_expr :

         switch (u_sub)

         {

            case xpath_equality_expr_simple :

               v_execute_one (xpath_relational_expr, o_skip_only);

               break;

            case xpath_equality_expr_equal : 

            case xpath_equality_expr_not_equal :

               o_error = false;

               erpp_arg = NULL;

               XPATH_TRY

               {

                  v_execute_one (xpath_relational_expr, o_skip_only);

                  if (! o_skip_only)

                  {

                     erpp_arg = new expression_result * [2];

                     memset (erpp_arg, 0, 2 * sizeof (expression_result *));

                     erpp_arg [1] = new expression_result (* xs_stack . erp_top ());

                     xs_stack . v_pop ();

                  }

                  v_execute_one (xpath_relational_expr, o_skip_only); // this is buggy. should be xpath_equality_expr

                  if (! o_skip_only)

                  {

                     erpp_arg [0] = new expression_result (* xs_stack . erp_top ());

                     xs_stack . v_pop ();

                     if (u_sub == xpath_equality_expr_equal)

                        v_function_equal (erpp_arg);

                     else

                        v_function_not_equal (erpp_arg);

                  }

               }

               XPATH_CATCH (execution_error)

               {

                  o_error = true;

               }

               if (erpp_arg)

               {

                  for (u_arg = 0; u_arg < 2; u_arg++)

                  {

                     if (erpp_arg [u_arg])

                        delete erpp_arg [u_arg];

                  }

                  delete [] erpp_arg;

               }

               if (o_error)

                  XPATH_THROW execution_error (5);

               break;

         }

         break;



      case xpath_relational_expr :

         switch (u_sub)

         {

            case xpath_relational_expr_simple :

               v_execute_one (xpath_additive_expr, o_skip_only);

               break;

            case xpath_relational_expr_lt :

            case xpath_relational_expr_gt :

            case xpath_relational_expr_lte :

            case xpath_relational_expr_gte :

               o_error = false;

               erpp_arg = NULL;

               XPATH_TRY

               {

                  v_execute_one (xpath_additive_expr, o_skip_only);

                  if (! o_skip_only)

                  {

                     erpp_arg = new expression_result * [2];

                     memset (erpp_arg, 0, 2 * sizeof (expression_result *));

                     erpp_arg [1] = new expression_result (* xs_stack . erp_top ());

                     xs_stack . v_pop ();

                  }

                  v_execute_one (xpath_additive_expr, o_skip_only); // this is buggy. should be xpath_equality_expr

                  if (! o_skip_only)

                  {

                     erpp_arg [0] = new expression_result (* xs_stack . erp_top ());

                     xs_stack . v_pop ();

                     v_function_relational (erpp_arg, u_sub);

                  }

               }

               XPATH_CATCH (execution_error)

               {

                  o_error = true;

               }

               if (erpp_arg)

               {

                  for (u_arg = 0; u_arg < 2; u_arg++)

                  {

                     if (erpp_arg [u_arg])

                        delete erpp_arg [u_arg];

                  }

                  delete [] erpp_arg;

               }

               if (o_error)

                  XPATH_THROW execution_error (6);

               break;

            default :

               assert (false);

         }

         break;



      case xpath_additive_expr :

         switch (u_sub)

         {

            case xpath_additive_expr_simple :

               v_execute_one (xpath_multiplicative_expr, o_skip_only);

               break;

            case xpath_additive_expr_plus :  

            case xpath_additive_expr_minus :

               XPATH_TRY

               {

                  o_error = false;

                  erpp_arg = NULL;

                  v_execute_one (xpath_multiplicative_expr, o_skip_only);

                  if (! o_skip_only)

                  {

                     erpp_arg = new expression_result * [2];

                     memset (erpp_arg, 0, 2 * sizeof (expression_result *));

                     erpp_arg [1] = new expression_result (* xs_stack . erp_top ());

                     xs_stack . v_pop ();

                  }

                  v_execute_one (xpath_multiplicative_expr, o_skip_only);

                  if (! o_skip_only)

                  {

                     erpp_arg [0] = new expression_result (* xs_stack . erp_top ());

                     xs_stack . v_pop ();

                     if (u_sub == xpath_additive_expr_plus)

                        v_function_plus (erpp_arg);

                     else

                        v_function_minus (erpp_arg);

                  }

               }

               XPATH_CATCH (execution_error)

               {

                  o_error = true;

               }

               if (erpp_arg)

               {

                  for (u_arg = 0; u_arg < 2; u_arg++)

                  {

                     if (erpp_arg [u_arg])

                        delete erpp_arg [u_arg];

                  }

                  delete [] erpp_arg;

               }

               if (o_error)

                  XPATH_THROW execution_error (7);

               break;

            case xpath_additive_expr_more_plus :  

            case xpath_additive_expr_more_minus :  

               // These 2 cases are involved for expressions like a+b+c 

               // The second argument is an additive expression, not a multiplicative as it is the case

               // when single a+b expressions are encountered

               XPATH_TRY

               {

                  o_error = false;

                  erpp_arg = NULL;

                  v_execute_one (xpath_multiplicative_expr, o_skip_only);

                  if (! o_skip_only)

                  {

                     erpp_arg = new expression_result * [2];

                     memset (erpp_arg, 0, 2 * sizeof (expression_result *));

                     erpp_arg [1] = new expression_result (* xs_stack . erp_top ());

                     xs_stack . v_pop ();

                  }

                  v_execute_one (xpath_additive_expr, o_skip_only);

                  if (! o_skip_only)

                  {

                     erpp_arg [0] = new expression_result (* xs_stack . erp_top ());

                     xs_stack . v_pop ();

                     if (u_sub == xpath_additive_expr_more_plus)

                        v_function_plus (erpp_arg);

                     else

                        v_function_minus (erpp_arg);

                  }

               }

               XPATH_CATCH (execution_error)

               {

                  o_error = true;

               }

               if (erpp_arg)

               {

                  for (u_arg = 0; u_arg < 2; u_arg++)

                  {

                     if (erpp_arg [u_arg])

                        delete erpp_arg [u_arg];

                  }

                  delete [] erpp_arg;

               }

               if (o_error)

                  XPATH_THROW execution_error (7);

               break;

         }

         break;



      case xpath_multiplicative_expr :

         switch (u_sub)

         {

            case xpath_multiplicative_expr_simple :

               v_execute_one (xpath_unary_expr, o_skip_only);

               break;

            case xpath_multiplicative_expr_star :

            case xpath_multiplicative_expr_div :

            case xpath_multiplicative_expr_mod :

               XPATH_TRY

               {

                  o_error = false;

                  erpp_arg = NULL;

                  v_execute_one (xpath_unary_expr, o_skip_only);

                  if (! o_skip_only)

                  {

                     erpp_arg = new expression_result * [2];

                     memset (erpp_arg, 0, 2 * sizeof (expression_result *));

                     erpp_arg [1] = new expression_result (* xs_stack . erp_top ());

                     xs_stack . v_pop ();

                  }

                  v_execute_one (xpath_unary_expr, o_skip_only);

                  if (! o_skip_only)

                  {

                     erpp_arg [0] = new expression_result (* xs_stack . erp_top ());

                     xs_stack . v_pop ();

                     v_function_mult (erpp_arg, u_sub);

                  }

               }

               XPATH_CATCH (execution_error)

               {

                  o_error = true;

               }

               if (erpp_arg)

               {

                  for (u_arg = 0; u_arg < 2; u_arg++)

                  {

                     if (erpp_arg [u_arg])

                        delete erpp_arg [u_arg];

                  }

                  delete [] erpp_arg;

               }

               if (o_error)

                  XPATH_THROW execution_error (8);

               break;

         }

         break;

      case xpath_unary_expr :

         switch (u_sub)

         {

            case xpath_unary_expr_simple :   

               // [27]   UnaryExpr			::=   UnionExpr 

               v_execute_one (xpath_union_expr, o_skip_only);

               break;

            case xpath_unary_expr_minus :

               // [27]   UnaryExpr			::=   '-' UnaryExpr

               v_execute_one (xpath_unary_expr, o_skip_only);

               v_function_opposite ();

               break;

         }

         break;



      case xpath_union_expr :

         switch (u_sub)

         {

            case xpath_union_expr_simple :

               v_execute_one (xpath_path_expr, o_skip_only);

               break;

            case xpath_union_expr_union :

               v_execute_one (xpath_union_expr, o_skip_only);

               if (xs_stack . erp_top () -> e_type != e_node_set)

                  XPATH_THROW execution_error (9);

               // here after is a block, so that the node_set are locals to it

               {

                  node_set ns_1, ns_2;

                  ns_1 = ns_pop_node_set ();

                  v_execute_one (xpath_path_expr, o_skip_only);

                  ns_2 = ns_pop_node_set ();

                  v_function_union (ns_1, ns_2);

               }

               break;

         }

         break;



      case xpath_path_expr :

         switch (u_sub)

         {

            case xpath_path_expr_location_path :

               v_execute_one (xpath_location_path, o_skip_only);

               break;

            case xpath_path_expr_filter :

               v_execute_one (xpath_filter_expr, o_skip_only);

               break;

            case xpath_path_expr_slash :

               v_execute_one (xpath_filter_expr, o_skip_only);

               v_execute_one (xpath_relative_location_path, o_skip_only);

               break;

            case xpath_path_expr_2_slash :

               v_execute_one (xpath_filter_expr, o_skip_only);

               v_execute_one (xpath_relative_location_path, o_skip_only);

               break;

         }

         break;



      case xpath_filter_expr :

         switch (u_sub)

         {

            case xpath_filter_expr_primary :

               v_execute_one (xpath_primary_expr, o_skip_only);

               break;

            case xpath_filter_expr_predicate :

               v_execute_one (xpath_filter_expr, o_skip_only);

               v_execute_one (xpath_predicate, o_skip_only);

               break;

         }

         break;



      case xpath_primary_expr :

         switch (u_sub)

         {

            case xpath_primary_expr_variable :

               v_execute_one (xpath_variable_reference, o_skip_only);

               break;

            case xpath_primary_expr_paren_expr :

               v_execute_one (xpath_expr, o_skip_only);

               break;

            case xpath_primary_expr_literal :

               if (! o_skip_only)

                  v_push_string (S_literal);

               break;

            case xpath_primary_expr_number :

               if (! o_skip_only)

               {

                  if (strchr (S_literal . c_str (), '.'))

                     v_push_double (atof (S_literal . c_str ()));

                  else

                     v_push_int (atoi (S_literal . c_str ()), "primary number");

               }

               break;

            case xpath_primary_expr_function_call :

               v_execute_one (xpath_function_call, o_skip_only);

               break;

         }

         break;



      case xpath_function_call :

         erpp_arg = NULL;

         o_error = false;

         XPATH_TRY

         {

            if (u_sub)

            {

               // execute arguments

               if (! o_skip_only)

               {

                  erpp_arg = new expression_result * [u_variable];

                  memset (erpp_arg, 0, u_variable * sizeof (expression_result *));

               }

               for (u_arg = 0; u_arg < u_variable; u_arg++)

               {

                  /// Compute each argument, and store them in a temporary list

                  v_execute_one (xpath_argument, o_skip_only);

                  if (! o_skip_only)

                  {

                     if (! xs_stack . u_get_size ())

                        XPATH_THROW execution_error (10);

                     erpp_arg [u_variable - u_arg - 1] = new expression_result (* xs_stack . erp_top ());

                     xs_stack . v_pop ();

                  }

               }

            }

            v_execute_one (xpath_xml_q_name, o_skip_only);

            if (! o_skip_only)

            {

               S_name = S_pop_string ();

               v_execute_function (S_name, u_variable, erpp_arg);

            }

         }

         XPATH_CATCH (execution_error)

         {

            o_error = true;

         }

         if (erpp_arg)

         {

            for (u_arg = 0; u_arg < u_variable; u_arg++)

            {

               if (erpp_arg [u_arg])

                  delete erpp_arg [u_arg];

            }

            delete [] erpp_arg;

         }

         if (o_error)

            XPATH_THROW execution_error (11);

         break;



      case xpath_xml_q_name :

         switch (u_sub)

         {

            case xpath_xml_q_name_colon :

               v_execute_one (xpath_xml_prefix, o_skip_only);

               v_execute_one (xpath_xml_local_part, o_skip_only);

               break;

            case xpath_xml_q_name_simple :

               v_execute_one (xpath_xml_local_part, o_skip_only);

               break;

         }

         break;



      case xpath_xml_local_part :

         if (! o_skip_only)

            v_push_string (S_literal);

         break;



      case xpath_xml_prefix :

         if (! o_skip_only)

            v_push_string (S_literal);

         break;



      case xpath_argument :

         v_execute_one (xpath_expr, o_skip_only);

         break;



      case xpath_location_path :

         switch (u_sub)

         {

            case xpath_location_path_rel :

               v_execute_one (xpath_relative_location_path, o_skip_only);

               break;

            case xpath_location_path_abs :

               v_execute_one (xpath_absolute_location_path, o_skip_only);

               break;

         }

         break;

      

      case xpath_relative_location_path :

         switch (u_sub)

         {

            case xpath_relative_location_path_rel_step :

               // RelativeLocationPath	::=   RelativeLocationPath '/' Step 

               v_execute_one (xpath_relative_location_path, o_skip_only);

               // v_execute_step (i_relative_action);

               break;

            case xpath_relative_location_path_rel_double_slash_step :

               // RelativeLocationPath	::= RelativeLocationPath '//' Step 

               break;

            case xpath_relative_location_path_step :

               // RelativeLocationPath	::=   Step

               int i_dummy;

               i_dummy = -2;

               v_execute_step (i_dummy, o_skip_only);

               break;

         }

         break;



      case xpath_absolute_location_path :

         switch (u_sub)

         {

            case xpath_absolute_location_path_slash :

               // AbsoluteLocationPath	::=   '/' 

               v_execute_absolute_path (u_variable, false, false);

               break;

            case xpath_absolute_location_path_slash_rel :

               // AbsoluteLocationPath	::=   '/' RelativeLocationPath

               v_execute_absolute_path (u_variable, true, false);

               break;

            case xpath_absolute_location_path_abbrev :

               // AbsoluteLocationPath	::= AbbreviatedAbsoluteLocationPath

               v_execute_absolute_path (u_variable, true, true);

               break;

         }

         break;



      case xpath_axis_specifier :

         switch (u_sub)

         {

            case xpath_axis_specifier_at :

               // AxisSpecifier			::= '@'

               if (! o_skip_only)

                  // will be used in the v_execute_step

                  v_push_int (1, "axis specifier is at");

               break;

            case xpath_axis_specifier_axis_name :

               // AxisSpecifier			::= AxisName '::'

               v_execute_one (xpath_axis_name, o_skip_only);

               break;

            case xpath_axis_specifier_empty :

               if (! o_skip_only)

                  // will be used in the v_execute_step

                  v_push_int (0, "axis specifier is empty");

               break;

         }

         break;



      case xpath_axis_name :

         if (! o_skip_only)

            // will be used in the v_execute_step

            v_push_int (u_variable, "axis is a name");

         break;



      case xpath_node_test :

         // to do : processing instructions ???

         switch (u_sub)

         {

            case xpath_node_test_reserved_keyword :

               // will be used in the v_execute_step

               if (! o_skip_only)

               {

                  i_pop_int ();

                  v_push_int (u_variable, "axis is a keyword");

                  v_push_string ("*");

               }

               break;

            case xpath_node_test_pi :

               break;

            case xpath_node_test_pi_lit :

               break;

            case xpath_node_test_name_test :

               v_execute_one (xpath_name_test, o_skip_only);

               break;

         }

         break;



      case xpath_predicate :

         // [8]   Predicate				::=   '[' PredicateExpr ']' 

         v_execute_one (xpath_predicate_expr, o_skip_only);

         break;



      case xpath_predicate_expr :

         // [9]   PredicateExpr			::=   Expr

         v_execute_one (xpath_expr, o_skip_only);

         break;



      case xpath_name_test :

         switch (u_sub)

         {

            case xpath_name_test_star :

               if (! o_skip_only)

                  v_push_string ("*");

               break;

            case xpath_name_test_ncname :

               break;

            case xpath_name_test_qname :

               v_execute_one (xpath_xml_q_name, o_skip_only);

               break;

         }

         break;



      default :

         XPATH_THROW execution_error (12);

   }

}



/// Execute a full set of absolute/relative/relative/.. computation

void xpath_processor::v_execute_absolute_path (

   unsigned u_action_position,      ///< Position of the placeholder after the rule

   bool o_with_rel,                 ///< true if there is some relative path

   bool o_everywhere)               ///< true if it's a '//' path

{

   unsigned u_end_action;

   int i_relative_action;

   bool o_do_last;



   u_end_action = u_action_position;



   if (o_with_rel)

   {

      int i_1, i_2, i_3;

      int i_bak_position, i_current, i_first, i_relative;

      TIXML_STRING S_lit;



      // compute position of the first (absolute) step

      i_current = as_action_store . i_get_position ();

      if (o_everywhere)

         i_relative = i_current - 2;

      else

         i_relative = i_current - 1;

      as_action_store . v_get (i_relative, i_1, i_2, i_3, S_lit);

      if (i_1 == xpath_relative_location_path)

      {

         o_do_last = true;

         i_first = i_3 - 1;

      }

      else

      {

         o_do_last = false;

         i_first = i_relative;

      }

      // i_first = i_3 - 1;

      i_bak_position = as_action_store . i_get_position ();

      as_action_store . v_set_position (i_first);

      if (o_everywhere)

         i_relative_action = -1;

      else

         i_relative_action = 0;

      v_execute_step (i_relative_action, false);



      bool o_end = false;

      do

      {

         i_relative--;

         as_action_store . v_get (i_relative, i_1, i_2, i_3, S_lit);

         if (i_1 != xpath_relative_location_path)

            o_end = true;

         else

         {

            as_action_store . v_set_position (i_3 - 1);

            v_execute_step (i_relative_action, false);

         }

      } while (! o_end);



      if (o_do_last)

      {

         // apply last one



         as_action_store . v_set_position (i_relative);

         v_execute_step (i_relative_action, false);

      }

      // resume the execution after the whole path construction

      as_action_store . v_set_position ((int) u_end_action - 1);

   }

}



/// One step execution

void xpath_processor ::v_execute_step (

   int & i_relative_action,         ///< Path position : -1 if first in a '//' path, 0 if first in a '/' path, > 0 if followers

   bool o_skip_only)

{

   bool o_by_name;

   int i_axis_type, i_end_store, i_node_store, i_pred_store;

   unsigned u_nb_node, u_node, u_pred, u_sub, u_variable;

	xpath_construct xc_action;

   TIXML_STRING S_literal, S_name;

   TiXmlElement const* XEp_child;

   TiXmlElement const* XEp_elem;

   TiXmlElement const* XEp_father;

   TiXmlAttribute const * XAp_attrib;

   TiXmlNode const * XNp_next;

   TiXmlNode const * XNp_parent;

   node_set ns_source, ns_target;



   if (! o_skip_only)

   {

      /// Initialize the source node set if it's the first step of a path

      switch (i_relative_action)

      {

         case -2 :

            // relative to context

            ns_source . v_add_node_in_set (XEp_context);

            i_relative_action = 1;

            break;

         case -1 :

            // everywhere

            ns_source . v_copy_selected_node_recursive (XEp_root);

            i_relative_action = 1;

            break;

         case 0 :

            // first absolute

            ns_source . v_add_node_in_set (XEp_root);

            i_relative_action = 1;

            break;

         default :

            // second and following steps

            ns_source = * (xs_stack . erp_top () -> nsp_get_node_set ());

            xs_stack . v_pop ();

            break;

      }

   }

            

   // Pop our step action from the action placeholder

   v_pop_one_action (xc_action, u_sub, u_variable, S_literal);



   // Skip the predicates

   i_pred_store = as_action_store . i_get_position ();

   for (u_pred = 0; u_pred < u_variable; u_pred++)

      v_execute_one (xpath_predicate, true);

   i_node_store = as_action_store . i_get_position ();



   // Skip the node test

   v_execute_one (xpath_node_test, true);



   // Run the axis

   v_execute_one (xpath_axis_specifier, o_skip_only);

   i_end_store = as_action_store . i_get_position ();



   // Run the node test

   as_action_store . v_set_position (i_node_store);

   v_execute_one (xpath_node_test, o_skip_only);

   as_action_store . v_set_position (i_pred_store);



   if (! o_skip_only)

   {

      S_name = S_pop_string ();

      o_by_name = ! (S_name == "*");



      // Retrieve the archive flag stored by the xpath_axis_specifier rule execution

      // o_attrib_flag = o_pop_bool ();

      i_axis_type = i_pop_int ();



      u_nb_node = ns_source . u_get_nb_node_in_set ();

      for (u_node = 0; u_node < u_nb_node; u_node++)

      {

         if (! ns_source . o_is_attrib (u_node))

         {

            XEp_father = ns_source . XNp_get_node_in_set (u_node) -> ToElement ();

            if (XEp_father)

            {

               switch (i_axis_type)

               {

                  case 0 :

                  case lex_child :

                     // none

                     XEp_child = XEp_father -> FirstChildElement ();

                     while (XEp_child)

                     {

                        ns_target . v_add_node_in_set_if_name_or_star (XEp_child, S_name);

                        XEp_child = XEp_child -> NextSiblingElement ();

                     }

                     break;

                  case 1 :

                  case lex_attribute :

                     // @

                     XAp_attrib = XEp_father -> FirstAttribute ();

                     while (XAp_attrib)

                     {

                        ns_target . v_add_attrib_in_set_if_name_or_star (XAp_attrib, S_name);

                        XAp_attrib = XAp_attrib -> Next ();

                     }

                     break;

                  case lex_parent :

                     XNp_parent = XEp_father -> Parent ();

                     if (XNp_parent)

                        ns_target . v_add_node_in_set_if_name_or_star (XNp_parent, S_name);

                     break;

                  case lex_ancestor :

                     XNp_parent = XEp_father -> Parent ();

                     // we have to exclude our own dummy parent

                     while (XNp_parent && XNp_parent != XEp_root)

                     {

                        ns_target . v_add_node_in_set_if_name_or_star (XNp_parent, S_name);

                        XNp_parent = XNp_parent -> Parent ();

                     }

                     break;

                  case lex_ancestor_or_self :

                     if (XEp_father != XEp_root)

                        ns_target . v_add_node_in_set_if_name_or_star (XEp_father, S_name);

                     XNp_parent = XEp_father -> Parent ();

                     // we have to exclude our own dummy parent

                     while (XNp_parent && XNp_parent != XEp_root)

                     {

                        ns_target . v_add_node_in_set_if_name_or_star (XNp_parent, S_name);

                        XNp_parent = XNp_parent -> Parent ();

                     }

                     break;

                  case lex_following_sibling :

                     XNp_next = XEp_father -> NextSiblingElement ();

                     while (XNp_next)

                     {

                        ns_target . v_add_node_in_set_if_name_or_star (XNp_next, S_name);

                        XNp_next = XNp_next -> NextSiblingElement ();

                     }

                     break;

                  case lex_preceding_sibling :

                     XNp_next = XEp_father -> PreviousSibling ();

                     while (XNp_next)

                     {

                        if (XNp_next -> Type () == TiXmlNode::ELEMENT)

                           ns_target . v_add_node_in_set_if_name_or_star (XNp_next, S_name);

                        XNp_next = XNp_next -> PreviousSibling ();

                     }

                     break;

                  case lex_descendant :

                     if (S_name == "*")

                        ns_target . v_copy_selected_node_recursive_no_attrib (XEp_father, NULL);

                     else

                        ns_target . v_copy_selected_node_recursive_no_attrib (XEp_father, S_name . c_str ());

                     break;

                  case lex_descendant_or_self :

                     if (XEp_father != XEp_root)

                        ns_target . v_add_node_in_set_if_name_or_star (XEp_father, S_name);

                     if (S_name == "*")

                        ns_target . v_copy_selected_node_recursive_no_attrib (XEp_father, NULL);

                     else

                        ns_target . v_copy_selected_node_recursive_no_attrib (XEp_father, S_name . c_str ());

                     break;

                  case lex_self :

                     if (XEp_father != XEp_root)

                        ns_target . v_add_node_in_set_if_name_or_star (XEp_father, S_name);

                     break;

                  case lex_following :

                     ns_target . v_add_all_foll_node (XEp_father, S_name);

                     break;

                  case lex_preceding :

                     ns_target . v_add_all_prec_node (XEp_father, S_name);

                     break;

                  case lex_comment :

                     XNp_next = XEp_father -> FirstChild ();               

                     while (XNp_next)

                     {

                        if (XNp_next -> Type () == TiXmlNode::COMMENT)

                           ns_target . v_add_node_in_set (XNp_next);

                        XNp_next = XNp_next -> NextSibling ();

                     }

                     break;

                  case lex_text :

                     XNp_next = XEp_father -> FirstChild ();               

                     while (XNp_next)

                     {

                        if (XNp_next -> Type () == TiXmlNode::TEXT)

                           ns_target . v_add_node_in_set (XNp_next);

                        XNp_next = XNp_next -> NextSibling ();

                     }

                     break;

                  case lex_node :

                     XNp_next = XEp_father -> FirstChild ();               

                     while (XNp_next)

                     {

                        ns_target . v_add_node_in_set (XNp_next);

                        XNp_next = XNp_next -> NextSibling ();

                     }

                     break;

                  default :

                     // an axis name followed by '::'

                     XPATH_THROW error_not_yet ();

                     break;

               }

            }

         }

      }



      if (u_variable)

      {

         // we have predicates to apply

         node_set ns_after_predicate;

         for (u_node = 0; u_node < ns_target .u_get_nb_node_in_set (); u_node++)

         {

            if (! ns_target . o_is_attrib (u_node))

            {

               XEp_elem = ns_target . XNp_get_node_in_set (u_node) -> ToElement ();

               if (XEp_elem)

               {

                  as_action_store . v_set_position (i_pred_store);

                  if (o_check_predicate (XEp_elem, o_by_name))

                     ns_after_predicate . v_add_node_in_set (XEp_elem);

               }

            }

         }

         v_push_node_set (& ns_after_predicate);

      }

      else

         v_push_node_set (& ns_target);

   }

   as_action_store . v_set_position (i_end_store);

}



/// Spec extract :

/// \n A PredicateExpr is evaluated by evaluating the Expr and converting the result to a 

/// boolean. If the result is a number, the result will be converted to true if the number 

/// is equal to the context position and will be converted to false otherwise; if the result 

/// is not a number, then the result will be converted as if by a call to the boolean function. 

/// Thus a location path para[3] is equivalent to para[position()=3].

bool xpath_processor::o_check_predicate (TiXmlElement const * XEp_child, bool o_by_name)

{

   expression_result * erp_top;

   bool o_keep;



   v_set_context (XEp_child, o_by_name);

   v_execute_one (xpath_predicate, false);

   v_set_context (NULL, false);

   erp_top = xs_stack . erp_top ();

   switch (erp_top -> e_type)

   {

      case e_double :

      case e_int :

         o_keep = (erp_top -> i_get_int () == i_xml_cardinality (XEp_child, o_by_name));

         break;

      default :

         o_keep = erp_top -> o_get_bool ();

         break;

   }

   xs_stack . v_pop ();

   return o_keep;

}



/**

Execute an XPath function. The arguments are in normal order in erpp_arg\n

Calls one of the following :

- v_function_ceiling

- v_function_concat

- v_function_contains

- v_function_count

- v_function_false

- v_function_floor

- v_function_last

- v_function_name

- v_function_normalize_space

- v_function_not

- v_function_position

- v_function_starts_with

- v_function_string_length

- v_function_substring

- v_function_sum

- v_function_true

*/

void xpath_processor::v_execute_function (

   TIXML_STRING & S_name,           ///< Function name

   unsigned u_nb_arg,               ///< Nb of arguments

   expression_result ** erpp_arg)   ///< Argument list

{

   if (S_name == "ceiling")

      v_function_ceiling (u_nb_arg, erpp_arg);

   else



   if (S_name == "concat")

      v_function_concat (u_nb_arg, erpp_arg);

   else



   if (S_name == "contains")

      v_function_contains (u_nb_arg, erpp_arg);

   else



   if (S_name == "count")

      v_function_count (u_nb_arg, erpp_arg);

   else



   if (S_name == "false")

      v_function_false (u_nb_arg, erpp_arg);

   else



   if (S_name == "floor")

      v_function_floor (u_nb_arg, erpp_arg);

   else



   if (S_name == "last")

      v_function_last (u_nb_arg, erpp_arg);

   else



   if (S_name == "name")

      v_function_name (u_nb_arg, erpp_arg);

   else



	if (S_name == "normalize-space")

      v_function_normalize_space (u_nb_arg, erpp_arg);

   else



   if (S_name == "not")

      v_function_not (u_nb_arg, erpp_arg);

   else



   if (S_name == "position")

      v_function_position (u_nb_arg, erpp_arg);

   else



   if (S_name == "starts-with")

      v_function_starts_with (u_nb_arg, erpp_arg);

   else



   if (S_name == "string-length")

      v_function_string_length (u_nb_arg, erpp_arg);

   else



   if (S_name == "substring")

      v_function_substring (u_nb_arg, erpp_arg);

   else



   if (S_name == "sum")

      v_function_sum (u_nb_arg, erpp_arg);

   else



   if (S_name == "text")

      v_function_text (u_nb_arg, erpp_arg);

   else



   if (S_name == "true")

      v_function_true (u_nb_arg, erpp_arg);

   else



      XPATH_THROW execution_error (13);

}



/// XPath \b ceiling function

void xpath_processor::v_function_ceiling (

   unsigned u_nb_arg,               ///< Nb of arguments

   expression_result ** erpp_arg)   ///< Argument list

{

   int i_val;



   if (u_nb_arg != 1)

      XPATH_THROW execution_error (14);

   switch (erpp_arg [0] -> e_type)

   {

      case e_int :

      case e_bool :

         i_val = erpp_arg [0] -> i_get_int ();

         break;

      case e_double :

			i_val = (int) ceil (erpp_arg [0] -> d_get_double ());

         break;

      default :

         i_val = 0;

         break;

   }

   v_push_int (i_val, "ceiling");

}



/// XPath \b concat function

void xpath_processor::v_function_concat (

   unsigned u_nb_arg,               ///< Nb of arguments

   expression_result ** erpp_arg)   ///< Argument list

{

   TIXML_STRING S_res;   

   unsigned u_arg;



   if (! u_nb_arg)

      XPATH_THROW execution_error (15);

   S_res = erpp_arg [0] -> S_get_string ();

   for (u_arg = 1; u_arg < u_nb_arg; u_arg++)

      S_res += erpp_arg [u_arg] -> S_get_string () . c_str ();

   v_push_string (S_res);

}



/// XPath \b contains function

void xpath_processor::v_function_contains (

   unsigned u_nb_arg,               ///< Nb of arguments

   expression_result ** erpp_arg)   ///< Argument list

{

   TIXML_STRING S_arg_1, S_arg_2;   



   if (u_nb_arg != 2)

      XPATH_THROW execution_error (16);

   S_arg_1 = erpp_arg [0] -> S_get_string ();

   S_arg_2 = erpp_arg [1] -> S_get_string ();

   v_push_bool (strstr (S_arg_1 . c_str (), S_arg_2 . c_str ()) ? true : false);

}



/// XPath \b count function

void xpath_processor::v_function_count (

   unsigned u_nb_arg,               ///< Nb of arguments

   expression_result ** erpp_arg)   ///< Argument list

{

   int i_res;



   if (! u_nb_arg)

      XPATH_THROW execution_error (17);

   if (erpp_arg [0] -> e_type != e_node_set)

      i_res = 0;

   else

      i_res = erpp_arg [0] -> nsp_get_node_set () -> u_get_nb_node_in_set ();

   v_push_int (i_res, "count result");

}



/// XPath \b false function

void xpath_processor::v_function_false (

   unsigned u_nb_arg,               ///< Nb of arguments

   expression_result ** erpp_arg)   ///< Argument list

{

   if (u_nb_arg)

      XPATH_THROW execution_error (18);

   v_push_bool (false);

}



/// XPath \b floor function

void xpath_processor::v_function_floor (

   unsigned u_nb_arg,               ///< Nb of arguments

   expression_result ** erpp_arg)   ///< Argument list

{

   int i_val;



   if (u_nb_arg != 1)

      XPATH_THROW execution_error (19);

   switch (erpp_arg [0] -> e_type)

   {

      case e_int :

      case e_bool :

         i_val = erpp_arg [0] -> i_get_int ();

         break;

      case e_double :

			i_val = (int) floor (erpp_arg [0] -> d_get_double ());

         break;

      default :

         i_val = 0;

         break;

   }

   v_push_int (i_val, "floor");

}



/// XPath \b last function

void xpath_processor::v_function_last (

   unsigned u_nb_arg,               ///< Nb of arguments

   expression_result ** erpp_arg)   ///< Argument list

{

   const TiXmlElement * XEp_context;



   if (u_nb_arg)

      XPATH_THROW execution_error (20);

   XEp_context = XEp_get_context ();

   if (! XEp_context)

      XPATH_THROW execution_error (21);

   v_push_int (i_xml_family_size (XEp_context), "last()");

}



/// XPath \b name function

void xpath_processor::v_function_name (

   unsigned u_nb_arg,               ///< Nb of arguments

   expression_result ** erpp_arg)   ///< Argument list

{

   TIXML_STRING S_res;   

   node_set * nsp_set;



   if (u_nb_arg != 1)

      XPATH_THROW execution_error (22);

   S_res = "";

   if (erpp_arg [0] -> e_type == e_node_set)

   {

      nsp_set = erpp_arg [0] -> nsp_get_node_set ();

      if (nsp_set -> u_get_nb_node_in_set ())

         if (nsp_set -> o_is_attrib (0))

            S_res = nsp_set -> XAp_get_attribute_in_set (0) -> Name ();

         else

            S_res = nsp_set -> XNp_get_node_in_set (0) -> Value ();

   }

   v_push_string (S_res);

}



/// XPath \b normalize-space function

void xpath_processor::v_function_normalize_space (

   unsigned u_nb_arg,               ///< Nb of arguments

   expression_result ** erpp_arg)   ///< Argument list

{

   TIXML_STRING S_arg, S_res;   



   if (u_nb_arg != 1)

      XPATH_THROW execution_error (23);

   S_arg = erpp_arg [0] -> S_get_string ();

   S_res = S_remove_lead_trail (S_arg . c_str ());

   v_push_string (S_res);

}



/// XPath \b not function

void xpath_processor::v_function_not (

   unsigned u_nb_arg,               ///< Nb of arguments

   expression_result ** erpp_arg)   ///< Argument list

{

   if (u_nb_arg != 1)

      XPATH_THROW execution_error (24);

   v_push_bool (! erpp_arg [0] -> o_get_bool ());

}



/// XPath \b position function

void xpath_processor::v_function_position (

   unsigned u_nb_arg,               ///< Nb of arguments

   expression_result ** erpp_arg)   ///< Argument list

{

   const TiXmlElement * XEp_context;



   if (u_nb_arg)

      XPATH_THROW execution_error (25);

   XEp_context = XEp_get_context ();

   if (! XEp_context)

      XPATH_THROW execution_error (26);

   v_push_int (i_xml_cardinality (XEp_context, o_is_context_by_name), "position()");

}



/// XPath \b starts-with function

void xpath_processor::v_function_starts_with (

   unsigned u_nb_arg,               ///< Nb of arguments

   expression_result ** erpp_arg)   ///< Argument list

{

   TIXML_STRING S_arg_1, S_arg_2;   



   if (u_nb_arg != 2)

      XPATH_THROW execution_error (27);

   S_arg_1 = erpp_arg [0] -> S_get_string ();

   S_arg_2 = erpp_arg [1] -> S_get_string ();

   v_push_bool (! strncmp (S_arg_1 . c_str (), S_arg_2 . c_str (), S_arg_2 . length ()));

}



/// XPath \b sttring-length function

void xpath_processor::v_function_string_length (

   unsigned u_nb_arg,               ///< Nb of arguments

   expression_result ** erpp_arg)   ///< Argument list

{

   TIXML_STRING S_arg;   



   if (u_nb_arg != 1)

      XPATH_THROW execution_error (28);

   S_arg = erpp_arg [0] -> S_get_string ();

   v_push_int (S_arg . length (), "string-length");

}



/**

XPath \b substring function\n

Standard excerpt:\n

The substring function returns the substring of the first argument starting at the position specified in the second argument 

with length specified in the third argument. For example, substring("12345",2,3) returns "234". If the third argument is not 

specified, it returns the substring starting at the position specified in the second argument and continuing to the end of 

the string. For example, substring("12345",2) returns "2345".

*/

void xpath_processor::v_function_substring (

   unsigned u_nb_arg,               ///< Nb of arguments

   expression_result ** erpp_arg)   ///< Argument list

{

   TIXML_STRING S_base, S_ret;   

   int i_length, i_start;

   const char * cp_base;

   char * cp_work;



   if (u_nb_arg != 2 && u_nb_arg != 3)

      XPATH_THROW execution_error (29);

   S_base = erpp_arg [0] -> S_get_string ();

   i_start = erpp_arg [1] -> i_get_int ();

   if (u_nb_arg == 3)

      i_length = erpp_arg [2] -> i_get_int ();

   else

      i_length = 0;



   if (i_start < 1)

      S_ret = "";

   else

   {

      i_start--;

      if (i_start >= (int) S_base . length ())

         S_ret = "";

      else

      {

         cp_base = S_base . c_str () + i_start;

         if (u_nb_arg == 2 || (int) strlen (cp_base) <= i_length)

            S_ret = cp_base;

         else

         {

            cp_work = new char [i_length + 1];

            strncpy (cp_work, cp_base, i_length);

            cp_work [i_length] = 0;

            S_ret = cp_work;

            delete [] cp_work;

         }

      }

   }

   v_push_string (S_ret);

}



/// XPath \b sum function\n

/// Standard exerpt :\n

/// The sum function returns the sum, for each node in the argument node-set, 

/// of the result of converting the string-values of the node to a number.

void xpath_processor::v_function_sum (

   unsigned u_nb_arg,               ///< Nb of arguments

   expression_result ** erpp_arg)   ///< Argument list

{

   int i_sum;

   double d_sum;

   unsigned u_node;

   node_set * nsp_set;

   bool o_out_double;



   if (u_nb_arg != 1)

      XPATH_THROW execution_error (30);

   i_sum = 0;

   d_sum = 0.0;

   o_out_double = false;

   if (erpp_arg [0] -> e_type != e_node_set)

      XPATH_THROW execution_error (31);

   nsp_set = erpp_arg [0] -> nsp_get_node_set ();

   assert (nsp_set);

   if (nsp_set -> u_get_nb_node_in_set () > 1)

      nsp_set -> v_document_sort ();

   for (u_node = 0; u_node < nsp_set -> u_get_nb_node_in_set (); u_node++)

   {

      i_sum += nsp_set -> i_get_value (u_node);

      d_sum += nsp_set -> d_get_value (u_node);

      if (strchr (nsp_set -> S_get_value (u_node) . c_str (), '.'))

         o_out_double = true;

   }

   if (o_out_double)

      v_push_double (d_sum);

   else

      v_push_int (i_sum, "sum()");

}



/// XPath \b text function

void xpath_processor::v_function_text (

   unsigned u_nb_arg,               ///< Nb of arguments

   expression_result ** erpp_arg)   ///< Argument list

{

   const TiXmlElement * XEp_context;

   const TiXmlNode * XNp_child;

   TIXML_STRING S_res;



   if (u_nb_arg)

      XPATH_THROW execution_error (38);

   XEp_context = XEp_get_context ();

   if (! XEp_context)

      XPATH_THROW execution_error (39);

   XNp_child = XEp_context -> FirstChild ();

   while (XNp_child)

   {

      if (XNp_child -> Type () == TiXmlNode::TEXT)

         S_res += XNp_child -> Value ();

      XNp_child = XNp_child -> NextSibling ();

   }

   v_push_string (S_res);

}





/// XPath \b true function

void xpath_processor::v_function_true (

   unsigned u_nb_arg,               ///< Nb of arguments

   expression_result ** erpp_arg)   ///< Argument list

{

   if (u_nb_arg)

      XPATH_THROW execution_error (32);

   v_push_bool (true);

}



/**

This function is for internal use : evaluation of an equality expression \n

Standard excerpt :\n



If both objects to be compared are node-sets, then the comparison will be true if and only if there is a node in the first 

node-set and a node in the second node-set such that the result of performing the comparison on the string-values of the 

two nodes is true. \n



If one object to be compared is a node-set and the other is a number, then the comparison will be true 

if and only if there is a node in the node-set such that the result of performing the comparison on the number to be compared 

and on the result of converting the string-value of that node to a number using the number function is true. \n



If one object to be compared is a node-set and the other is a string, then the comparison will be true if and only if there is a node in 

the node-set such that the result of performing the comparison on the string-value of the node and the other string is true. \n



If one object to be compared is a node-set and the other is a boolean, then the comparison will be true if and only if the 

result of performing the comparison on the boolean and on the result of converting the node-set to a boolean using the boolean 

function is true.\n



When neither object to be compared is a node-set and the operator is = or !=, then the objects are compared by converting them 

to a common type as follows and then comparing them. 

-  If at least one object to be compared is a boolean, then each object to be compared is converted to a boolean as if by 

   applying the boolean function. 

-  Otherwise, if at least one object to be compared is a number, then each object to be compared is converted to a number as 

   if by applying the number function. 

-  Otherwise, both objects to be compared are converted to strings as if by applying the string function. The = comparison will 

   be true if and only if the objects are equal; the != comparison will be true if and only if the objects are not equal. 

-  Numbers are compared for equality according to IEEE 754. 

-  Two booleans are equal if either both are true or both are false. 

-  Two strings are equal if and only if they consist of the same sequence of UCS characters. 

*/

void xpath_processor::v_function_equal (expression_result ** erpp_arg)

{

   bool o_res;



   assert (erpp_arg);

   assert (erpp_arg [0]);

   assert (erpp_arg [1]);

   if (erpp_arg [0] -> e_type == e_node_set)

      if (erpp_arg [1] -> e_type == e_node_set)   

         v_function_equal_2_node (erpp_arg [0], erpp_arg [1]);

      else

         v_function_equal_node_and_other (erpp_arg [0], erpp_arg [1]);

   else

      if (erpp_arg [1] -> e_type == e_node_set)   

         v_function_equal_node_and_other (erpp_arg [1], erpp_arg [0]);

      else

      {

         // none are node sets : alternate decision table on bools, numbers and strings

         if (erpp_arg [0] -> e_type == e_bool || erpp_arg [1] -> e_type == e_bool)

            o_res = erpp_arg [0] -> o_get_bool () == erpp_arg [1] -> o_get_bool ();

         else

            if (erpp_arg [0] -> e_type == e_int || erpp_arg [1] -> e_type == e_int ||

                  erpp_arg [0] -> e_type == e_double || erpp_arg [1] -> e_type == e_double)

               o_res = erpp_arg [0] -> d_get_double () == erpp_arg [1] -> d_get_double ();

            else

               o_res = erpp_arg [0] -> S_get_string () == erpp_arg [1] -> S_get_string ();

         v_push_bool (o_res);

      }

}



/// This function is for internal use : evaluation of a non-equality expression

void xpath_processor::v_function_not_equal (expression_result ** erpp_arg)

{

   v_function_equal (erpp_arg);

   v_push_bool (! o_pop_bool ());

}



/**

Utility function that evaluates the equality between a node set and a non-node set\n



Standard excerpt :\n

If one object to be compared is a node-set and the other is a number, then the comparison will be true 

if and only if there is a node in the node-set such that the result of performing the comparison on the number to be compared 

and on the result of converting the string-value of that node to a number using the number function is true. \n



If one object to be compared is a node-set and the other is a string, then the comparison will be true if and only if there is a node in 

the node-set such that the result of performing the comparison on the string-value of the node and the other string is true. \n



If one object to be compared is a node-set and the other is a boolean, then the comparison will be true if and only if the 

result of performing the comparison on the boolean and on the result of converting the node-set to a boolean using the boolean 

function is true.\n

*/

void xpath_processor::v_function_equal_node_and_other (expression_result * erp_node_set, expression_result * erp_non)

{

   bool o_res;

   node_set * nsp_ptr;

   unsigned u_node;



   o_res = false;

   nsp_ptr = erp_node_set -> nsp_get_node_set ();

   switch (erp_non -> e_type)

   {

      case e_bool :

         o_res = erp_non -> o_get_bool () ==  erp_node_set -> o_get_bool ();

         break;

      case e_int :

      case e_double :

         for (u_node = 0; u_node < nsp_ptr -> u_get_nb_node_in_set (); u_node++)

            if (nsp_ptr -> i_get_value (u_node) == erp_non -> i_get_int ())

               o_res = true;

         break;

      case e_string :

         for (u_node = 0; u_node < nsp_ptr -> u_get_nb_node_in_set (); u_node++)

            if (nsp_ptr -> S_get_value (u_node) == erp_non -> S_get_string ())

               o_res = true;

         break;

      default :

         // this case should have been excluded before our call

         assert (false);

         break;

   }

   v_push_bool (o_res);

}



/// \todo : Implement this function. We don't compare 2 node sets yet

void xpath_processor::v_function_equal_2_node (expression_result * erp_node_set_, expression_result * erp_node_set_2)

{

   XPATH_THROW error_not_yet ();

}



/// Union function

void xpath_processor::v_function_union (node_set & ns_1, node_set & ns_2)

{

   node_set ns_target;

   unsigned u_node;

   const TiXmlBase * XBp_base;



   ns_target = ns_1;

   for (u_node = 0; u_node < ns_2 . u_get_nb_node_in_set (); u_node++)

   {

      XBp_base = ns_2 . XBp_get_base_in_set (u_node);

      ns_target . v_add_base_in_set (XBp_base, ns_2 . o_is_attrib (u_node));

   }

   v_push_node_set (& ns_target);

}



/// XPath <b> + </b> function

void xpath_processor::v_function_plus (expression_result ** erpp_arg)

{

   assert (erpp_arg);

   assert (erpp_arg [0]);

   assert (erpp_arg [1]);

   if (erpp_arg [0] -> e_type == e_double || erpp_arg [1] -> e_type == e_double)

      v_push_double (erpp_arg [0] -> d_get_double () + erpp_arg [1] -> d_get_double ());

   else

      v_push_int (erpp_arg [0] -> i_get_int () + erpp_arg [1] -> i_get_int (), "+");

}



/// XPath <b> - </b> function

void xpath_processor::v_function_minus (expression_result ** erpp_arg)

{

   assert (erpp_arg);

   assert (erpp_arg [0]);

   assert (erpp_arg [1]);

   if (erpp_arg [0] -> e_type == e_double || erpp_arg [1] -> e_type == e_double)

      v_push_double (erpp_arg [0] -> d_get_double () - erpp_arg [1] -> d_get_double ());

   else

      v_push_int (erpp_arg [0] -> i_get_int () - erpp_arg [1] -> i_get_int (), "-");

}



/// XPath \b or function

void xpath_processor::v_function_or (expression_result ** erpp_arg)

{

   assert (erpp_arg);

   assert (erpp_arg [0]);

   assert (erpp_arg [1]);

   v_push_bool (erpp_arg [0] -> o_get_bool () || erpp_arg [1] -> o_get_bool ());

}



/// XPath \b and function

void xpath_processor::v_function_and (expression_result ** erpp_arg)

{

   assert (erpp_arg);

   assert (erpp_arg [0]);

   assert (erpp_arg [1]);

   v_push_bool (erpp_arg [0] -> o_get_bool () && erpp_arg [1] -> o_get_bool ());

}



/// XPath relational comparison function 

void xpath_processor::v_function_relational (expression_result ** erpp_arg, unsigned u_sub)

{

   bool o_res;



   assert (erpp_arg);

   assert (erpp_arg [0]);

   assert (erpp_arg [1]);



   if (erpp_arg [0] -> e_type == e_double || erpp_arg [1] -> e_type == e_double)

   {  

      double d_arg_1, d_arg_2;



      d_arg_1 = erpp_arg [0] -> d_get_double ();

      d_arg_2 = erpp_arg [1] -> d_get_double ();

      switch (u_sub)

      {

         case xpath_relational_expr_lt :

            o_res = d_arg_1 < d_arg_2;

            break;

         case xpath_relational_expr_gt :

            o_res = d_arg_1 > d_arg_2;

            break;

         case xpath_relational_expr_lte :

            o_res = d_arg_1 <= d_arg_2;

            break;

         case xpath_relational_expr_gte :

            o_res = d_arg_1 >= d_arg_2;

            break;

         default :

            assert (false);

      }

   }

   else

   {

      int i_arg_1, i_arg_2;



      i_arg_1 = erpp_arg [0] -> i_get_int ();

      i_arg_2 = erpp_arg [1] -> i_get_int ();

      switch (u_sub)

      {

         case xpath_relational_expr_lt :

            o_res = i_arg_1 < i_arg_2;

            break;

         case xpath_relational_expr_gt :

            o_res = i_arg_1 > i_arg_2;

            break;

         case xpath_relational_expr_lte :

            o_res = i_arg_1 <= i_arg_2;

            break;

         case xpath_relational_expr_gte :

            o_res = i_arg_1 >= i_arg_2;

            break;

         default :

            assert (false);

      }

   }

   v_push_bool (o_res);

}



/// XPath <b> * </b> function (arithmetic)

void xpath_processor::v_function_mult (expression_result ** erpp_arg, unsigned u_sub)

{

   assert (erpp_arg);

   assert (erpp_arg [0]);

   assert (erpp_arg [1]);



   if (erpp_arg [0] -> e_type == e_double || erpp_arg [1] -> e_type == e_double || u_sub == xpath_multiplicative_expr_div)

   {  

      double d_arg_1, d_arg_2, d_res;



      d_arg_1 = erpp_arg [0] -> d_get_double ();

      d_arg_2 = erpp_arg [1] -> d_get_double ();

      switch (u_sub)

      {

         case xpath_multiplicative_expr_star :

            d_res = d_arg_1 * d_arg_2;

            break;

         case xpath_multiplicative_expr_div :

            if (fabs (d_arg_2) < 1.0e-6)

               XPATH_THROW execution_error (33);

            d_res = d_arg_1 / d_arg_2;

            break;

         case xpath_multiplicative_expr_mod :

            d_res = (int) d_arg_1 % (int) d_arg_2;

            break;

         default :

            assert (false);

      }

      v_push_double (d_res);

   }

   else

   {

      int i_arg_1, i_arg_2, i_res;



      i_arg_1 = erpp_arg [0] -> i_get_int ();

      i_arg_2 = erpp_arg [1] -> i_get_int ();

      switch (u_sub)

      {

         case xpath_multiplicative_expr_star :

            i_res = i_arg_1 * i_arg_2;

            break;

         case xpath_multiplicative_expr_mod :

            i_res = i_arg_1 % i_arg_2;

            break;

         default :

            assert (false);

      }

      v_push_int (i_res, "*");

   }

}





/// This function, because it only operates on one argument retrieves it himself from the stack

/// \n It computes the mathematical opposite

void xpath_processor::v_function_opposite ()

{

   expression_result er_arg;



   er_arg = * xs_stack . erp_top ();

   xs_stack . v_pop ();

   switch (er_arg . e_type)

   {

      case e_double :

         v_push_double (-1.0 * er_arg . d_get_double ());

         break;

      default :

         v_push_int (-1 * er_arg . i_get_int (), "unary -");

         break;

   }

}



/// Order the tree : assign an integer to all nodes based on the document order

/// \n Right now, tinyxml doesn't have a user value associated to attributes ... arrrgh.

/// We order everything else, and will have to compute attributes order on demand

void xpath_processor::v_order_tree ()

{

   int i_dummy;



   i_dummy = 1;

   v_order_recurs (XEp_root, i_dummy);

}



/// Recursive ordering of an XML tree according to XPath's document order.

/// \n The result is stored as an integer in the void * pointer managed by TiXmlNode::SetUserData / TiXmlNode::GetUserData

void xpath_processor::v_order_recurs (TiXmlNode const * Np_base, int & i_current)

{

   TiXmlNode const * Np_child;



   const_cast<TiXmlNode*>(Np_base) -> SetUserData ((void *) i_current++);

   Np_child = Np_base -> FirstChild ();

   while (Np_child)

   {

      v_order_recurs (Np_child, i_current);

      Np_child = Np_child -> NextSibling ();

   }

}



/// Set the current context node for predicate evaluations

void xpath_processor::v_set_context (

   TiXmlElement const * XEp_in,     ///< Context node

   bool o_by_name)            ///< true if the current node search is by name, false if it's a *

{

   XEp_context = XEp_in; 

   o_is_context_by_name = o_by_name;

}

