From 5ffbc365cbe050403182d9a6a8d93c4569339a80 Mon Sep 17 00:00:00 2001
From: tomas <tomas@cfc7b370-4200-0410-a6e3-cb6bdb053afe>
Date: Wed, 2 Mar 2005 02:26:12 +0000
Subject: [PATCH] Major changes in node children validation. + comments added.

---
 .../modules/storageServer/var/Validator.php   | 248 ++++++++++++++----
 1 file changed, 192 insertions(+), 56 deletions(-)

diff --git a/livesupport/modules/storageServer/var/Validator.php b/livesupport/modules/storageServer/var/Validator.php
index a0d1f59b3..a0ffdf8de 100644
--- a/livesupport/modules/storageServer/var/Validator.php
+++ b/livesupport/modules/storageServer/var/Validator.php
@@ -23,119 +23,242 @@
  
  
     Author   : $Author: tomas $
-    Version  : $Revision: 1.2 $
+    Version  : $Revision: 1.3 $
     Location : $Source: /home/paul/cvs2svn-livesupport/newcvsrepo/livesupport/modules/storageServer/var/Validator.php,v $
 
 ------------------------------------------------------------------------------*/
 define('VAL_ROOT', 110);
-define('VAL_NOREQ', 111);
+define('VAL_NOREQE', 111);
 define('VAL_NOONEOF', 112);
 define('VAL_UNKNOWNE', 113);
 define('VAL_UNKNOWNA', 114);
 define('VAL_NOTDEF', 115);
