5 # Part of the ScoutLib application support library 6 # Copyright 2005-2016 Edward Almasy and Internet Scout Research Group 7 # http://scout.wisc.edu 12 # ---- PUBLIC INTERFACE -------------------------------------------------- 20 # set default debug output level 21 $this->DebugLevel = 0;
23 # create XML parser and tell it about our methods 24 $this->Parser = xml_parser_create($Encoding);
25 xml_set_object($this->Parser, $this);
26 xml_set_element_handler($this->Parser,
"OpenTag",
"CloseTag");
27 xml_set_character_data_handler($this->Parser,
"ReceiveData");
29 # initialize tag storage arrays 30 $this->TagNames = array();
31 $this->TagAttribs = array();
32 $this->TagData = array();
33 $this->TagParents = array();
35 # initialize indexes for parsing and retrieving data 36 $this->CurrentParseIndex = -1;
37 $this->CurrentSeekIndex = -1;
38 $this->NameKeyCache = array();
46 public function ParseText($Text, $LastTextToParse = TRUE)
48 # pass text to PHP XML parser 49 xml_parse($this->Parser, $Text, $LastTextToParse);
59 # perform seek based on arguments passed by caller 60 $SeekResult = $this->PerformSeek(func_get_args(), TRUE);
63 if ($SeekResult !== NULL)
65 # retrieve item count at seek location 66 $ItemCount = count($this->CurrentItemList);
70 # return null value to indicate that seek failed 74 # return count of tags found at requested location 75 if ($this->DebugLevel > 0)
77 print(
"XMLParser->SeekTo(");
80 foreach (func_get_args() as $Arg)
82 $DbugArgList .= $Sep.
"\"".$Arg.
"\"";
85 print($DbugArgList.
") returned ".intval($ItemCount)
86 .
" items starting at index ".$this->CurrentSeekIndex.
"\n");
97 # if we are not at the root of the tree 98 if ($this->CurrentSeekIndex >= 0)
100 # move up one level in tree 101 $this->CurrentSeekIndex = $this->TagParents[$this->CurrentSeekIndex];
104 unset($this->CurrentItemList);
106 # return name of new tag to caller 107 $Result = $this->TagNames[$this->CurrentSeekIndex];
111 # return NULL indicating that no parent was found 115 # return result to caller 116 if ($this->DebugLevel > 0)
118 print(
"XMLParser->SeekToParent() returned ".$Result.
"<br>\n");
130 # look for tags with current tag as parent 131 $ChildTags = array_keys($this->TagParents, $this->CurrentSeekIndex);
133 # if child tag was found with requested index 134 if (isset($ChildTags[$ChildIndex]))
136 # set current seek index to child 137 $this->CurrentSeekIndex = $ChildTags[$ChildIndex];
139 # clear item list info 140 unset($this->CurrentItemList);
142 # return name of new tag to caller 143 $Result = $this->TagNames[$this->CurrentSeekIndex];
147 # return NULL indicating that no children were found 151 # return result to caller 152 if ($this->DebugLevel > 0)
154 print(
"XMLParser->SeekToChild() returned ".$Result.
"<br>\n");
164 $this->CurrentSeekIndex = -1;
173 # get list of tags with same parent as this tag 174 $LevelTags = array_keys($this->TagParents,
175 $this->TagParents[$this->CurrentSeekIndex]);
177 # find position of next tag in list 178 $NextTagPosition = array_search($this->CurrentSeekIndex, $LevelTags) + 1;
180 # if there is a next tag 181 if (count($LevelTags) > $NextTagPosition)
183 # move seek pointer to next tag at this level 184 $this->CurrentSeekIndex = $LevelTags[$NextTagPosition];
188 # return name of tag at new position to caller 189 return $this->TagNames[$this->CurrentSeekIndex];
193 # return NULL to caller to indicate no next tag 204 # set up item list if necessary 205 if (!isset($this->CurrentItemList)) { $this->RebuildItemList(); }
207 # if there are items left to move to 208 if ($this->CurrentItemIndex < ($this->CurrentItemCount - 1))
210 # move item pointer to next item 211 $this->CurrentItemIndex++;
213 # set current seek pointer to next item 214 $this->CurrentSeekIndex =
215 $this->CurrentItemList[$this->CurrentItemIndex];
217 # return new item index to caller 218 $Result = $this->CurrentItemIndex;
222 # return NULL value to caller to indicate failure 226 # return result to caller 236 # set up item list if necessary 237 if (!isset($this->CurrentItemList)) { $this->RebuildItemList(); }
239 # if we are not at the first item 240 if ($this->CurrentItemIndex > 0)
242 # move item pointer to previous item 243 $this->CurrentItemIndex--;
245 # set current seek pointer to next item 246 $this->CurrentSeekIndex =
247 $this->CurrentItemList[$this->CurrentItemIndex];
249 # return new item index to caller 250 return $this->CurrentItemIndex;
254 # return NULL value to caller to indicate failure 265 if (isset($this->TagNames[$this->CurrentSeekIndex]))
267 return $this->TagNames[$this->CurrentSeekIndex];
282 # assume that we will not be able to retrieve data 285 # if arguments were supplied 288 # retrieve index for specified point 289 $Index = $this->PerformSeek(func_get_args(), FALSE);
291 # if valid index was found 294 # retrieve data at index to be returned to caller 295 $Data = $this->TagData[$Index];
300 # if current seek index points to valid tag 301 if ($this->CurrentSeekIndex >= 0)
303 # retrieve data to be returned to caller 304 $Data = $this->TagData[$this->CurrentSeekIndex];
308 # return data to caller 309 if ($this->DebugLevel > 0)
311 print(
"XMLParser->GetData(");
315 foreach (func_get_args() as $Arg)
317 $ArgString .=
"\"".$Arg.
"\", ";
319 $ArgString = substr($ArgString, 0, strlen($ArgString) - 2);
322 print(
") returned ".($Data ?
"\"".$Data.
"\"" :
"NULL").
"<br>\n");
336 $Args = func_get_args();
337 $Attrib = $this->PerformGetAttribute($Args, FALSE);
339 # return requested attribute to caller 340 if ($this->DebugLevel > 0)
342 print(
"XMLParser->GetAttribute() returned ".$Attrib.
"<br>\n");
356 $Args = func_get_args();
357 $Attribs = $this->PerformGetAttribute($Args, TRUE);
359 # return requested attribute to caller 360 if ($this->DebugLevel > 0)
362 print(
"XMLParser->GetAttributes() returned ".count($Attribs)
363 .
" attributes<br>\n");
369 # ---- PRIVATE INTERFACE ------------------------------------------------- 375 private $CurrentParseIndex;
376 private $CurrentSeekIndex;
377 private $CurrentItemIndex;
378 private $CurrentItemList;
379 private $CurrentItemCount;
381 private $NameKeyCache;
387 private function SetDebugLevel($NewLevel)
389 $this->DebugLevel = $NewLevel;
398 private function OpenTag($Parser, $ElementName, $ElementAttribs)
400 # add new tag to list 401 $NewTagIndex = count($this->TagNames);
402 $this->TagNames[$NewTagIndex] = $ElementName;
403 $this->TagAttribs[$NewTagIndex] = $ElementAttribs;
404 $this->TagParents[$NewTagIndex] = $this->CurrentParseIndex;
405 $this->TagData[$NewTagIndex] = NULL;
407 # set current tag to new tag 408 $this->CurrentParseIndex = $NewTagIndex;
416 private function ReceiveData($Parser, $Data)
418 # add data to currently open tag 419 $this->TagData[$this->CurrentParseIndex] .= $Data;
427 private function CloseTag($Parser, $ElementName)
429 # if we have an open tag and closing tag matches currently open tag 430 if (($this->CurrentParseIndex >= 0)
431 && ($ElementName == $this->TagNames[$this->CurrentParseIndex]))
433 # set current tag to parent tag 434 $this->CurrentParseIndex = $this->TagParents[$this->CurrentParseIndex];
444 private function PerformSeek($SeekArgs, $MoveSeekPointer)
446 # for each tag name or index in argument list 447 $NewSeekIndex = $this->CurrentSeekIndex;
448 foreach ($SeekArgs as $Arg)
450 # if argument is string 453 # look for tags with given name and current tag as parent 454 $Arg = strtoupper($Arg);
455 if (!isset($this->NameKeyCache[$Arg]))
457 $this->NameKeyCache[$Arg] = array_keys($this->TagNames, $Arg);
458 $TestArray = array_keys($this->TagNames, $Arg);
460 $ChildTags = array_keys($this->TagParents, $NewSeekIndex);
461 $NewItemList = array_values(
462 array_intersect($this->NameKeyCache[$Arg], $ChildTags));
463 $NewItemCount = count($NewItemList);
465 # if matching tag found 466 if ($NewItemCount > 0)
468 # update local seek index 469 $NewSeekIndex = $NewItemList[0];
471 # save new item index 476 # report seek failure to caller 482 # look for tags with same name and same parent as current tag 483 $NameTags = array_keys($this->TagNames,
484 $this->TagNames[$NewSeekIndex]);
485 $ChildTags = array_keys($this->TagParents,
486 $this->TagParents[$NewSeekIndex]);
487 $NewItemList = array_values(array_intersect($NameTags, $ChildTags));
488 $NewItemCount = count($NewItemList);
490 # if enough matching tags were found to contain requested index 491 if ($NewItemCount > $Arg)
493 # update local seek index 494 $NewSeekIndex = $NewItemList[$Arg];
496 # save new item index 497 $NewItemIndex = $Arg;
501 # report seek failure to caller 507 # if caller requested that seek pointer be moved to reflect seek 508 if ($MoveSeekPointer)
511 $this->CurrentSeekIndex = $NewSeekIndex;
513 # update item index and list 514 $this->CurrentItemIndex = $NewItemIndex;
515 $this->CurrentItemList = $NewItemList;
516 $this->CurrentItemCount = $NewItemCount;
519 # return index of found seek 520 return $NewSeekIndex;
530 private function PerformGetAttribute($Args, $GetMultiple)
532 # assume that we will not be able to retrieve attribute 535 # retrieve attribute name and (possibly) seek arguments 538 $AttribName = strtoupper(array_shift($Args));
541 # if arguments were supplied 544 # retrieve index for specified point 545 $Index = $this->PerformSeek($Args, FALSE);
547 # if valid index was found 550 # if specified attribute exists 551 if (isset($this->TagAttribs[$Index][$AttribName]))
553 # retrieve attribute(s) at index to be returned to caller 556 $ReturnVal = $this->TagAttribs[$Index];
560 $ReturnVal = $this->TagAttribs[$Index][$AttribName];
567 # if current seek index points to valid tag 568 $SeekIndex = $this->CurrentSeekIndex;
571 # if specified attribute exists 572 if (isset($this->TagAttribs[$SeekIndex][$AttribName]))
574 # retrieve attribute(s) to be returned to caller 577 $ReturnVal = $this->TagAttribs[$SeekIndex];
581 $ReturnVal = $this->TagAttribs[$SeekIndex][$AttribName];
587 # return requested attribute to caller 595 private function RebuildItemList()
597 # get list of tags with the same parent as current tag 598 $SameParentTags = array_keys($this->TagParents,
599 $this->TagParents[$this->CurrentSeekIndex]);
601 # get list of tags with the same name as current tag 602 $SameNameTags = array_keys($this->TagNames,
603 $this->TagNames[$this->CurrentSeekIndex]);
605 # intersect lists to get tags with both same name and same parent as current 606 $this->CurrentItemList = array_values(
607 array_intersect($SameNameTags, $SameParentTags));
609 # find and save index of current tag within item list 610 $this->CurrentItemIndex = array_search(
611 $this->CurrentSeekIndex, $this->CurrentItemList);
613 # save length of item list 614 $this->CurrentItemCount = count($this->CurrentItemList);
620 private function DumpInternalArrays()
622 foreach ($this->TagNames as $Index => $Name)
624 printf(
"[%03d] %-12.12s %03d %-30.30s \n", $Index, $Name,
625 $this->TagParents[$Index], trim($this->TagData[$Index]));
SeekToRoot()
Move seek pointer to root of tree.
__construct($Encoding="UTF-8")
Object constructor.
NextItem()
Move to next instance of current tag.
GetTagName()
Retrieve tag name from current seek point.
ParseText($Text, $LastTextToParse=TRUE)
Parse text stream and store result.
GetAttributes()
Retrieve specified attributes from current seek point or specified point below.
GetData()
Retrieve data from current seek point.
SeekTo()
Move current tag pointer to specified item.
SeekToParent()
Move seek pointer up one level.
GetAttribute()
Retrieve specified attribute from current seek point or specified point below.
SeekToChild($ChildIndex=0)
Move seek pointer to first child of current tag.
PreviousItem()
Move to previous instance of current tag.
NextTag()
Move to next tag at current level.