Skip to content

Commit c53db2f

Browse files
Add XML path matching method to XML handlers (#2712)
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.
1 parent e2e1e43 commit c53db2f

14 files changed

+104
-64
lines changed

app/Utils/Stack.php

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,15 @@
22

33
namespace App\Utils;
44

5+
use InvalidArgumentException;
6+
7+
/**
8+
* @template T
9+
*/
510
class Stack
611
{
712
/**
8-
* @var array<mixed>
13+
* @var array<T>
914
*/
1015
private array $stack = [];
1116

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

22+
/**
23+
* @param T $e
24+
*
25+
* @return self<T>
26+
*/
1727
public function push(mixed $e): self
1828
{
1929
$this->stack[] = $e;
2030
return $this;
2131
}
2232

33+
/**
34+
* @return self<T>
35+
*/
2336
public function pop(): self
2437
{
2538
array_pop($this->stack);
2639
return $this;
2740
}
2841

42+
/**
43+
* @return T
44+
*/
2945
public function top(): mixed
3046
{
3147
return $this->stack[count($this->stack) - 1];
@@ -36,10 +52,13 @@ public function isEmpty(): bool
3652
return count($this->stack) === 0;
3753
}
3854

55+
/**
56+
* @return T
57+
*/
3958
public function at(int $index): mixed
4059
{
4160
if ($index < 0 || $index >= count($this->stack)) {
42-
return false;
61+
throw new InvalidArgumentException("Invalid stack index: $index");
4362
}
4463
return $this->stack[$index];
4564
}

app/cdash/xml_handlers/abstract_xml_handler.php

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626

2727
abstract class AbstractXmlHandler extends AbstractSubmissionHandler
2828
{
29+
/**
30+
* @var Stack<string>
31+
*/
2932
private Stack $stack;
3033
protected bool $Append = false;
3134
protected Site $Site;
@@ -103,11 +106,36 @@ public static function validate(string $path): array
103106
return $errors;
104107
}
105108

106-
protected function getParent()
109+
protected function getParent(): ?string
107110
{
111+
if ($this->stack->size() <= 1) {
112+
return null;
113+
}
114+
108115
return $this->stack->at($this->stack->size() - 2);
109116
}
110117

118+
protected function currentPathMatches(string $path): bool
119+
{
120+
$path = explode('.', $path);
121+
122+
// We can return early if this isn't even the right level of the document
123+
if ($this->stack->size() !== count($path)) {
124+
return false;
125+
}
126+
127+
for ($i = 0; $i < $this->stack->size(); $i++) {
128+
if ($path[$i] === '*') { // Wildcard matches any string at a given level (but only one level)
129+
continue;
130+
} elseif (strtoupper($path[$i]) === strtoupper((string) $this->stack->at($i))) { // Match the specified string
131+
continue;
132+
} else {
133+
return false;
134+
}
135+
}
136+
return true;
137+
}
138+
111139
protected function getElement()
112140
{
113141
return $this->stack->top();

app/cdash/xml_handlers/build_handler.php

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public function startElement($parser, $name, $attributes): void
7575
parent::startElement($parser, $name, $attributes);
7676
$factory = $this->getModelFactory();
7777

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

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

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

199197
if ($name == 'BUILD') {
@@ -302,7 +300,7 @@ public function endElement($parser, $name): void
302300
$this->Builds[$this->SubProjectName]->AddError($this->Error);
303301
}
304302
unset($this->Error);
305-
} elseif ($name == 'LABEL' && $parent == 'LABELS') {
303+
} elseif ($name == 'LABEL' && $this->getParent() === 'LABELS') {
306304
if (!empty($this->ErrorSubProjectName)) {
307305
$this->SubProjectName = $this->ErrorSubProjectName;
308306
} elseif (isset($this->Error) && $this->Error instanceof BuildFailure) {
@@ -311,13 +309,14 @@ public function endElement($parser, $name): void
311309
$this->Labels[] = $this->Label;
312310
}
313311
}
312+
313+
parent::endElement($parser, $name);
314314
}
315315

316316
public function text($parser, $data)
317317
{
318-
$parent = $this->getParent();
319318
$element = $this->getElement();
320-
if ($parent == 'BUILD') {
319+
if ($this->getParent() === 'BUILD') {
321320
switch ($element) {
322321
case 'STARTBUILDTIME':
323322
$this->StartTimeStamp = $data;
@@ -329,7 +328,7 @@ public function text($parser, $data)
329328
$this->BuildCommand = htmlspecialchars_decode($data);
330329
break;
331330
}
332-
} elseif ($parent == 'ACTION') {
331+
} elseif ($this->getParent() === 'ACTION') {
333332
switch ($element) {
334333
case 'LANGUAGE':
335334
$this->Error->Language .= $data;
@@ -347,7 +346,7 @@ public function text($parser, $data)
347346
$this->Error->OutputType .= $data;
348347
break;
349348
}
350-
} elseif ($parent == 'COMMAND') {
349+
} elseif ($this->getParent() === 'COMMAND') {
351350
switch ($element) {
352351
case 'WORKINGDIRECTORY':
353352
$this->Error->WorkingDirectory .= $data;
@@ -356,7 +355,7 @@ public function text($parser, $data)
356355
$this->Error->AddArgument($data);
357356
break;
358357
}
359-
} elseif ($parent == 'RESULT') {
358+
} elseif ($this->getParent() === 'RESULT') {
360359
switch ($element) {
361360
case 'STDOUT':
362361
$this->Error->StdOutput .= $data;
@@ -382,9 +381,9 @@ public function text($parser, $data)
382381
$this->Error->PreContext .= $data;
383382
} elseif ($element == 'POSTCONTEXT') {
384383
$this->Error->PostContext .= $data;
385-
} elseif ($parent == 'SUBPROJECT' && $element == 'LABEL') {
384+
} elseif ($this->getParent() === 'SUBPROJECT' && $element == 'LABEL') {
386385
$this->SubProjects[$this->SubProjectName][] = $data;
387-
} elseif ($parent == 'LABELS' && $element == 'LABEL') {
386+
} elseif ($this->getParent() === 'LABELS' && $element == 'LABEL') {
388387
// First, check if this label belongs to a SubProject
389388
foreach ($this->SubProjects as $subproject => $labels) {
390389
if (in_array($data, $labels)) {

app/cdash/xml_handlers/configure_handler.php

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public function startElement($parser, $name, $attributes): void
6969
{
7070
parent::startElement($parser, $name, $attributes);
7171

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

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

145145
public function endElement($parser, $name): void
146146
{
147-
$parent = $this->getParent();
148-
149-
parent::endElement($parser, $name);
150-
151147
if ($name == 'CONFIGURE') {
152148
$start_time = gmdate(FMT_DATETIME, $this->StartTimeStamp);
153149
$end_time = gmdate(FMT_DATETIME, $this->EndTimeStamp);
@@ -252,11 +248,13 @@ public function endElement($parser, $name): void
252248
// so only need to do this once
253249
$build->UpdateParentConfigureNumbers(
254250
(int) $this->Configure->NumberOfWarnings, (int) $this->Configure->NumberOfErrors);
255-
} elseif ($name == 'LABEL' && $parent == 'LABELS') {
251+
} elseif ($name === 'LABEL' && $this->getParent() === 'LABELS') {
256252
if (isset($this->Configure)) {
257253
$this->Configure->AddLabel($this->Label);
258254
}
259255
}
256+
257+
parent::endElement($parser, $name);
260258
}
261259

262260
public function text($parser, $data)

app/cdash/xml_handlers/coverage_handler.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public function __construct(Project $project)
5555
public function startElement($parser, $name, $attributes): void
5656
{
5757
parent::startElement($parser, $name, $attributes);
58-
if ($name == 'SITE') {
58+
if ($this->currentPathMatches('site')) {
5959
$site_name = !empty($attributes['NAME']) ? $attributes['NAME'] : '(empty)';
6060
$this->Site = Site::firstOrCreate(['name' => $site_name], ['name' => $site_name]);
6161

@@ -113,8 +113,7 @@ public function startElement($parser, $name, $attributes): void
113113
/** End element */
114114
public function endElement($parser, $name): void
115115
{
116-
parent::endElement($parser, $name);
117-
if ($name == 'SITE') {
116+
if ($this->currentPathMatches('site')) {
118117
$start_time = gmdate(FMT_DATETIME, $this->StartTimeStamp);
119118
$end_time = gmdate(FMT_DATETIME, $this->EndTimeStamp);
120119

@@ -187,6 +186,7 @@ public function endElement($parser, $name): void
187186
$this->Coverage->AddLabel($this->Label);
188187
}
189188
}
189+
parent::endElement($parser, $name);
190190
}
191191

192192
/** Text function */

app/cdash/xml_handlers/coverage_junit_handler.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public function startElement($parser, $name, $attributes): void
5050
{
5151
parent::startElement($parser, $name, $attributes);
5252
$parent = $this->getParent();
53-
if ($name == 'SITE') {
53+
if ($this->currentPathMatches('site')) {
5454
$site_name = !empty($attributes['NAME']) ? $attributes['NAME'] : '(empty)';
5555
$this->Site = Site::firstOrCreate(['name' => $site_name], ['name' => $site_name]);
5656

@@ -134,8 +134,7 @@ public function startElement($parser, $name, $attributes): void
134134
/** End element */
135135
public function endElement($parser, $name): void
136136
{
137-
parent::endElement($parser, $name);
138-
if ($name == 'SITE') {
137+
if ($this->currentPathMatches('site')) {
139138
$start_time = gmdate(FMT_DATETIME, $this->StartTimeStamp);
140139
$end_time = gmdate(FMT_DATETIME, $this->EndTimeStamp);
141140

@@ -173,6 +172,8 @@ public function endElement($parser, $name): void
173172
$this->Coverage->AddLabel($this->Label);
174173
}
175174
}
175+
176+
parent::endElement($parser, $name);
176177
}
177178

178179
/** Text function */

app/cdash/xml_handlers/coverage_log_handler.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public function __construct(Project $project)
5151
public function startElement($parser, $name, $attributes): void
5252
{
5353
parent::startElement($parser, $name, $attributes);
54-
if ($name == 'SITE') {
54+
if ($this->currentPathMatches('site')) {
5555
$site_name = !empty($attributes['NAME']) ? $attributes['NAME'] : '(empty)';
5656
$this->Site = Site::firstOrCreate(['name' => $site_name], ['name' => $site_name]);
5757

@@ -77,9 +77,7 @@ public function startElement($parser, $name, $attributes): void
7777
/** End Element */
7878
public function endElement($parser, $name): void
7979
{
80-
parent::endElement($parser, $name);
81-
82-
if ($name === 'SITE') {
80+
if ($this->currentPathMatches('site')) {
8381
$start_time = gmdate(FMT_DATETIME, $this->StartTimeStamp);
8482
$end_time = gmdate(FMT_DATETIME, $this->EndTimeStamp);
8583
$this->Build->ProjectId = $this->GetProject()->Id;
@@ -159,6 +157,8 @@ public function endElement($parser, $name): void
159157
$this->CoverageFiles[] = [new CoverageFile(), new CoverageFileLog()];
160158
}
161159
}
160+
161+
parent::endElement($parser, $name);
162162
}
163163

164164
/** Text */

app/cdash/xml_handlers/dynamic_analysis_handler.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public function startElement($parser, $name, $attributes): void
7171
parent::startElement($parser, $name, $attributes);
7272
$factory = $this->getModelFactory();
7373

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

@@ -152,10 +152,8 @@ public function startElement($parser, $name, $attributes): void
152152
/** Function endElement */
153153
public function endElement($parser, $name): void
154154
{
155-
$parent = $this->getParent(); // should be before endElement
156-
parent::endElement($parser, $name);
157155
$factory = $this->getModelFactory();
158-
if ($name == 'STARTTESTTIME' && $parent == 'DYNAMICANALYSIS') {
156+
if ($name === 'STARTTESTTIME' && $this->getParent() === 'DYNAMICANALYSIS') {
159157
if (empty($this->SubProjects)) {
160158
// Not a SubProject build.
161159
$this->createBuild('');
@@ -165,7 +163,7 @@ public function endElement($parser, $name): void
165163
$this->createBuild($subproject);
166164
}
167165
}
168-
} elseif ($name == 'TEST' && $parent == 'DYNAMICANALYSIS') {
166+
} elseif ($name === 'TEST' && $this->getParent() == 'DYNAMICANALYSIS') {
169167
/** @var Build $build */
170168
$build = $this->Builds[$this->SubProjectName];
171169
$GLOBALS['PHP_ERROR_BUILD_ID'] = $build->Id;
@@ -213,6 +211,8 @@ public function endElement($parser, $name): void
213211
}
214212
}
215213
}
214+
215+
parent::endElement($parser, $name);
216216
}
217217

218218
/** Function Text */

app/cdash/xml_handlers/note_handler.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public function __construct(Project $project)
4444
public function startElement($parser, $name, $attributes): void
4545
{
4646
parent::startElement($parser, $name, $attributes);
47-
if ($name == 'SITE') {
47+
if ($this->currentPathMatches('site')) {
4848
$site_name = !empty($attributes['NAME']) ? $attributes['NAME'] : '(empty)';
4949
$this->Site = Site::firstOrCreate(['name' => $site_name], ['name' => $site_name]);
5050

0 commit comments

Comments
 (0)