+define('VAL_UNEXPONEOF', 116);
+define('VAL_FORMAT', 117);
+define('VAL_CONTENT', 118);
+define('VAL_NOREQA', 119);
+define('VAL_ATTRIB', 120);
 
 #require_once "";
 
 /**
  *  Simple XML validator against structure stored in PHP hash-array hierarchy.
  *
- *  It should be replaced by XML schema validation in the future.
+ *  Basic format files:
+ *  <ul>
+ *      <li>audioClipFormat.php</li>
+ *      <li>webstreamFormat.php</li>
+ *      <li>playlistFormat.php</li>
+ *  </ul>
+ *  It probably should be replaced by XML schema validation in the future.
  */
 class Validator{
     /**
-     *
+     *  string - format type of validated document
      */
-    var $format = '';
+    var $formatType = NULL;
     /**
-     *
+     *  
      */
-    var $data = '';
+    var $format = NULL;
     /**
-     *
+     *  string - gunid of validated file for identification in mass input
      */
-    var $anode = '';
+    var $gunid = NULL;
     /**
+     *  Constructor
      *
+     *  @param formatType string - format type of validated document
+     *  @param gunid string - gunid of validated file for identification
+     *          in mass input
      */
-    function Validator($format)
+    function Validator($formatType, $gunid)
     {
-        $this->format = $format;
+        $this->formatType   = $formatType;
+        $this->gunid        = $gunid;
+        $formats = array(
+            'audioclip' => "audioClipFormat",
+            'playlist'  => "playlistFormat",
+            'webstream' => "webstreamFormat",
+        );
+        if(!isset($formats[$formatType])) return $this->_err(VAL_FORMAT);
+        $format = $formats[$formatType];
+        $formatFile = dirname(__FILE__)."/$format.php";
+        if(!file_exists($formatFile)) return $this->_err(VAL_FORMAT);
+        require $formatFile;
+        $this->format = $$format;
     }
 
     /**
+     *  Validate document - only wrapper for validateNode method
      *
+     *  @param data object, validated object tree
+     *  @return TRUE or PEAR::error
      */
     function validate(&$data)
     {
-        $this->data =& $data;
         $r = $this->validateNode($data, $this->format['_root']);
         return $r;
     }
 
     /**
+     *  Validation of one element node from object tree
      *
+     *  @param node object - validated node
+     *  @param fname string - aktual name in format structur
+     *  @return TRUE or PEAR::error
      */
     function validateNode(&$node, $fname)
     {
         $dname = strtolower(($node->ns? $node->ns.":" : '').$node->name);
         $format =& $this->format;
         if(DEBUG) echo"\nVAL::validateNode: 1 $dname/$fname\n";
-        if($dname != $fname) return $this->_err(VAL_ROOT);
+        // check root node name:
+        if($dname != $fname) return $this->_err(VAL_ROOT, $fname);
+        // check if this element is defined in format:
         if(!isset($format[$fname])) return $this->_err(VAL_NOTDEF, $fname);
-        $attrs = array();
-        foreach($node->attrs as $i=>$attr){
-            $attrs[$attr->name] =& $node->attrs[$i];
-            $listr = $this->isListedAs($fname, $attr->name, 'attrs', 'required');
-            $listi = $this->isListedAs($fname, $attr->name, 'attrs', 'implied');
-            $listn = $this->isListedAs($fname, $attr->name, 'attrs', 'normal');
-            if($listr===FALSE && $listi===FALSE && $listn===FALSE)
-                return $this->_err(VAL_UNKNOWNA, $attr->name);
+        // check element content
+        if(isset($format[$fname]['regexp'])){
+            // echo "XXX {$format[$fname]['regexp']} / ".$node->content."\n";
+            if(!preg_match("|{$format[$fname]['regexp']}|", $node->content))
+                return $this->_err(VAL_CONTENT, $fname);
         }
+        // validate attributes:
+        $ra = $this->validateAttributes($node, $fname);
+        if(PEAR::isError($ra)) return $ra;
+        // validate children:
+        $r = $this->validateChildren($node, $fname);
+        if(PEAR::isError($r)) return $r;
+        return TRUE;
+    }
+
+    /**
+     *  Validation of attributes
+     *
+     *  @param node object - validated node
+     *  @param fname string - aktual name in format structure
+     *  @return TRUE or PEAR::error
+     */
+    function validateAttributes(&$node, $fname)
+    {
+        $format =& $this->format;
+        $attrs = array();
+        // check if all attrs are permitted here:
+        foreach($node->attrs as $i=>$attr){
+            $aname = strtolower(($attr->ns? $attr->ns.":" : '').$attr->name);
+            $attrs[$aname] =& $node->attrs[$i];
+            if(!$this->isAttrInFormat($fname, $aname))
+                return $this->_err(VAL_UNKNOWNA, $aname);
+            // check attribute format
+            // echo "XXA $aname\n";
+            if(isset($format[$aname]['regexp'])){
+                // echo "XAR {$format[$fname]['regexp']} / ".$node->content."\n";
+                if(!preg_match("|{$format[$aname]['regexp']}|", $attr->val))
+                    return $this->_err(VAL_ATTRIB, $aname);
+            }
+        }
+        // check if all required attrs are here:
         if(isset($format[$fname]['attrs'])){
             $fattrs =& $format[$fname]['attrs'];
             if(isset($fattrs['required'])){
                 foreach($fattrs['required'] as $i=>$attr){
-                    if(!isset($childs[$attr])) return $this->_err(VAL_NOREQ, $attr);
+                    if(!isset($attrs[$attr]))
+                        return $this->_err(VAL_NOREQA, $attr);
                 }
             }
         }
-        $childs = array();
-        foreach($node->children as $i=>$ch){
-            $chname = strtolower(($ch->ns? $ch->ns.":" : '').$ch->name);
-            $childs[$chname] =& $node->children[$i];
-            $listo = $this->isListedAs($fname, $chname, 'childs', 'optional');
-            $listr = $this->isListedAs($fname, $chname, 'childs', 'required');
-            $list1 = $this->isListedAs($fname, $chname, 'childs', 'oneof');
-            if($listo===FALSE && $listr===FALSE && $list1===FALSE)
-                return $this->_err(VAL_UNKNOWNE, $chname);
-        }
-        //var_dump($childs);
-        if(isset($format[$fname]['childs'])){
-            $fchilds =& $format[$fname]['childs'];
-            if(isset($fchilds['required'])){
-                foreach($fchilds['required'] as $i=>$ch){
-                    if(!isset($childs[$ch])) return $this->_err(VAL_NOREQ, $ch);
-                }
-            }
-            if(isset($fchilds['oneof'])){
-                $one = FALSE;
-                foreach($fchilds['oneof'] as $i=>$ch){
-                    if(isset($childs[$ch])) $one = TRUE;
-                }
-                if(!$one) return $this->_err(VAL_NOONEOF);
-            }
-        }
-        foreach($childs as $chname=>$ch){
-            $r = $this->validateNode($childs[$chname], $chname);
-            if(PEAR::isError($r)) return $r;
-        }
         return TRUE;
     }
 
     /**
+     *  Validation children nodes
      *
+     *  @param node object - validated node
+     *  @param fname string - aktual name in format structure
+     *  @return TRUE or PEAR::error
      */
-    function isListedAs($fname, $chname, $nType='childs', $reqType='required')
+    function validateChildren(&$node, $fname)
+    {
+        $format =& $this->format;
+        $childs = array();
+        // check if all children are permitted here:
+        foreach($node->children as $i=>$ch){
+            $chname = strtolower(($ch->ns? $ch->ns.":" : '').$ch->name);
+            // echo "XXE $chname\n";
+            if(!$this->isChildInFormat($fname, $chname))
+                return $this->_err(VAL_UNKNOWNE, $chname);
+            // call children recursive:
+            $r = $this->validateNode($node->children[$i], $chname);
+            if(PEAR::isError($r)) return $r;
+            $childs[$chname] = TRUE;
+        }
+        // check if all required children are here:
+        if(isset($format[$fname]['childs'])){
+            $fchilds =& $format[$fname]['childs'];
+            if(isset($fchilds['required'])){
+                foreach($fchilds['required'] as $i=>$ch){
+                    if(!isset($childs[$ch])) return $this->_err(VAL_NOREQE, $ch);
+                }
+            }
+            // required one from set
+            if(isset($fchilds['oneof'])){
+                $one = FALSE;
+                foreach($fchilds['oneof'] as $i=>$ch){
+                    if(isset($childs[$ch])){
+                        if($one) return $this->_err(
+                            VAL_UNEXPONEOF, "$ch in $fname");
+                        $one = TRUE;
+                    }
+                }
+                if(!$one) return $this->_err(VAL_NOONEOF);
+            }
+        }
+        return TRUE;
+    }
+
+    /**
+     *  Test if child is presented in format structure
+     *
+     *  @param fname string - node name in format structure
+     *  @param chname string - child node name
+     *  @return boolean
+     */
+    function isChildInFormat($fname, $chname)
+    {
+        $listo = $this->isInFormatAs($fname, $chname, 'childs', 'optional');
+        $listr = $this->isInFormatAs($fname, $chname, 'childs', 'required');
+        $list1 = $this->isInFormatAs($fname, $chname, 'childs', 'oneof');
+        return ($listo!==FALSE || $listr!==FALSE || $list1!==FALSE);
+    }
+
+    /**
+     *  Test if attribute is presented in format structure
+     *
+     *  @param fname string - node name in format structure
+     *  @param aname string - attribute name
+     *  @return boolean
+     */
+    function isAttrInFormat($fname, $aname)
+    {
+        $listr = $this->isInFormatAs($fname, $aname, 'attrs', 'required');
+        $listi = $this->isInFormatAs($fname, $aname, 'attrs', 'implied');
+        $listn = $this->isInFormatAs($fname, $aname, 'attrs', 'normal');
+        return ($listr!==FALSE || $listi!==FALSE || $listn!==FALSE);
+    }
+
+    /**
+     *  Check if node/attribute is presented in format structure
+     *
+     *  @param fname string - node name in format structure
+     *  @param chname string - node/attribute name
+     *  @param nType string - 'childs' | 'attrs'
+     *  @param reqType string - <ul>
+     *          <li>for elements: 'required' | 'optional' | 'oneof'</li>
+     *          <li>for attributes: 'required' | 'implied' | 'normal'</li>
+     *      </ul>
+     *  @return boolean/int (index int format array returned if found)
+     */
+    function isInFormatAs($fname, $chname, $nType='childs', $reqType='required')
     {
         $format =& $this->format;
         $listed = (
@@ -147,25 +270,38 @@ class Validator{
     }
 
     /**
+     *  Error exception generator
      *
+     *  @param errno int - erron code
+     *  @param par string - optional string for more descriptive  error messages
+     *  @return PEAR::error
      */
     function _err($errno, $par='')
     {
         $msg = array(
             110=>'Wrong root element',
-            111=>'Required object missing',
-            112=>'One-of object missing',
+            111=>'Required element missing',
+            112=>'One-of element missing',
             113=>'Unknown element',
             114=>'Unknown attribute',
             115=>'Not defined',
+            116=>'Unexpected second object from one-of set',
+            117=>'Unknown format',
+            118=>'Invalid content',
+            119=>'Required attribute missing',
+            120=>'Invalid attribute format',
         );
         return PEAR::raiseError(
-            "Validator: {$msg[$errno]} #$errno ($par)"
+            "Validator: {$msg[$errno]} #$errno ($par, gunid={$this->gunid})",
+            $errno
         );
     }
 
     /**
      *
+     *
+     *  @param
+     *  @return
      * /
     function ()
     {