Passed
Push — develop ( 9c7e6c...a31d48 )
by Mikaël
09:44
created

Struct::getStructMethodAnnotationBlock()   A

Complexity

Conditions 6
Paths 6

Size

Total Lines 29
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 6
eloc 17
nc 6
nop 1
dl 0
loc 29
ccs 18
cts 18
cp 1
crap 6
rs 9.0777
c 1
b 0
f 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace WsdlToPhp\PackageGenerator\File;
6
7
use WsdlToPhp\PackageGenerator\Container\Model\StructAttribute as StructAttributeContainer;
8
use WsdlToPhp\PackageGenerator\Container\PhpElement\Constant as ConstantContainer;
9
use WsdlToPhp\PackageGenerator\Container\PhpElement\Property as PropertyContainer;
10
use WsdlToPhp\PackageGenerator\File\Element\PhpFunctionParameter;
11
use WsdlToPhp\PackageGenerator\File\Validation\ArrayRule;
12
use WsdlToPhp\PackageGenerator\File\Validation\ChoiceRule;
13
use WsdlToPhp\PackageGenerator\File\Validation\LengthRule;
14
use WsdlToPhp\PackageGenerator\File\Validation\MaxLengthRule;
15
use WsdlToPhp\PackageGenerator\File\Validation\MinLengthRule;
16
use WsdlToPhp\PackageGenerator\File\Validation\PatternRule;
17
use WsdlToPhp\PackageGenerator\File\Validation\Rules;
18
use WsdlToPhp\PackageGenerator\File\Validation\UnionRule;
19
use WsdlToPhp\PackageGenerator\Model\AbstractModel;
20
use WsdlToPhp\PackageGenerator\Model\Struct as StructModel;
21
use WsdlToPhp\PackageGenerator\Model\StructAttribute as StructAttributeModel;
22
use WsdlToPhp\PhpGenerator\Element\AccessRestrictedElementInterface;
23
use WsdlToPhp\PhpGenerator\Element\AssignedValueElementInterface;
24
use WsdlToPhp\PhpGenerator\Element\PhpAnnotation;
25
use WsdlToPhp\PhpGenerator\Element\PhpAnnotationBlock;
26
use WsdlToPhp\PhpGenerator\Element\PhpConstant;
27
use WsdlToPhp\PhpGenerator\Element\PhpMethod;
28
use WsdlToPhp\PhpGenerator\Element\PhpProperty;
29
30
class Struct extends AbstractModelFile
31
{
32 144
    public function setModel(AbstractModel $model): self
33
    {
34 144
        if (!$model instanceof StructModel) {
35 2
            throw new \InvalidArgumentException('Model must be an instance of a Struct', __LINE__);
36
        }
37
38 142
        return parent::setModel($model);
0 ignored issues
show
Bug Best Practice introduced by
The expression return parent::setModel($model) returns the type WsdlToPhp\PackageGenerator\File\AbstractModelFile which includes types incompatible with the type-hinted return WsdlToPhp\PackageGenerator\File\Struct.
Loading history...
39
    }
40
41 140
    public function getModel(): ?StructModel
42
    {
43 140
        return parent::getModel();
0 ignored issues
show
Bug Best Practice introduced by
The expression return parent::getModel() could return the type WsdlToPhp\PackageGenerator\Model\AbstractModel which includes types incompatible with the type-hinted return WsdlToPhp\PackageGenerator\Model\Struct|null. Consider adding an additional type-check to rule them out.
Loading history...
44
    }
45
46 112
    protected function defineUseStatements(): self
47
    {
48 112
        if ($this->getGenerator()->getOptionValidation()) {
49 110
            $this->getFile()->addUse(\InvalidArgumentException::class, null, false);
50
        }
51
52 112
        return parent::defineUseStatements();
0 ignored issues
show
Bug Best Practice introduced by
The expression return parent::defineUseStatements() returns the type WsdlToPhp\PackageGenerator\File\AbstractModelFile which includes types incompatible with the type-hinted return WsdlToPhp\PackageGenerator\File\Struct.
Loading history...
53
    }
54
55 112
    protected function fillClassConstants(ConstantContainer $constants): void
56
    {
57 112
    }
58
59
    protected function getConstantAnnotationBlock(PhpConstant $constant): ?PhpAnnotationBlock
60
    {
61
        return null;
62
    }
63
64 130
    protected function getModelAttributes(): StructAttributeContainer
65
    {
66 130
        return $this->getModel()->getProperAttributes(true);
67
    }
68
69 130
    protected function fillClassProperties(PropertyContainer $properties): void
70
    {
71
        /** @var StructAttributeModel $attribute */
72 130
        foreach ($this->getModelAttributes() as $attribute) {
73
            switch (true) {
74 108
                case $attribute->isXml():
75 4
                    $type = null;
76
77 4
                    break;
78
79
                default:
80 108
                    $type = (($attribute->isRequired() && !$attribute->isNullable()) ? '' : '?').$this->getStructAttributeTypeAsPhpType($attribute);
81
82 108
                    break;
83
            }
84
85 108
            $properties->add(
86 108
                new PhpProperty(
87 108
                    $attribute->getCleanName(),
88 108
                    $attribute->isRequired() ? AssignedValueElementInterface::NO_VALUE : null,
89 108
                    $this->getGenerator()->getOptionValidation() ? AccessRestrictedElementInterface::ACCESS_PROTECTED : AccessRestrictedElementInterface::ACCESS_PUBLIC,
90 108
                    $type
91 108
                )
92 108
            );
93
        }
94
    }
95
96 108
    protected function getPropertyAnnotationBlock(PhpProperty $property): ?PhpAnnotationBlock
97
    {
98 108
        $annotationBlock = new PhpAnnotationBlock();
99 108
        $annotationBlock->addChild(sprintf('The %s', $property->getName()));
100 108
        $attribute = $this->getModel()->getAttribute($property->getName());
101 108
        if (!$attribute instanceof StructAttributeModel) {
102 2
            $attribute = $this->getModel()->getAttributeByCleanName($property->getName());
103
        }
104 108
        if ($attribute instanceof StructAttributeModel) {
105 108
            $this->defineModelAnnotationsFromWsdl($annotationBlock, $attribute);
106 108
            $annotationBlock->addChild(new PhpAnnotation(self::ANNOTATION_VAR, $this->getStructAttributeTypeGetAnnotation($attribute)));
107
        }
108
109 108
        return $annotationBlock;
110
    }
111
112 112
    protected function fillClassMethods(): void
113
    {
114 112
        $this
115 112
            ->addStructMethodConstruct()
116 112
            ->addStructMethodsSetAndGet()
117 112
        ;
118
    }
119
120 112
    protected function addStructMethodConstruct(): self
121
    {
122 112
        if (0 < count($parameters = $this->getStructMethodParametersValues())) {
123 108
            $method = new PhpMethod(self::METHOD_CONSTRUCT, $parameters);
124 108
            $this->addStructMethodConstructBody($method);
125 108
            $this->methods->add($method);
126
        }
127
128 112
        return $this;
129
    }
130
131 108
    protected function addStructMethodConstructBody(PhpMethod $method): self
132
    {
133 108
        $count = $this->getModelAttributes()->count();
134 108
        foreach ($this->getModelAttributes() as $index => $attribute) {
135 108
            if (0 === $index) {
136 108
                $method->addChild('$this');
137
            }
138 108
            $this->addStructMethodConstructBodyForAttribute($method, $attribute, $count - 1 === $index);
139
        }
140
141 108
        return $this;
142
    }
143
144 108
    protected function addStructMethodConstructBodyForAttribute(PhpMethod $method, StructAttributeModel $attribute, bool $isLast): self
145
    {
146 108
        $uniqueString = $attribute->getUniqueString($attribute->getCleanName(), 'method');
147 108
        $method->addChild($method->getIndentedString(sprintf('->%s($%s)%s', $attribute->getSetterName(), lcfirst($uniqueString), $isLast ? ';' : ''), 1));
148
149 108
        return $this;
150
    }
151
152 112
    protected function getStructMethodParametersValues(): array
153
    {
154 112
        $parametersValues = [];
155 112
        foreach ($this->getModelAttributes() as $attribute) {
156 108
            $parametersValues[] = $this->getStructMethodParameter($attribute);
157
        }
158
159 112
        return $parametersValues;
160
    }
161
162 108
    protected function getStructMethodParameter(StructAttributeModel $attribute): PhpFunctionParameter
163
    {
164
        switch (true) {
165 108
            case $attribute->isXml():
166 108
            case $attribute->isList():
167 10
                $type = null;
168
169 10
                break;
170
171
            default:
172 108
                $type = (($attribute->isRequired() && !$attribute->isNullable()) ? '' : '?').$this->getStructAttributeTypeAsPhpType($attribute);
173
174 108
                break;
175
        }
176
177
        try {
178 108
            $defaultValue = $attribute->getDefaultValue();
179
180 108
            return new PhpFunctionParameter(
181 108
                lcfirst($attribute->getUniqueString($attribute->getCleanName(), 'method')),
182 108
                $attribute->isRequired() && !$attribute->isAChoice() ? AssignedValueElementInterface::NO_VALUE : (str_contains($type ?? '', '?') ? $defaultValue ?? null : $defaultValue),
183 108
                $type,
184 108
                $attribute
185 108
            );
186
        } catch (\InvalidArgumentException $exception) {
187
            throw new \InvalidArgumentException(sprintf('Unable to create function parameter for struct "%s" with type "%s" for attribute "%s"', $this->getModel()->getName(), var_export($this->getStructAttributeTypeAsPhpType($attribute), true), $attribute->getName()), __LINE__, $exception);
188
        }
189
    }
190
191 112
    protected function addStructMethodsSetAndGet(): self
192
    {
193 112
        foreach ($this->getModelAttributes() as $attribute) {
194 108
            $this
195 108
                ->addStructMethodGet($attribute)
196 108
                ->addStructMethodSet($attribute)
197 108
                ->addStructMethodAddTo($attribute)
198 108
            ;
199
        }
200
201 112
        return $this;
202
    }
203
204 94
    protected function addStructMethodAddTo(StructAttributeModel $attribute): self
205
    {
206 94
        if ($attribute->isArray()) {
207 40
            $method = new PhpMethod(sprintf('addTo%s', ucfirst($attribute->getCleanName())), [
208 40
                new PhpFunctionParameter(
209 40
                    'item',
210 40
                    AssignedValueElementInterface::NO_VALUE,
211 40
                    $this->getStructAttributeTypeAsPhpType($attribute, false),
212 40
                    $attribute
213 40
                ),
214 40
            ], self::TYPE_SELF);
215 40
            $this->addStructMethodAddToBody($method, $attribute);
216 40
            $this->methods->add($method);
217
        }
218
219 94
        return $this;
220
    }
221
222 40
    protected function addStructMethodAddToBody(PhpMethod $method, StructAttributeModel $attribute): self
223
    {
224 40
        $this->applyRules($method, $attribute, 'item', true);
225
226 40
        if ($attribute->nameIsClean()) {
227 40
            $assignment = sprintf('$this->%s[] = $item;', $attribute->getCleanName());
228
        } else {
229
            $assignment = sprintf('$this->%s[] = $this->{\'%s\'}[] = $item;', $attribute->getCleanName(), addslashes($attribute->getName()));
230
        }
231
232 40
        $method
233 40
            ->addChild($assignment)
234 40
            ->addChild('')
235 40
            ->addChild('return $this;')
236 40
        ;
237
238 40
        return $this;
239
    }
240
241 108
    protected function addStructMethodSet(StructAttributeModel $attribute): self
242
    {
243 108
        $method = new PhpMethod($attribute->getSetterName(), [
244 108
            $this->getStructMethodParameter($attribute),
245 108
        ], self::TYPE_SELF);
246 108
        $this->addStructMethodSetBody($method, $attribute);
247 108
        $this->methods->add($method);
248
249 108
        return $this;
250
    }
251
252 108
    protected function addStructMethodSetBody(PhpMethod $method, StructAttributeModel $attribute): self
253
    {
254 108
        $parameters = $method->getParameters();
255 108
        $parameter = array_shift($parameters);
256 108
        $parameterName = is_string($parameter) ? $parameter : $parameter->getName();
257
258 108
        return $this
259 108
            ->applyRules($method, $attribute, $parameterName)
260 108
            ->addStructMethodSetBodyAssignment($method, $attribute, $parameterName)
261 108
            ->addStructMethodSetBodyReturn($method)
262 108
        ;
263
    }
264
265 108
    protected function addStructMethodSetBodyAssignment(PhpMethod $method, StructAttributeModel $attribute, string $parameterName): self
266
    {
267 108
        if ($attribute->getRemovableFromRequest() || $attribute->isAChoice()) {
268 18
            $method
269 18
                ->addChild(sprintf('if (is_null($%1$s) || (is_array($%1$s) && empty($%1$s))) {', $parameterName))
270 18
                ->addChild($method->getIndentedString(sprintf('unset($this->%1$s%2$s);', $attribute->getCleanName(), $attribute->nameIsClean() ? '' : sprintf(', $this->{\'%s\'}', addslashes($attribute->getName()))), 1))
271 18
                ->addChild('} else {')
272 18
                ->addChild($method->getIndentedString($this->getStructMethodSetBodyAssignment($attribute, $parameterName), 1))
273 18
                ->addChild('}')
274 18
            ;
275
        } else {
276 104
            $method->addChild($this->getStructMethodSetBodyAssignment($attribute, $parameterName));
277
        }
278
279 108
        return $this;
280
    }
281
282 108
    protected function addStructMethodSetBodyReturn(PhpMethod $method): self
283
    {
284 108
        $method
285 108
            ->addChild('')
286 108
            ->addChild('return $this;')
287 108
        ;
288
289 108
        return $this;
290
    }
291
292 108
    protected function getStructMethodSetBodyAssignment(StructAttributeModel $attribute, string $parameterName): string
293
    {
294 108
        $prefix = '$';
295 108
        if ($attribute->isList()) {
296 6
            $prefix = '';
297 6
            $parameterName = sprintf('is_array($%1$s) ? implode(\' \', $%1$s) : $%1$s', $parameterName);
298 108
        } elseif ($attribute->isXml()) {
299 4
            $prefix = '';
300 4
            $parameterName = sprintf('($%1$s instanceof \DOMDocument) ? $%1$s->saveXML($%1$s->hasChildNodes() ? $%1$s->childNodes->item(0) : null) : $%1$s', $parameterName);
301
        }
302
303 108
        if ($attribute->nameIsClean()) {
304 108
            $assignment = sprintf('$this->%s = %s%s;', $attribute->getName(), $prefix, $parameterName);
305
        } else {
306 2
            $assignment = sprintf('$this->%s = $this->{\'%s\'} = %s%s;', $attribute->getCleanName(), addslashes($attribute->getName()), $prefix, $parameterName);
307
        }
308
309 108
        return $assignment;
310
    }
311
312 108
    protected function addStructMethodGetBody(PhpMethod $method, StructAttributeModel $attribute, string $thisAccess): self
313
    {
314 108
        return $this->addStructMethodGetBodyReturn($method, $attribute, $thisAccess);
315
    }
316
317 108
    protected function addStructMethodGetBodyReturn(PhpMethod $method, StructAttributeModel $attribute, string $thisAccess): self
318
    {
319 108
        $return = sprintf('return $this->%s;', $thisAccess);
320 108
        if ($attribute->isXml()) {
321 4
            $method
322 4
                ->addChild('$domDocument = null;')
323 4
                ->addChild(sprintf('if (!empty($this->%1$s) && $asDomDocument) {', $thisAccess))
324 4
                ->addChild($method->getIndentedString('$domDocument = new \DOMDocument(\'1.0\', \'UTF-8\');', 1))
325 4
                ->addChild($method->getIndentedString(sprintf('$domDocument->loadXML($this->%s);', $thisAccess), 1))
326 4
                ->addChild('}')
327 4
            ;
328 4
            if ($attribute->getRemovableFromRequest() || $attribute->isAChoice()) {
329
                $return = sprintf('return $asDomDocument ? $domDocument : (isset($this->%1$s) ? $this->%1$s : null);', $thisAccess);
330
            } else {
331 4
                $return = sprintf('return $asDomDocument ? $domDocument : $this->%1$s;', $thisAccess);
332
            }
333 108
        } elseif ($attribute->getRemovableFromRequest() || $attribute->isAChoice()) {
334 18
            $return = sprintf('return $this->%s ?? null;', $thisAccess);
335
        }
336 108
        $method->addChild($return);
337
338 108
        return $this;
339
    }
340
341 108
    protected function addStructMethodGet(StructAttributeModel $attribute): self
342
    {
343
        switch (true) {
344
            // it can either be a string, a DOMDocument or null...
345 108
            case $attribute->isXml():
346 4
                $returnType = '';
347
348 4
                break;
349
350
            default:
351 108
                $returnType = (!$attribute->getRemovableFromRequest() && !$attribute->isAChoice() && $attribute->isRequired() ? '' : '?').$this->getStructAttributeTypeAsPhpType($attribute);
352
353 108
                break;
354
        }
355
356 108
        $method = new PhpMethod(
357 108
            $attribute->getGetterName(),
358 108
            $this->getStructMethodGetParameters($attribute),
359 108
            $returnType
360 108
        );
361 108
        if ($attribute->nameIsClean()) {
362 108
            $thisAccess = sprintf('%s', $attribute->getName());
363
        } else {
364 2
            $thisAccess = sprintf('{\'%s\'}', addslashes($attribute->getName()));
365
        }
366 108
        $this->addStructMethodGetBody($method, $attribute, $thisAccess);
367 108
        $this->methods->add($method);
368
369 108
        return $this;
370
    }
371
372 108
    protected function getStructMethodGetParameters(StructAttributeModel $attribute): array
373
    {
374 108
        $parameters = [];
375 108
        if ($attribute->isXml()) {
376 4
            $parameters[] = new PhpFunctionParameter('asDomDocument', false, self::TYPE_BOOL, $attribute);
377
        }
378
379 108
        return $parameters;
380
    }
381
382 108
    protected function getMethodAnnotationBlock(PhpMethod $method): ?PhpAnnotationBlock
383
    {
384 108
        return $this->getStructMethodAnnotationBlock($method);
385
    }
386
387 108
    protected function getStructMethodAnnotationBlock(PhpMethod $method): ?PhpAnnotationBlock
388
    {
389 108
        $annotationBlock = null;
390 108
        $matches = [];
391
392 108
        switch ($method->getName()) {
393 108
            case self::METHOD_CONSTRUCT:
394 108
                $annotationBlock = $this->getStructMethodConstructAnnotationBlock();
395
396 108
                break;
397
398 108
            case 0 === mb_strpos($method->getName(), 'get'):
399 108
            case 0 === mb_strpos($method->getName(), 'set'):
400 108
                $annotationBlock = $this->getStructMethodsSetAndGetAnnotationBlock($method);
401
402 108
                break;
403
404 72
            case 0 === mb_strpos($method->getName(), 'addTo'):
405 40
                $annotationBlock = $this->getStructMethodsAddToAnnotationBlock($method);
406
407 40
                break;
408
409 72
            case 1 === preg_match('/validate(.+)For(.+)ConstraintFrom(.+)/', $method->getName(), $matches):
410 72
                $annotationBlock = $this->getStructMethodsValidateMethodAnnotationBlock($matches[1], $matches[2], $matches[3]);
411
412 72
                break;
413
        }
414
415 108
        return $annotationBlock;
416
    }
417
418 108
    protected function getStructMethodConstructAnnotationBlock(): PhpAnnotationBlock
419
    {
420 108
        $annotationBlock = new PhpAnnotationBlock([
421 108
            sprintf('Constructor method for %s', $this->getModel()->getName()),
422 108
        ]);
423 108
        $this->addStructPropertiesToAnnotationBlock($annotationBlock);
424
425 108
        return $annotationBlock;
426
    }
427
428 108
    protected function getStructMethodsSetAndGetAnnotationBlock(PhpMethod $method): PhpAnnotationBlock
429
    {
430 108
        $parameters = $method->getParameters();
431 108
        $setOrGet = mb_strtolower(mb_substr($method->getName(), 0, 3));
432 108
        $parameter = array_shift($parameters);
433
        // Only set parameter must be based on a potential PhpFunctionParameter
434 108
        if ($parameter instanceof PhpFunctionParameter && 'set' === $setOrGet) {
435 108
            $parameterName = ucfirst($parameter->getName());
436
        } else {
437 108
            $parameterName = mb_substr($method->getName(), 3);
438
        }
439
440
        /**
441
         * Since properties can be duplicated with different case, we assume that _\d+ is replaceable by an empty string as methods are "duplicated" with this suffix.
442
         */
443 108
        $parameterName = preg_replace('/(_\d+)/', '', $parameterName);
444 108
        $attribute = $this->getModel()->getAttribute($parameterName);
445 108
        if (!$attribute instanceof StructAttributeModel) {
446 46
            $attribute = $this->getModel()->getAttributeByCleanName($parameterName);
447
        }
448 108
        if (!$attribute instanceof StructAttributeModel) {
449 46
            $parameterName = lcfirst($parameterName);
450 46
            $attribute = $this->getModel()->getAttribute($parameterName);
451 46
            if (!$attribute instanceof StructAttributeModel) {
452 4
                $attribute = $this->getModel()->getAttributeByCleanName($parameterName);
453
            }
454
        }
455 108
        $setValueAnnotation = '%s %s value';
456 108
        $annotationBlock = new PhpAnnotationBlock();
457 108
        if ($attribute instanceof StructAttributeModel) {
458 108
            $annotationBlock->addChild(sprintf($setValueAnnotation, ucfirst($setOrGet), $parameterName));
459 108
            $this->addStructMethodsSetAndGetAnnotationBlockFromStructAttribute($setOrGet, $annotationBlock, $attribute);
460 2
        } elseif (!$attribute) {
0 ignored issues
show
introduced by
$attribute is of type null, thus it always evaluated to false.
Loading history...
461 2
            $annotationBlock->addChild(sprintf($setValueAnnotation, ucfirst($setOrGet), lcfirst($parameterName)));
462 2
            $this->addStructMethodsSetAndGetAnnotationBlockFromScalar($setOrGet, $annotationBlock, $parameterName);
463
        }
464
465 108
        return $annotationBlock;
466
    }
467
468 108
    protected function addStructMethodsSetAndGetAnnotationBlockFromStructAttribute(string $setOrGet, PhpAnnotationBlock $annotationBlock, StructAttributeModel $attribute): self
469
    {
470
        switch ($setOrGet) {
471 108
            case 'set':
472 108
                if ($attribute->getRemovableFromRequest()) {
473 10
                    $annotationBlock->addChild('This property is removable from request (nillable=true+minOccurs=0), therefore if the value assigned to this property is null, it is removed from this object');
474
                }
475 108
                if ($attribute->isAChoice()) {
476 8
                    $annotationBlock->addChild('This property belongs to a choice that allows only one property to exist. It is therefore removable from the request, consequently if the value assigned to this property is null, the property is removed from this object');
477
                }
478 108
                if ($attribute->isXml()) {
479 4
                    $annotationBlock
480 4
                        ->addChild(new PhpAnnotation(self::ANNOTATION_USES, '\DOMDocument::hasChildNodes()'))
481 4
                        ->addChild(new PhpAnnotation(self::ANNOTATION_USES, '\DOMDocument::saveXML()'))
482 4
                        ->addChild(new PhpAnnotation(self::ANNOTATION_USES, '\DOMNode::item()'))
483 4
                    ;
484
                }
485 108
                if ($this->getGenerator()->getOptionValidation()) {
486 106
                    if ($attribute->isAChoice()) {
487 8
                        $annotationBlock->addChild(new PhpAnnotation(self::ANNOTATION_THROWS, \InvalidArgumentException::class));
488
                    }
489 106
                    if (($model = $this->getRestrictionFromStructAttribute($attribute)) instanceof StructModel) {
490 48
                        $annotationBlock
491 48
                            ->addChild(new PhpAnnotation(self::ANNOTATION_USES, sprintf('%s::%s()', $model->getPackagedName(true), StructEnum::METHOD_VALUE_IS_VALID)))
492 48
                            ->addChild(new PhpAnnotation(self::ANNOTATION_USES, sprintf('%s::%s()', $model->getPackagedName(true), StructEnum::METHOD_GET_VALID_VALUES)))
493 48
                            ->addChild(new PhpAnnotation(self::ANNOTATION_THROWS, \InvalidArgumentException::class))
494 48
                        ;
495 104
                    } elseif ($attribute->isArray()) {
496 58
                        $annotationBlock->addChild(new PhpAnnotation(self::ANNOTATION_THROWS, \InvalidArgumentException::class));
497
                    }
498
                }
499 108
                $this->addStructMethodsSetAnnotationBlock($annotationBlock, $this->getStructAttributeTypeSetAnnotation($attribute, false), lcfirst($attribute->getCleanName()));
500
501 108
                break;
502
503 108
            case 'get':
504 108
                if ($attribute->getRemovableFromRequest()) {
505 10
                    $annotationBlock->addChild('An additional test has been added (isset) before returning the property value as this property may have been unset before, due to the fact that this property is removable from the request (nillable=true+minOccurs=0)');
506
                }
507 108
                $this
508 108
                    ->addStructMethodsGetAnnotationBlockFromXmlAttribute($annotationBlock, $attribute)
509 108
                    ->addStructMethodsGetAnnotationBlock($annotationBlock, $this->getStructAttributeTypeGetAnnotation($attribute, true, $attribute->isAChoice()))
510 108
                ;
511
512 108
                break;
513
        }
514
515 108
        return $this;
516
    }
517
518 2
    protected function addStructMethodsSetAndGetAnnotationBlockFromScalar(string $setOrGet, PhpAnnotationBlock $annotationBlock, string $attributeName): self
519
    {
520
        switch ($setOrGet) {
521 2
            case 'set':
522 2
                $this->addStructMethodsSetAnnotationBlock($annotationBlock, lcfirst($attributeName), lcfirst($attributeName));
523
524 2
                break;
525
526 2
            case 'get':
527 2
                $this->addStructMethodsGetAnnotationBlock($annotationBlock, lcfirst($attributeName));
528
529 2
                break;
530
        }
531
532 2
        return $this;
533
    }
534
535 108
    protected function addStructMethodsSetAnnotationBlock(PhpAnnotationBlock $annotationBlock, string $type, string $name): self
536
    {
537 108
        $annotationBlock
538 108
            ->addChild(new PhpAnnotation(self::ANNOTATION_PARAM, sprintf('%s $%s', $type, $name)))
539 108
            ->addChild(new PhpAnnotation(self::ANNOTATION_RETURN, $this->getModel()->getPackagedName(true)))
540 108
        ;
541
542 108
        return $this;
543
    }
544
545 108
    protected function addStructMethodsGetAnnotationBlock(PhpAnnotationBlock $annotationBlock, string $attributeType): self
546
    {
547 108
        $annotationBlock->addChild(new PhpAnnotation(self::ANNOTATION_RETURN, $attributeType));
548
549 108
        return $this;
550
    }
551
552 108
    protected function addStructMethodsGetAnnotationBlockFromXmlAttribute(PhpAnnotationBlock $annotationBlock, StructAttributeModel $attribute): self
553
    {
554 108
        if ($attribute->isXml()) {
555 4
            $annotationBlock
556 4
                ->addChild(new PhpAnnotation(self::ANNOTATION_USES, '\DOMDocument::loadXML()'))
557 4
                ->addChild(new PhpAnnotation(self::ANNOTATION_PARAM, 'bool $asDomDocument true: returns \DOMDocument, false: returns XML string'))
558 4
            ;
559
        }
560
561 108
        return $this;
562
    }
563
564 108
    protected function addStructPropertiesToAnnotationBlock(PhpAnnotationBlock $annotationBlock): self
565
    {
566 108
        return $this
567 108
            ->addStructPropertiesToAnnotationBlockUses($annotationBlock)
568 108
            ->addStructPropertiesToAnnotationBlockParams($annotationBlock)
569 108
        ;
570
    }
571
572 108
    protected function addStructPropertiesToAnnotationBlockUses(PhpAnnotationBlock $annotationBlock): self
573
    {
574 108
        foreach ($this->getModelAttributes() as $attribute) {
575 108
            $annotationBlock->addChild(new PhpAnnotation(self::ANNOTATION_USES, sprintf('%s::%s()', $this->getModel()->getPackagedName(), $attribute->getSetterName())));
576
        }
577
578 108
        return $this;
579
    }
580
581 108
    protected function addStructPropertiesToAnnotationBlockParams(PhpAnnotationBlock $annotationBlock): self
582
    {
583 108
        foreach ($this->getModelAttributes() as $attribute) {
584 108
            $annotationBlock->addChild(new PhpAnnotation(self::ANNOTATION_PARAM, sprintf('%s $%s', $this->getStructAttributeTypeSetAnnotation($attribute, false), lcfirst($attribute->getCleanName()))));
585
        }
586
587 108
        return $this;
588
    }
589
590 40
    protected function getStructMethodsAddToAnnotationBlock(PhpMethod $method): PhpAnnotationBlock
591
    {
592 40
        $methodParameters = $method->getParameters();
593
594
        /** @var PhpFunctionParameter $firstParameter */
595 40
        $firstParameter = array_shift($methodParameters);
596 40
        $attribute = $this->getModel()->getAttribute($firstParameter->getModel()->getName());
597 40
        $annotationBlock = new PhpAnnotationBlock();
598 40
        if ($attribute instanceof StructAttributeModel) {
599 40
            $model = $this->getRestrictionFromStructAttribute($attribute);
600 40
            $annotationBlock->addChild(sprintf('Add item to %s value', $attribute->getCleanName()));
601 40
            if ($model instanceof StructModel) {
602 2
                $annotationBlock
603 2
                    ->addChild(new PhpAnnotation(self::ANNOTATION_USES, sprintf('%s::%s()', $model->getPackagedName(true), StructEnum::METHOD_VALUE_IS_VALID)))
604 2
                    ->addChild(new PhpAnnotation(self::ANNOTATION_USES, sprintf('%s::%s()', $model->getPackagedName(true), StructEnum::METHOD_GET_VALID_VALUES)))
605 2
                ;
606
            }
607 40
            $annotationBlock
608 40
                ->addChild(new PhpAnnotation(self::ANNOTATION_THROWS, \InvalidArgumentException::class))
609 40
                ->addChild(new PhpAnnotation(self::ANNOTATION_PARAM, sprintf('%s $item', $this->getStructAttributeTypeSetAnnotation($attribute, false, true))))
610 40
                ->addChild(new PhpAnnotation(self::ANNOTATION_RETURN, $this->getModel()->getPackagedName(true)))
611 40
            ;
612
        }
613
614 40
        return $annotationBlock;
615
    }
616
617 72
    protected function getStructMethodsValidateMethodAnnotationBlock(string $propertyName, string $constraintName, string $fromMethodName): PhpAnnotationBlock
618
    {
619 72
        $customConstraintMessage = '';
620 72
        $constraintArgName = 'array $values';
621
622 72
        switch (lcfirst($constraintName)) {
623 72
            case ArrayRule::NAME:
624 60
                $customConstraintMessage = 'This has to validate that each item contained by the array match the itemType constraint';
625
626 60
                break;
627
628 32
            case ChoiceRule::NAME:
629 8
                $customConstraintMessage = 'This has to validate that the property which is being set is the only one among the given choices';
630 8
                $constraintArgName = 'mixed $value';
631
632 8
                break;
633
634 24
            case LengthRule::NAME:
635 24
            case MaxLengthRule::NAME:
636 22
            case MinLengthRule::NAME:
637 6
                $customConstraintMessage = 'This has to validate that the items contained by the array match the length constraint';
638
639 6
                break;
640
641 18
            case PatternRule::NAME:
642 8
                $customConstraintMessage = 'This has to validate that the items contained by the array match the defined pattern';
643
644 8
                break;
645
646 10
            case UnionRule::NAME:
647 4
                $customConstraintMessage = sprintf('This is a set of validation rules based on the union types associated to the property %s', $propertyName);
648 4
                $constraintArgName = 'mixed $value';
649
650 4
                break;
651
        }
652
653 72
        return new PhpAnnotationBlock([
654 72
            new PhpAnnotation(
655 72
                PhpAnnotation::NO_NAME,
656 72
                sprintf(
657 72
                    'This method is responsible for validating the value(s) passed to the %s method',
658 72
                    lcfirst($fromMethodName)
659 72
                ),
660 72
                self::ANNOTATION_LONG_LENGTH
661 72
            ),
662 72
            new PhpAnnotation(
663 72
                PhpAnnotation::NO_NAME,
664 72
                sprintf(
665 72
                    'This method is willingly generated in order to preserve the one-line inline validation within the %s method',
666 72
                    lcfirst($fromMethodName)
667 72
                ),
668 72
                self::ANNOTATION_LONG_LENGTH
669 72
            ),
670 72
            new PhpAnnotation(
671 72
                PhpAnnotation::NO_NAME,
672 72
                $customConstraintMessage,
673 72
                self::ANNOTATION_LONG_LENGTH
674 72
            ),
675 72
            new PhpAnnotation(self::ANNOTATION_PARAM, $constraintArgName),
676 72
            new PhpAnnotation(
677 72
                self::ANNOTATION_RETURN,
678 72
                'string A non-empty message if the values does not match the validation rules'
679 72
            ),
680 72
        ]);
681
    }
682
683 108
    protected function applyRules(PhpMethod $method, StructAttributeModel $attribute, string $parameterName, bool $itemType = false): self
684
    {
685 108
        if ($this->getGenerator()->getOptionValidation()) {
686 106
            $rules = new Rules($this, $method, $attribute, $this->methods);
687 106
            $rules->applyRules($parameterName, $itemType);
688
        }
689
690 108
        return $this;
691
    }
692
}
693