<?php
/*
 *
 * Copyright (c) Paid, Inc.  All rights reserved.
 *
 * $Id: ShipRateParserXML.inc,v 1.5 2005/05/25 12:54:36 jmartin Exp $
 *
 * This program is part of the Paid Shipping Rate API toolkit for PHP and is responsible for parsing the XML
 * that is returned by the rate engine.
 *
 */

/**
 * SAX Parser
 *
 * @author "Victor Didovicher" <victord@paid.com>
 * @date 2005.05.17
 * @version 1.0
 * @copyright (c) Paid, Inc.  All rights reserved.
 */
class ShipRateParserXML
{
   var $tag;
   var $parser;
   var $dom;
   var $_current_tag;
   var $_err_state;
   var $_error_cnt;

   /**
    * Default Constructor
    * @access public
    */
   function ShipRateParserXML() {
      $this->_init();
   }

   /**
    * Initialize the parser object
    * @return bool 
    * @access private
    */
   function _init()
   {
      $this->_current_tag = false;
      $this->_err_state   = false;
      $this->_error_cnt   = 0;
      $this->dom          = array();

      if (isset($this->parser) && is_resource($this->parser)) {
         @xml_parser_free($this->parser);
      }
      $this->parser = @xml_parser_create();
      if (is_resource($this->parser)) {
         xml_set_object($this->parser, $this);
         xml_set_element_handler($this->parser, 'startHandler', 'endHandler');
         xml_set_character_data_handler($this->parser, 'cdataHandler');
         xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, false);
         return true;
      }
      return false;
   }

   /**
    * XML Element Handler (start)
    * @param Object xml parser
    * @param String Tag
    * @param Array Attribute Array
    * @return none
    * @access public
    */
   function parse($string)
   {
      if (isset($this->parser) && is_resource($this->parser)) {
         xml_parse($this->parser, $string, true);
         xml_parser_free($this->parser);
         $this->parser = null;
         return $this->dom;
      }
      return false;
   }

   ### ----------------------------------------------------------------------
   ### Methods to Override - OVERRIDE ONLY METHODS -
   ### ----------------------------------------------------------------------

   /**
    * XML Element Handler (start)
    * @param Object xml parser
    * @param String Tag
    * @param Array Attribute Array
    * @return none
    * @access public
    */
   function startHandler($parser, $tag, $attribs)
   {
      $this->_current_tag = $tag;
      $attribs = array_change_key_case($attribs,CASE_LOWER);
      switch ($tag) {
         case 'ErrorList': xml_set_character_data_handler($this->parser,'_cdataHandler_Error');
                           $this->dom = array();
                           $this->dom['ErrorList'] = array();
                           $this->_err_state = true;
                           break;
         case 'Error':     $this->dom['ErrorList'][$this->_error_cnt] = array();
                           break;
      }
   }

   /**
    * XML Element Handler (end)
    * @param object $parser xml parser
    * @param string $tag string that will be provided by the parser
    * @return none
    * @access public
    */
   function endHandler($parser, $tag)
   {
      $this->_current_tag = null; // <<<<< WATCH THIS, cdataHandlers called 3 times: tag space and \n
      switch ($tag) {
         case 'ErrorList': break;
         case 'Error':     $this->_error_cnt++; break;
      }  
   }

   ### ----------------------------------------------------------------------
   ### Default Character Data Handler :: Handle ErrorList
   ### ----------------------------------------------------------------------

   /**
    * XML Character Data Handler
    * @param object $parser xml parser
    * @param string $data string that will be provided by the parser
    * @return none
    * @access public
    */
   function cdataHandler($parser, $data)
   { }

   /**
    * XML Character Data Handler
    * @param object $parser xml parser
    * @param string $data string that will be provided by the parser
    * @return none
    * @access public
    */
   function _cdataHandler_Error($parser, $val)
   {
      static $map;
      if (!$this->_err_state || ($this->_current_tag == null) || empty($this->_current_tag)) return;
      if (!isset($map)) {
         $map = array(
            'code'     => 'Code',
            'message'  => 'Message',
            'severity' => 'Severity');
      }
      $this->cdataAdapter($val, $map, $this->_error_cnt, $this->dom['ErrorList']);
   }

   ### ----------------------------------------------------------------------
   ### Support Methods
   ### ----------------------------------------------------------------------

   /**
    * Used by cdataHandler methods to store the data into the object container in the proper position
    * @param    string      text just parsed from XML
    * @param    assocArray  Element list with names mapped
    * @param    string      Node name that current tag will be place in the container, blank if
    * @param    assocArray(ref)   reference point of $this->_res where the node will reside
    * @return   void
    */
   function cdataAdapter($data, $map, $nodeName, & $container)
   {
      $str = strtolower($this->_current_tag);
      if (isset($map[$str])) {

         // Get a reference into the container where data should be placed
         if ( is_null($nodeName) ) {
            $ref = & $container;
         } else {
            // Double check to see if the node has been created yet
            if (! isset($container[$nodeName])) $container[$nodeName] = array();
            $ref = & $container[$nodeName];
         }

         // Get the element name from the map
         $key = $map[$str];

         if (isset($ref[$key]))
            $ref[$key] .= $data;
         else
            $ref[$key] = $data;
      }
   }
}
?>