Search:

# CWIS Developers Documentation

- Main Page
- Classes
- Files

- Search for |

- File List
- File Members

# XMLParser.php

Go to the documentation of this file.

    00001 <?PHP
    00002 #
    00003 #   FILE:  SPT--XMLParser.php
    00004 #
    00005 #   METHODS PROVIDED:
    00006 #       XMLParser()
    00007 #           - constructor
    00008 #       SomeMethod($SomeParameter, $AnotherParameter)
    00009 #           - short description of method
    00010 #
    00011 #   AUTHOR:  Edward Almasy
    00012 #
    00013 #   Part of the Scout Portal Toolkit
    00014 #   Copyright 2005 Internet Scout Project
    00015 #   http://scout.wisc.edu
    00016 #
    00017
    00018 class XMLParser {
    00019
    00020     # ---- PUBLIC INTERFACE --------------------------------------------------
    00021
    00022     # object constructor
    00023     function XMLParser()
    00024     {
    00025         # set default debug output level
    00026         $this->DebugLevel = 0;
    00027
    00028         # create XML parser and tell it about our methods
    00029         $this->Parser = xml_parser_create();
    00030         xml_set_object($this->Parser, $this);
    00031         xml_set_element_handler($this->Parser, "OpenTag", "CloseTag");
    00032         xml_set_character_data_handler($this->Parser, "ReceiveData");
    00033
    00034         # initialize tag storage arrays
    00035         $this->TagNames = array();
    00036         $this->TagAttribs = array();
    00037         $this->TagData = array();
    00038         $this->TagParents = array();
    00039
    00040         # initialize indexes for parsing and retrieving data
    00041         $this->CurrentParseIndex = -1;
    00042         $this->CurrentSeekIndex = -1;
    00043         $this->NameKeyCache = array();
    00044     }
    00045
    00046     # parse text stream and store result
    00047     function ParseText($Text, $LastTextToParse = TRUE)
    00048     {
    00049         # pass text to PHP XML parser
    00050         xml_parse($this->Parser, $Text, $LastTextToParse);
    00051     }
    00052
    00053     # move current tag pointer to specified item (returns NULL on failure)
    00054     function SeekTo()    # (args may be tag names or indexes)
    00055     {
    00056         # perform seek based on arguments passed by caller
    00057         $SeekResult = $this->PerformSeek(func_get_args(), TRUE);
    00058
    00059         # if seek successful
    00060         if ($SeekResult !== NULL)
    00061         {
    00062             # retrieve item count at seek location
    00063             $ItemCount = count($this->CurrentItemList);
    00064         }
    00065         else
    00066         {
    00067             # return null value to indicate that seek failed
    00068             $ItemCount = NULL;
    00069         }
    00070
    00071         # return count of tags found at requested location
    00072         if ($this->DebugLevel > 0)
    00073         {
    00074             print("XMLParser->SeekTo(");
    00075             $Sep = "";
    00076             $DbugArgList = "";
    00077             foreach (func_get_args() as $Arg)
    00078             {
    00079                 $DbugArgList .= $Sep."\"".$Arg."\"";
    00080                 $Sep = ", ";
    00081             }
    00082             print($DbugArgList.") returned ".intval($ItemCount)." items starting at index ".$this->CurrentSeekIndex."\n");
    00083         }
    00084         return $ItemCount;
    00085     }
    00086
    00087     # move seek pointer up one level (returns tag name or NULL if no parent)
    00088     function SeekToParent()
    00089     {
    00090         # if we are not at the root of the tree
    00091         if ($this->CurrentSeekIndex >= 0)
    00092         {
    00093             # move up one level in tree
    00094             $this->CurrentSeekIndex = $this->TagParents[$this->CurrentSeekIndex];
    00095
    00096             # clear item list
    00097             unset($this->CurrentItemList);
    00098
    00099             # return name of new tag to caller
    00100             $Result = $this->TagNames[$this->CurrentSeekIndex];
    00101         }
    00102         else
    00103         {
    00104             # return NULL indicating that no parent was found
    00105             $Result = NULL;
    00106         }
    00107
    00108         # return result to caller
    00109         if ($this->DebugLevel > 0) {  print("XMLParser->SeekToParent() returned ".$Result."<br>\n");  }
    00110         return $Result;
    00111     }
    00112
    00113     # move seek pointer to first child of current tag (returns tag name or NULL if no children)
    00114     function SeekToChild($ChildIndex = 0)
    00115     {
    00116         # look for tags with current tag as parent
    00117         $ChildTags = array_keys($this->TagParents, $this->CurrentSeekIndex);
    00118
    00119         # if child tag was found with requested index
    00120         if (isset($ChildTags[$ChildIndex]))
    00121         {
    00122             # set current seek index to child
    00123             $this->CurrentSeekIndex = $ChildTags[$ChildIndex];
    00124
    00125             # clear item list info
    00126             unset($this->CurrentItemList);
    00127
    00128             # return name of new tag to caller
    00129             $Result = $this->TagNames[$this->CurrentSeekIndex];
    00130         }
    00131         else
    00132         {
    00133             # return NULL indicating that no children were found
    00134             $Result = NULL;
    00135         }
    00136
    00137         # return result to caller
    00138         if ($this->DebugLevel > 0) {  print("XMLParser->SeekToChild() returned ".$Result."<br>\n");  }
    00139         return $Result;
    00140     }
    00141
    00142     # move seek pointer to root of tree
    00143     function SeekToRoot()
    00144     {
    00145         $this->CurrentSeekIndex = -1;
    00146     }
    00147
    00148     # move to next tag at current level (returns tag name or NULL if no next)
    00149     function NextTag()
    00150     {
    00151         # get list of tags with same parent as this tag
    00152         $LevelTags = array_keys($this->TagParents,
    00153                 $this->TagParents[$this->CurrentSeekIndex]);
    00154
    00155         # find position of next tag in list
    00156         $NextTagPosition = array_search($this->CurrentSeekIndex, $LevelTags) + 1;
    00157
    00158         # if there is a next tag
    00159         if (count($LevelTags) > $NextTagPosition)
    00160         {
    00161             # move seek pointer to next tag at this level
    00162             $this->CurrentSeekIndex = $LevelTags[$NextTagPosition];
    00163
    00164             # rebuild item list
    00165
    00166             # return name of tag at new position to caller
    00167             return $this->TagNames[$this->CurrentSeekIndex];
    00168         }
    00169         else
    00170         {
    00171             # return NULL to caller to indicate no next tag
    00172             return NULL;
    00173         }
    00174     }
    00175
    00176     # move to next instance of current tag (returns index or NULL if no next)
    00177     function NextItem()
    00178     {
    00179         # set up item list if necessary
    00180         if (!isset($this->CurrentItemList)) {  $this->RebuildItemList();  }
    00181
    00182         # if there are items left to move to
    00183         if ($this->CurrentItemIndex < ($this->CurrentItemCount - 1))
    00184         {
    00185             # move item pointer to next item
    00186             $this->CurrentItemIndex++;
    00187
    00188             # set current seek pointer to next item
    00189             $this->CurrentSeekIndex =
    00190                     $this->CurrentItemList[$this->CurrentItemIndex];
    00191
    00192             # return new item index to caller
    00193             $Result = $this->CurrentItemIndex;
    00194         }
    00195         else
    00196         {
    00197             # return NULL value to caller to indicate failure
    00198             $Result = NULL;
    00199         }
    00200
    00201         # return result to caller
    00202         return $Result;
    00203     }
    00204
    00205     # move to previous instance of current tag (returns index or NULL on fail)
    00206     function PreviousItem()
    00207     {
    00208         # set up item list if necessary
    00209         if (!isset($this->CurrentItemList)) {  $this->RebuildItemList();  }
    00210
    00211         # if we are not at the first item
    00212         if ($this->CurrentItemIndex > 0)
    00213         {
    00214             # move item pointer to previous item
    00215             $this->CurrentItemIndex--;
    00216
    00217             # set current seek pointer to next item
    00218             $this->CurrentSeekIndex =
    00219                     $this->CurrentItemList[$this->CurrentItemIndex];
    00220
    00221             # return new item index to caller
    00222             return $this->CurrentItemIndex;
    00223         }
    00224         else
    00225         {
    00226             # return NULL value to caller to indicate failure
    00227             return NULL;
    00228         }
    00229     }
    00230
    00231     # retrieve tag name from current seek point
    00232     function GetTagName()
    00233     {
    00234         if (isset($this->TagNames[$this->CurrentSeekIndex]))
    00235         {
    00236             return $this->TagNames[$this->CurrentSeekIndex];
    00237         }
    00238         else
    00239         {
    00240             return NULL;
    00241         }
    00242     }
    00243
    00244     # retrieve data from current seek point
    00245     function GetData()
    00246     {
    00247         # assume that we will not be able to retrieve data
    00248         $Data = NULL;
    00249
    00250         # if arguments were supplied
    00251         if (func_num_args())
    00252         {
    00253             # retrieve index for specified point
    00254             $Index = $this->PerformSeek(func_get_args(), FALSE);
    00255
    00256             # if valid index was found
    00257             if ($Index !== NULL)
    00258             {
    00259                 # retrieve data at index to be returned to caller
    00260                 $Data = $this->TagData[$Index];
    00261             }
    00262         }
    00263         else
    00264         {
    00265             # if current seek index points to valid tag
    00266             if ($this->CurrentSeekIndex >= 0)
    00267             {
    00268                 # retrieve data to be returned to caller
    00269                 $Data = $this->TagData[$this->CurrentSeekIndex];
    00270             }
    00271         }
    00272
    00273         # return data to caller
    00274         if ($this->DebugLevel > 0)
    00275         {
    00276             print("XMLParser->GetData(");
    00277             if (func_num_args()) {  $ArgString = "";  foreach (func_get_args() as $Arg) {  $ArgString .= "\"".$Arg."\", ";  }  $ArgString = substr($ArgString, 0, strlen($ArgString) - 2);  print($ArgString);  }
    00278             print(") returned ".($Data ? "\"".$Data."\"" : "NULL")."<br>\n");
    00279         }
    00280         return $Data;
    00281     }
    00282
    00283     # retrieve specified attribute(s) from current seek point or specified point below
    00284     #   (first arg is attribute name and optional subsequent args tell where to seek to)
    00285     #   (returns NULL if no such attribute for current or specified tag)
    00286     function GetAttribute()
    00287     {
    00288         # retrieve attribute
    00289         $Args = func_get_args();
    00290         $Attrib = $this->PerformGetAttribute($Args, FALSE);
    00291
    00292         # return requested attribute to caller
    00293         if ($this->DebugLevel > 0) {  print("XMLParser->GetAttribute() returned ".$Attrib."<br>\n");  }
    00294         return $Attrib;
    00295     }
    00296     function GetAttributes()
    00297     {
    00298         # retrieve attribute
    00299         $Args = func_get_args();
    00300         $Attribs = $this->PerformGetAttribute($Args, TRUE);
    00301
    00302         # return requested attribute to caller
    00303         if ($this->DebugLevel > 0) {  print("XMLParser->GetAttributes() returned ".count($Attribs)." attributes<br>\n");  }
    00304         return $Attribs;
    00305     }
    00306
    00307
    00308     # ---- PRIVATE INTERFACE -------------------------------------------------
    00309
    00310     var $TagNames;
    00311     var $TagAttribs;
    00312     var $TagData;
    00313     var $TagParents;
    00314     var $CurrentParseIndex;
    00315     var $CurrentSeekIndex;
    00316     var $CurrentItemIndex;
    00317     var $CurrentItemList;
    00318     var $CurrentItemCount;
    00319     var $DebugLevel;
    00320     var $NameKeyCache;
    00321
    00322     # set current debug output level (0-9)
    00323     function SetDebugLevel($NewLevel)
    00324     {
    00325         $this->DebugLevel = $NewLevel;
    00326     }
    00327
    00328     # callback function for handling open tags
    00329     function OpenTag($Parser, $ElementName, $ElementAttribs)
    00330     {
    00331         # add new tag to list
    00332         $NewTagIndex = count($this->TagNames);
    00333         $this->TagNames[$NewTagIndex] = $ElementName;
    00334         $this->TagAttribs[$NewTagIndex] = $ElementAttribs;
    00335         $this->TagParents[$NewTagIndex] = $this->CurrentParseIndex;
    00336         $this->TagData[$NewTagIndex] = NULL;
    00337
    00338         # set current tag to new tag
    00339         $this->CurrentParseIndex = $NewTagIndex;
    00340     }
    00341
    00342     # callback function for receiving data between tags
    00343     function ReceiveData($Parser, $Data)
    00344     {
    00345         # add data to currently open tag
    00346         $this->TagData[$this->CurrentParseIndex] .= $Data;
    00347     }
    00348
    00349     # callback function for handling close tags
    00350     function CloseTag($Parser, $ElementName)
    00351     {
    00352         # if we have an open tag and closing tag matches currently open tag
    00353         if (($this->CurrentParseIndex >= 0)
    00354                 && ($ElementName == $this->TagNames[$this->CurrentParseIndex]))
    00355         {
    00356             # set current tag to parent tag
    00357             $this->CurrentParseIndex = $this->TagParents[$this->CurrentParseIndex];
    00358         }
    00359     }
    00360
    00361     # perform seek to point in tag tree and update seek pointer (if requested)
    00362     function PerformSeek($SeekArgs, $MoveSeekPointer)
    00363     {
    00364         # for each tag name or index in argument list
    00365         $NewSeekIndex = $this->CurrentSeekIndex;
    00366         foreach ($SeekArgs as $Arg)
    00367         {
    00368             # if argument is string
    00369             if (is_string($Arg))
    00370             {
    00371                 # look for tags with given name and current tag as parent
    00372                 $Arg = strtoupper($Arg);
    00373                 if (!isset($this->NameKeyCache[$Arg]))
    00374                 {
    00375                     $this->NameKeyCache[$Arg] = array_keys($this->TagNames, $Arg);
    00376                     $TestArray = array_keys($this->TagNames, $Arg);
    00377                 }
    00378                 $ChildTags = array_keys($this->TagParents, $NewSeekIndex);
    00379                 $NewItemList = array_values(
    00380                         array_intersect($this->NameKeyCache[$Arg], $ChildTags));
    00381                 $NewItemCount = count($NewItemList);
    00382
    00383                 # if matching tag found
    00384                 if ($NewItemCount > 0)
    00385                 {
    00386                     # update local seek index
    00387                     $NewSeekIndex = $NewItemList[0];
    00388
    00389                     # save new item index
    00390                     $NewItemIndex = 0;
    00391                 }
    00392                 else
    00393                 {
    00394                     # report seek failure to caller
    00395                     return NULL;
    00396                 }
    00397             }
    00398             else
    00399             {
    00400                 # look for tags with same name and same parent as current tag
    00401                 $NameTags = array_keys($this->TagNames, $this->TagNames[$NewSeekIndex]);
    00402                 $ChildTags = array_keys($this->TagParents, $this->TagParents[$NewSeekIndex]);
    00403                 $NewItemList = array_values(array_intersect($NameTags, $ChildTags));
    00404                 $NewItemCount = count($NewItemList);
    00405
    00406                 # if enough matching tags were found to contain requested index
    00407                 if ($NewItemCount > $Arg)
    00408                 {
    00409                     # update local seek index
    00410                     $NewSeekIndex = $NewItemList[$Arg];
    00411
    00412                     # save new item index
    00413                     $NewItemIndex = $Arg;
    00414                 }
    00415                 else
    00416                 {
    00417                     # report seek failure to caller
    00418                     return NULL;
    00419                 }
    00420             }
    00421         }
    00422
    00423         # if caller requested that seek pointer be moved to reflect seek
    00424         if ($MoveSeekPointer)
    00425         {
    00426             # update seek index
    00427             $this->CurrentSeekIndex = $NewSeekIndex;
    00428
    00429             # update item index and list
    00430             $this->CurrentItemIndex = $NewItemIndex;
    00431             $this->CurrentItemList = $NewItemList;
    00432             $this->CurrentItemCount = $NewItemCount;
    00433         }
    00434
    00435         # return index of found seek
    00436         return $NewSeekIndex;
    00437     }
    00438
    00439     function PerformGetAttribute($Args, $GetMultiple)
    00440     {
    00441         # assume that we will not be able to retrieve attribute
    00442         $ReturnVal = NULL;
    00443
    00444         # retrieve attribute name and (possibly) seek arguments
    00445         if (!$GetMultiple)
    00446         {
    00447             $AttribName = strtoupper(array_shift($Args));
    00448         }
    00449
    00450         # if arguments were supplied
    00451         if (count($Args))
    00452         {
    00453             # retrieve index for specified point
    00454             $Index = $this->PerformSeek($Args, FALSE);
    00455
    00456             # if valid index was found
    00457             if ($Index !== NULL)
    00458             {
    00459                 # if specified attribute exists
    00460                 if (isset($this->TagAttribs[$Index][$AttribName]))
    00461                 {
    00462                     # retrieve attribute(s) at index to be returned to caller
    00463                     if ($GetMultiple)
    00464                     {
    00465                         $ReturnVal = $this->TagAttribs[$Index];
    00466                     }
    00467                     else
    00468                     {
    00469                         $ReturnVal = $this->TagAttribs[$Index][$AttribName];
    00470                     }
    00471                 }
    00472             }
    00473         }
    00474         else
    00475         {
    00476             # if current seek index points to valid tag
    00477             if ($this->CurrentSeekIndex >= 0)
    00478             {
    00479                 # if specified attribute exists
    00480                 if (isset($this->TagAttribs[$this->CurrentSeekIndex][$AttribName]))
    00481                 {
    00482                     # retrieve attribute(s) to be returned to caller
    00483                     if ($GetMultiple)
    00484                     {
    00485                         $ReturnVal = $this->TagAttribs[$this->CurrentSeekIndex];
    00486                     }
    00487                     else
    00488                     {
    00489                         $ReturnVal = $this->TagAttribs[$this->CurrentSeekIndex][$AttribName];
    00490                     }
    00491                 }
    00492             }
    00493         }
    00494
    00495         # return requested attribute to caller
    00496         return $ReturnVal;
    00497     }
    00498
    00499     # rebuild internal list of tags with the same tag name and same parent as current
    00500     function RebuildItemList()
    00501     {
    00502         # get list of tags with the same parent as current tag
    00503         $SameParentTags = array_keys($this->TagParents,
    00504                 $this->TagParents[$this->CurrentSeekIndex]);
    00505
    00506         # get list of tags with the same name as current tag
    00507         $SameNameTags = array_keys($this->TagNames,
    00508                 $this->TagNames[$this->CurrentSeekIndex]);
    00509
    00510         # intersect lists to get tags with both same name and same parent as current
    00511         $this->CurrentItemList = array_values(
    00512                 array_intersect($SameNameTags, $SameParentTags));
    00513
    00514         # find and save index of current tag within item list
    00515         $this->CurrentItemIndex = array_search(
    00516                 $this->CurrentSeekIndex, $this->CurrentItemList);
    00517
    00518         # save length of item list
    00519         $this->CurrentItemCount = count($this->CurrentItemList);
    00520     }
    00521
    00522     # internal method for debugging
    00523     function DumpInternalArrays()
    00524     {
    00525         foreach ($this->TagNames as $Index => $Name)
    00526         {
    00527             printf("[%03d] %-12.12s %03d %-30.30s \n", $Index, $Name, $this->TagParents[$Index], trim($this->TagData[$Index]));
    00528         }
    00529     }
    00530 }
    00531
    00532
    00533 ?>