Skip to content

Commit

Permalink
Add XML path matching method to XML handlers (#2712)
Browse files Browse the repository at this point in the history
This PR pulls out some of the logic in
#2612 for matching full XML paths
into a separate PR in anticipation of several upcoming PRs. By
specifying the full XML path for an element, the exact element being
targeted is more clear. For the sake of example, I used the new path
method to locate all of the `<Site>` elements.
  • Loading branch information
williamjallen authored Feb 17, 2025
1 parent e55bed2 commit 6dbc92a
Show file tree
Hide file tree
Showing 14 changed files with 104 additions and 64 deletions.
23 changes: 21 additions & 2 deletions app/Utils/Stack.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@

namespace App\Utils;

use InvalidArgumentException;

/**
* @template T
*/
class Stack
{
/**
* @var array<mixed>
* @var array<T>
*/
private array $stack = [];

Expand All @@ -14,18 +19,29 @@ public function size(): int
return count($this->stack);
}

/**
* @param T $e
*
* @return self<T>
*/
public function push(mixed $e): self
{
$this->stack[] = $e;
return $this;
}

/**
* @return self<T>
*/
public function pop(): self
{
array_pop($this->stack);
return $this;
}

/**
* @return T
*/
public function top(): mixed
{
return $this->stack[count($this->stack) - 1];
Expand All @@ -36,10 +52,13 @@ public function isEmpty(): bool
return count($this->stack) === 0;
}

/**
* @return T
*/
public function at(int $index): mixed
{
if ($index < 0 || $index >= count($this->stack)) {
return false;
throw new InvalidArgumentException("Invalid stack index: $index");
}
return $this->stack[$index];
}
Expand Down
30 changes: 29 additions & 1 deletion app/cdash/xml_handlers/abstract_xml_handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@

abstract class AbstractXmlHandler extends AbstractSubmissionHandler
{
/**
* @var Stack<string>
*/
private Stack $stack;
protected bool $Append = false;
protected Site $Site;
Expand Down Expand Up @@ -103,11 +106,36 @@ public static function validate(string $path): array
return $errors;
}

protected function getParent()
protected function getParent(): ?string
{
if ($this->stack->size() <= 1) {
return null;
}

return $this->stack->at($this->stack->size() - 2);
}

protected function currentPathMatches(string $path): bool
{
$path = explode('.', $path);

// We can return early if this isn't even the right level of the document
if ($this->stack->size() !== count($path)) {
return false;
}

for ($i = 0; $i < $this->stack->size(); $i++) {
if ($path[$i] === '*') { // Wildcard matches any string at a given level (but only one level)
continue;
} elseif (strtoupper($path[$i]) === strtoupper((string) $this->stack->at($i))) { // Match the specified string
continue;
} else {
return false;
}
}
return true;
}

protected function getElement()
{
return $this->stack->top();
Expand Down
21 changes: 10 additions & 11 deletions app/cdash/xml_handlers/build_handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public function startElement($parser, $name, $attributes): void
parent::startElement($parser, $name, $attributes);
$factory = $this->getModelFactory();

if ($name == 'SITE') {
if ($this->currentPathMatches('site')) {
$site_name = !empty($attributes['NAME']) ? $attributes['NAME'] : '(empty)';
$this->Site = Site::firstOrCreate(['name' => $site_name], ['name' => $site_name]);

Expand Down Expand Up @@ -192,8 +192,6 @@ public function startElement($parser, $name, $attributes): void

public function endElement($parser, $name): void
{
$parent = $this->getParent(); // should be before endElement
parent::endElement($parser, $name);
$factory = $this->getModelFactory();

if ($name == 'BUILD') {
Expand Down Expand Up @@ -302,7 +300,7 @@ public function endElement($parser, $name): void
$this->Builds[$this->SubProjectName]->AddError($this->Error);
}
unset($this->Error);
} elseif ($name == 'LABEL' && $parent == 'LABELS') {
} elseif ($name == 'LABEL' && $this->getParent() === 'LABELS') {
if (!empty($this->ErrorSubProjectName)) {
$this->SubProjectName = $this->ErrorSubProjectName;
} elseif (isset($this->Error) && $this->Error instanceof BuildFailure) {
Expand All @@ -311,13 +309,14 @@ public function endElement($parser, $name): void
$this->Labels[] = $this->Label;
}
}

parent::endElement($parser, $name);
}

public function text($parser, $data)
{
$parent = $this->getParent();
$element = $this->getElement();
if ($parent == 'BUILD') {
if ($this->getParent() === 'BUILD') {
switch ($element) {
case 'STARTBUILDTIME':
$this->StartTimeStamp = $data;
Expand All @@ -329,7 +328,7 @@ public function text($parser, $data)
$this->BuildCommand = htmlspecialchars_decode($data);
break;
}
} elseif ($parent == 'ACTION') {
} elseif ($this->getParent() === 'ACTION') {
switch ($element) {
case 'LANGUAGE':
$this->Error->Language .= $data;
Expand All @@ -347,7 +346,7 @@ public function text($parser, $data)
$this->Error->OutputType .= $data;
break;
}
} elseif ($parent == 'COMMAND') {
} elseif ($this->getParent() === 'COMMAND') {
switch ($element) {
case 'WORKINGDIRECTORY':
$this->Error->WorkingDirectory .= $data;
Expand All @@ -356,7 +355,7 @@ public function text($parser, $data)
$this->Error->AddArgument($data);
break;
}
} elseif ($parent == 'RESULT') {
} elseif ($this->getParent() === 'RESULT') {
switch ($element) {
case 'STDOUT':
$this->Error->StdOutput .= $data;
Expand All @@ -382,9 +381,9 @@ public function text($parser, $data)
$this->Error->PreContext .= $data;
} elseif ($element == 'POSTCONTEXT') {
$this->Error->PostContext .= $data;
} elseif ($parent == 'SUBPROJECT' && $element == 'LABEL') {
} elseif ($this->getParent() === 'SUBPROJECT' && $element == 'LABEL') {
$this->SubProjects[$this->SubProjectName][] = $data;
} elseif ($parent == 'LABELS' && $element == 'LABEL') {
} elseif ($this->getParent() === 'LABELS' && $element == 'LABEL') {
// First, check if this label belongs to a SubProject
foreach ($this->SubProjects as $subproject => $labels) {
if (in_array($data, $labels)) {
Expand Down
10 changes: 4 additions & 6 deletions app/cdash/xml_handlers/configure_handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public function startElement($parser, $name, $attributes): void
{
parent::startElement($parser, $name, $attributes);

if ($name == 'SITE') {
if ($this->currentPathMatches('site')) {
$sitename = !empty($attributes['NAME']) ? $attributes['NAME'] : '(empty)';
$this->Site = Site::firstOrCreate(['name' => $sitename], ['name' => $sitename]);

Expand Down Expand Up @@ -144,10 +144,6 @@ public function startElement($parser, $name, $attributes): void

public function endElement($parser, $name): void
{
$parent = $this->getParent();

parent::endElement($parser, $name);

if ($name == 'CONFIGURE') {
$start_time = gmdate(FMT_DATETIME, $this->StartTimeStamp);
$end_time = gmdate(FMT_DATETIME, $this->EndTimeStamp);
Expand Down Expand Up @@ -252,11 +248,13 @@ public function endElement($parser, $name): void
// so only need to do this once
$build->UpdateParentConfigureNumbers(
(int) $this->Configure->NumberOfWarnings, (int) $this->Configure->NumberOfErrors);
} elseif ($name == 'LABEL' && $parent == 'LABELS') {
} elseif ($name === 'LABEL' && $this->getParent() === 'LABELS') {
if (isset($this->Configure)) {
$this->Configure->AddLabel($this->Label);
}
}

parent::endElement($parser, $name);
}

public function text($parser, $data)
Expand Down
6 changes: 3 additions & 3 deletions app/cdash/xml_handlers/coverage_handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public function __construct(Project $project)
public function startElement($parser, $name, $attributes): void
{
parent::startElement($parser, $name, $attributes);
if ($name == 'SITE') {
if ($this->currentPathMatches('site')) {
$site_name = !empty($attributes['NAME']) ? $attributes['NAME'] : '(empty)';
$this->Site = Site::firstOrCreate(['name' => $site_name], ['name' => $site_name]);

Expand Down Expand Up @@ -113,8 +113,7 @@ public function startElement($parser, $name, $attributes): void
/** End element */
public function endElement($parser, $name): void
{
parent::endElement($parser, $name);
if ($name == 'SITE') {
if ($this->currentPathMatches('site')) {
$start_time = gmdate(FMT_DATETIME, $this->StartTimeStamp);
$end_time = gmdate(FMT_DATETIME, $this->EndTimeStamp);

Expand Down Expand Up @@ -187,6 +186,7 @@ public function endElement($parser, $name): void
$this->Coverage->AddLabel($this->Label);
}
}
parent::endElement($parser, $name);
}

/** Text function */
Expand Down
7 changes: 4 additions & 3 deletions app/cdash/xml_handlers/coverage_junit_handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public function startElement($parser, $name, $attributes): void
{
parent::startElement($parser, $name, $attributes);
$parent = $this->getParent();
if ($name == 'SITE') {
if ($this->currentPathMatches('site')) {
$site_name = !empty($attributes['NAME']) ? $attributes['NAME'] : '(empty)';
$this->Site = Site::firstOrCreate(['name' => $site_name], ['name' => $site_name]);

Expand Down Expand Up @@ -134,8 +134,7 @@ public function startElement($parser, $name, $attributes): void
/** End element */
public function endElement($parser, $name): void
{
parent::endElement($parser, $name);
if ($name == 'SITE') {
if ($this->currentPathMatches('site')) {
$start_time = gmdate(FMT_DATETIME, $this->StartTimeStamp);
$end_time = gmdate(FMT_DATETIME, $this->EndTimeStamp);

Expand Down Expand Up @@ -173,6 +172,8 @@ public function endElement($parser, $name): void
$this->Coverage->AddLabel($this->Label);
}
}

parent::endElement($parser, $name);
}

/** Text function */
Expand Down
8 changes: 4 additions & 4 deletions app/cdash/xml_handlers/coverage_log_handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public function __construct(Project $project)
public function startElement($parser, $name, $attributes): void
{
parent::startElement($parser, $name, $attributes);
if ($name == 'SITE') {
if ($this->currentPathMatches('site')) {
$site_name = !empty($attributes['NAME']) ? $attributes['NAME'] : '(empty)';
$this->Site = Site::firstOrCreate(['name' => $site_name], ['name' => $site_name]);

Expand All @@ -77,9 +77,7 @@ public function startElement($parser, $name, $attributes): void
/** End Element */
public function endElement($parser, $name): void
{
parent::endElement($parser, $name);

if ($name === 'SITE') {
if ($this->currentPathMatches('site')) {
$start_time = gmdate(FMT_DATETIME, $this->StartTimeStamp);
$end_time = gmdate(FMT_DATETIME, $this->EndTimeStamp);
$this->Build->ProjectId = $this->GetProject()->Id;
Expand Down Expand Up @@ -159,6 +157,8 @@ public function endElement($parser, $name): void
$this->CoverageFiles[] = [new CoverageFile(), new CoverageFileLog()];
}
}

parent::endElement($parser, $name);
}

/** Text */
Expand Down
10 changes: 5 additions & 5 deletions app/cdash/xml_handlers/dynamic_analysis_handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public function startElement($parser, $name, $attributes): void
parent::startElement($parser, $name, $attributes);
$factory = $this->getModelFactory();

if ($name == 'SITE') {
if ($this->currentPathMatches('site')) {
$site_name = !empty($attributes['NAME']) ? $attributes['NAME'] : '(empty)';
$this->Site = Site::firstOrCreate(['name' => $site_name], ['name' => $site_name]);

Expand Down Expand Up @@ -152,10 +152,8 @@ public function startElement($parser, $name, $attributes): void
/** Function endElement */
public function endElement($parser, $name): void
{
$parent = $this->getParent(); // should be before endElement
parent::endElement($parser, $name);
$factory = $this->getModelFactory();
if ($name == 'STARTTESTTIME' && $parent == 'DYNAMICANALYSIS') {
if ($name === 'STARTTESTTIME' && $this->getParent() === 'DYNAMICANALYSIS') {
if (empty($this->SubProjects)) {
// Not a SubProject build.
$this->createBuild('');
Expand All @@ -165,7 +163,7 @@ public function endElement($parser, $name): void
$this->createBuild($subproject);
}
}
} elseif ($name == 'TEST' && $parent == 'DYNAMICANALYSIS') {
} elseif ($name === 'TEST' && $this->getParent() == 'DYNAMICANALYSIS') {
/** @var Build $build */
$build = $this->Builds[$this->SubProjectName];
$GLOBALS['PHP_ERROR_BUILD_ID'] = $build->Id;
Expand Down Expand Up @@ -213,6 +211,8 @@ public function endElement($parser, $name): void
}
}
}

parent::endElement($parser, $name);
}

/** Function Text */
Expand Down
2 changes: 1 addition & 1 deletion app/cdash/xml_handlers/note_handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public function __construct(Project $project)
public function startElement($parser, $name, $attributes): void
{
parent::startElement($parser, $name, $attributes);
if ($name == 'SITE') {
if ($this->currentPathMatches('site')) {
$site_name = !empty($attributes['NAME']) ? $attributes['NAME'] : '(empty)';
$this->Site = Site::firstOrCreate(['name' => $site_name], ['name' => $site_name]);

Expand Down
Loading

0 comments on commit 6dbc92a

Please sign in to comment.