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");
374 $this->DebugLevel = $NewLevel;
378 # ---- PRIVATE INTERFACE ------------------------------------------------- 380 private $CurrentItemCount;
381 private $CurrentItemIndex;
382 private $CurrentItemList;
383 private $CurrentParseIndex;
384 private $CurrentSeekIndex;
386 private $NameKeyCache;
399 private function OpenTag($Parser, $ElementName, $ElementAttribs)
401 # add new tag to list 402 $NewTagIndex = count($this->TagNames);
403 $this->TagNames[$NewTagIndex] = $ElementName;
404 $this->TagAttribs[$NewTagIndex] = $ElementAttribs;
405 $this->TagParents[$NewTagIndex] = $this->CurrentParseIndex;
406 $this->TagData[$NewTagIndex] = NULL;
408 # set current tag to new tag 409 $this->CurrentParseIndex = $NewTagIndex;
417 private function ReceiveData($Parser, $Data)
419 # add data to currently open tag 420 $this->TagData[$this->CurrentParseIndex] .= $Data;
428 private function CloseTag($Parser, $ElementName)
430 # if we have an open tag and closing tag matches currently open tag 431 if (($this->CurrentParseIndex >= 0)
432 && ($ElementName == $this->TagNames[$this->CurrentParseIndex]))
434 # set current tag to parent tag 435 $this->CurrentParseIndex = $this->TagParents[$this->CurrentParseIndex];
445 private function PerformSeek($SeekArgs, $MoveSeekPointer)
447 # for each tag name or index in argument list 448 $NewSeekIndex = $this->CurrentSeekIndex;
449 foreach ($SeekArgs as $Arg)
451 # if argument is string 454 # look for tags with given name and current tag as parent 455 $Arg = strtoupper($Arg);
456 if (!isset($this->NameKeyCache[$Arg]))
458 $this->NameKeyCache[$Arg] = array_keys($this->TagNames, $Arg);
459 $TestArray = array_keys($this->TagNames, $Arg);
461 $ChildTags = array_keys($this->TagParents, $NewSeekIndex);
462 $NewItemList = array_values(
463 array_intersect($this->NameKeyCache[$Arg], $ChildTags));
464 $NewItemCount = count($NewItemList);
466 # if matching tag found 467 if ($NewItemCount > 0)
469 # update local seek index 470 $NewSeekIndex = $NewItemList[0];
472 # save new item index 477 # report seek failure to caller 483 # look for tags with same name and same parent as current tag 484 $NameTags = array_keys($this->TagNames,
485 $this->TagNames[$NewSeekIndex]);
486 $ChildTags = array_keys($this->TagParents,
487 $this->TagParents[$NewSeekIndex]);
488 $NewItemList = array_values(array_intersect($NameTags, $ChildTags));
489 $NewItemCount = count($NewItemList);
491 # if enough matching tags were found to contain requested index 492 if ($NewItemCount > $Arg)
494 # update local seek index 495 $NewSeekIndex = $NewItemList[$Arg];
497 # save new item index 498 $NewItemIndex = $Arg;
502 # report seek failure to caller 508 # if caller requested that seek pointer be moved to reflect seek 509 if ($MoveSeekPointer)
512 $this->CurrentSeekIndex = $NewSeekIndex;
514 # update item index and list 515 $this->CurrentItemIndex = $NewItemIndex;
516 $this->CurrentItemList = $NewItemList;
517 $this->CurrentItemCount = $NewItemCount;
520 # return index of found seek 521 return $NewSeekIndex;
531 private function PerformGetAttribute($Args, $GetMultiple)
533 # assume that we will not be able to retrieve attribute 536 # retrieve attribute name and (possibly) seek arguments 539 $AttribName = strtoupper(array_shift($Args));
542 # if arguments were supplied 545 # retrieve index for specified point 546 $Index = $this->PerformSeek($Args, FALSE);
548 # if valid index was found 551 # if specified attribute exists 552 if (isset($this->TagAttribs[$Index][$AttribName]))
554 # retrieve attribute(s) at index to be returned to caller 557 $ReturnVal = $this->TagAttribs[$Index];
561 $ReturnVal = $this->TagAttribs[$Index][$AttribName];
568 # if current seek index points to valid tag 569 $SeekIndex = $this->CurrentSeekIndex;
572 # if specified attribute exists 573 if (isset($this->TagAttribs[$SeekIndex][$AttribName]))
575 # retrieve attribute(s) to be returned to caller 578 $ReturnVal = $this->TagAttribs[$SeekIndex];
582 $ReturnVal = $this->TagAttribs[$SeekIndex][$AttribName];
588 # return requested attribute to caller 596 private function RebuildItemList()
598 # get list of tags with the same parent as current tag 599 $SameParentTags = array_keys($this->TagParents,
600 $this->TagParents[$this->CurrentSeekIndex]);
602 # get list of tags with the same name as current tag 603 $SameNameTags = array_keys($this->TagNames,
604 $this->TagNames[$this->CurrentSeekIndex]);
606 # intersect lists to get tags with both same name and same parent as current 607 $this->CurrentItemList = array_values(
608 array_intersect($SameNameTags, $SameParentTags));
610 # find and save index of current tag within item list 611 $this->CurrentItemIndex = array_search(
612 $this->CurrentSeekIndex, $this->CurrentItemList);
614 # save length of item list 615 $this->CurrentItemCount = count($this->CurrentItemList);
621 private function DumpInternalArrays()
623 foreach ($this->TagNames as $Index => $Name)
625 printf(
"[%03d] %-12.12s %03d %-30.30s \n", $Index, $Name,
626 $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.
SetDebugLevel($NewLevel)
Set current debug output level (0-9).
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.