writePropertyReferenceExpressionElementHeader()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 1
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace AlgoWeb\ODataMetadata\Csdl\Internal\Serialization;
6
7
use AlgoWeb\ODataMetadata\Csdl\Internal\EdmValueWriter;
8
use AlgoWeb\ODataMetadata\CsdlConstants;
9
use AlgoWeb\ODataMetadata\EdmConstants;
10
use AlgoWeb\ODataMetadata\EdmUtil;
11
use AlgoWeb\ODataMetadata\Enums\ConcurrencyMode;
12
use AlgoWeb\ODataMetadata\Enums\ExpressionKind;
13
use AlgoWeb\ODataMetadata\Enums\FunctionParameterMode;
14
use AlgoWeb\ODataMetadata\Enums\Multiplicity;
15
use AlgoWeb\ODataMetadata\Enums\OnDeleteAction;
16
use AlgoWeb\ODataMetadata\Enums\PrimitiveTypeKind;
17
use AlgoWeb\ODataMetadata\Exception\InvalidOperationException;
18
use AlgoWeb\ODataMetadata\Interfaces\Annotations\IDirectValueAnnotation;
19
use AlgoWeb\ODataMetadata\Interfaces\Annotations\IPropertyValueBinding;
20
use AlgoWeb\ODataMetadata\Interfaces\Annotations\ITypeAnnotation;
21
use AlgoWeb\ODataMetadata\Interfaces\Annotations\IValueAnnotation;
22
use AlgoWeb\ODataMetadata\Interfaces\Expressions\IApplyExpression;
23
use AlgoWeb\ODataMetadata\Interfaces\Expressions\IAssertTypeExpression;
24
use AlgoWeb\ODataMetadata\Interfaces\Expressions\IBinaryConstantExpression;
25
use AlgoWeb\ODataMetadata\Interfaces\Expressions\IBooleanConstantExpression;
26
use AlgoWeb\ODataMetadata\Interfaces\Expressions\ICollectionExpression;
27
use AlgoWeb\ODataMetadata\Interfaces\Expressions\IDateTimeConstantExpression;
28
use AlgoWeb\ODataMetadata\Interfaces\Expressions\IDateTimeOffsetConstantExpression;
29
use AlgoWeb\ODataMetadata\Interfaces\Expressions\IDecimalConstantExpression;
30
use AlgoWeb\ODataMetadata\Interfaces\Expressions\IEntitySetReferenceExpression;
31
use AlgoWeb\ODataMetadata\Interfaces\Expressions\IEnumMemberReferenceExpression;
32
use AlgoWeb\ODataMetadata\Interfaces\Expressions\IExpression;
33
use AlgoWeb\ODataMetadata\Interfaces\Expressions\IFloatingConstantExpression;
34
use AlgoWeb\ODataMetadata\Interfaces\Expressions\IFunctionReferenceExpression;
35
use AlgoWeb\ODataMetadata\Interfaces\Expressions\IGuidConstantExpression;
36
use AlgoWeb\ODataMetadata\Interfaces\Expressions\IIfExpression;
37
use AlgoWeb\ODataMetadata\Interfaces\Expressions\IIntegerConstantExpression;
38
use AlgoWeb\ODataMetadata\Interfaces\Expressions\IIsTypeExpression;
39
use AlgoWeb\ODataMetadata\Interfaces\Expressions\ILabeledExpression;
40
use AlgoWeb\ODataMetadata\Interfaces\Expressions\INullExpression;
41
use AlgoWeb\ODataMetadata\Interfaces\Expressions\IParameterReferenceExpression;
42
use AlgoWeb\ODataMetadata\Interfaces\Expressions\IPathExpression;
43
use AlgoWeb\ODataMetadata\Interfaces\Expressions\IPropertyReferenceExpression;
44
use AlgoWeb\ODataMetadata\Interfaces\Expressions\IRecordExpression;
45
use AlgoWeb\ODataMetadata\Interfaces\Expressions\IStringConstantExpression;
46
use AlgoWeb\ODataMetadata\Interfaces\Expressions\ITimeConstantExpression;
47
use AlgoWeb\ODataMetadata\Interfaces\Expressions\RecordExpression\IPropertyConstructor;
48
use AlgoWeb\ODataMetadata\Interfaces\IBinaryTypeReference;
49
use AlgoWeb\ODataMetadata\Interfaces\ICollectionType;
50
use AlgoWeb\ODataMetadata\Interfaces\IComplexType;
51
use AlgoWeb\ODataMetadata\Interfaces\IDecimalTypeReference;
52
use AlgoWeb\ODataMetadata\Interfaces\IDocumentation;
53
use AlgoWeb\ODataMetadata\Interfaces\IEntityContainer;
54
use AlgoWeb\ODataMetadata\Interfaces\IEntitySet;
55
use AlgoWeb\ODataMetadata\Interfaces\IEntityType;
56
use AlgoWeb\ODataMetadata\Interfaces\IEnumMember;
57
use AlgoWeb\ODataMetadata\Interfaces\IEnumType;
58
use AlgoWeb\ODataMetadata\Interfaces\IFunction;
59
use AlgoWeb\ODataMetadata\Interfaces\IFunctionImport;
60
use AlgoWeb\ODataMetadata\Interfaces\IFunctionParameter;
61
use AlgoWeb\ODataMetadata\Interfaces\IModel;
62
use AlgoWeb\ODataMetadata\Interfaces\INavigationProperty;
63
use AlgoWeb\ODataMetadata\Interfaces\IProperty;
64
use AlgoWeb\ODataMetadata\Interfaces\ISchemaElement;
65
use AlgoWeb\ODataMetadata\Interfaces\ISchemaType;
66
use AlgoWeb\ODataMetadata\Interfaces\ISpatialTypeReference;
67
use AlgoWeb\ODataMetadata\Interfaces\IStringTypeReference;
68
use AlgoWeb\ODataMetadata\Interfaces\IStructuralProperty;
69
use AlgoWeb\ODataMetadata\Interfaces\ITemporalTypeReference;
70
use AlgoWeb\ODataMetadata\Interfaces\ITerm;
71
use AlgoWeb\ODataMetadata\Interfaces\ITypeReference;
72
use AlgoWeb\ODataMetadata\Interfaces\IValueTerm;
73
use AlgoWeb\ODataMetadata\Interfaces\Values\IPrimitiveValue;
74
use AlgoWeb\ODataMetadata\Interfaces\Values\IStringValue;
75
use AlgoWeb\ODataMetadata\StringConst;
76
use AlgoWeb\ODataMetadata\Version;
77
use ReflectionFunction;
78
use ReflectionMethod;
79
use ReflectionNamedType;
80
use XMLWriter;
81
82
class EdmModelCsdlSchemaWriter implements IEdmModelCsdlSchemaWriter
83
{
84
    /**
85
     * @var XMLWriter
86
     */
87
    protected $xmlWriter;
88
    /**
89
     * @var Version
90
     */
91
    protected $version;
92
    /**
93
     * @var array<string, string>
94
     */
95
    private $namespaceAliasMappings;
96
    /**
97
     * @var IModel
98
     */
99
    private $model;
100
101
    /**
102
     * EdmModelCsdlSchemaWriter constructor.
103
     * @param IModel    $model
104
     * @param array     $namespaceAliasMappings
105
     * @param Version   $version
106
     * @param XMLWriter $xmlWriter
107
     */
108
    public function __construct(IModel $model, array $namespaceAliasMappings, Version $version, XMLWriter $xmlWriter)
109
    {
110
        $this->xmlWriter              = $xmlWriter;
111
        $this->version                = $version;
112
        $this->namespaceAliasMappings = $namespaceAliasMappings;
113
        $this->model                  = $model;
114
    }
115
116
    /**
117
     * @param  IValueTerm           $term
118
     * @param  bool                 $inlineType
119
     * @throws \ReflectionException
120
     */
121
    public function writeValueTermElementHeader(IValueTerm $term, bool $inlineType): void
122
    {
123
        $this->xmlWriter->startElement(CsdlConstants::Element_ValueTerm);
124
        $this->writeRequiredAttribute(
125
            CsdlConstants::Attribute_Name,
126
            $term->getName(),
127
            [EdmValueWriter::class, 'stringAsXml']
128
        );
129
        if ($inlineType && null !== $term->getType()) {
130
            $this->writeRequiredAttribute(
131
                CsdlConstants::Attribute_Type,
132
                $term->getType(),
133
                [$this, 'typeReferenceAsXml']
134
            );
135
        }
136
    }
137
138
    /**
139
     * @param  INavigationProperty  $navigationProperty
140
     * @throws \ReflectionException
141
     */
142
    public function writeAssociationElementHeader(INavigationProperty $navigationProperty): void
143
    {
144
        $this->xmlWriter->startElement(CsdlConstants::Element_Association);
145
        $this->writeRequiredAttribute(
146
            CsdlConstants::Attribute_Name,
147
            $this->model->getAssociationName($navigationProperty),
148
            [EdmValueWriter::class, 'stringAsXml']
149
        );
150
    }
151
152
    /**
153
     * @param  IEntitySet           $entitySet
154
     * @param  INavigationProperty  $navigationProperty
155
     * @throws \ReflectionException
156
     */
157
    public function writeAssociationSetElementHeader(
158
        IEntitySet $entitySet,
159
        INavigationProperty $navigationProperty
160
    ): void {
161
        $this->xmlWriter->startElement(CsdlConstants::Element_AssociationSet);
162
        $this->writeRequiredAttribute(
163
            CsdlConstants::Attribute_Name,
164
            $this->model->getAssociationSetName($entitySet, $navigationProperty),
165
            [EdmValueWriter::class, 'stringAsXml']
166
        );
167
        $this->writeRequiredAttribute(
168
            CsdlConstants::Attribute_Association,
169
            $this->model->getAssociationFullName($navigationProperty),
170
            [EdmValueWriter::class, 'stringAsXml']
171
        );
172
    }
173
174
    /**
175
     * @param  IComplexType         $complexType
176
     * @throws \ReflectionException
177
     */
178
    public function writeComplexTypeElementHeader(IComplexType $complexType): void
179
    {
180
        $this->xmlWriter->startElement(CsdlConstants::Element_ComplexType);
181
        $this->writeRequiredAttribute(
182
            CsdlConstants::Attribute_Name,
183
            $complexType->getName(),
184
            [EdmValueWriter::class, 'stringAsXml']
185
        );
186
        $this->writeOptionalAttribute(
187
            CsdlConstants::Attribute_BaseType,
188
            $complexType->baseComplexType(),
189
            null,
190
            [$this, 'typeDefinitionAsXml']
191
        );
192
        $this->writeOptionalAttribute(
193
            CsdlConstants::Attribute_Abstract,
194
            $complexType->isAbstract(),
195
            CsdlConstants::Default_Abstract,
196
            [EdmValueWriter::class, 'booleanAsXml']
197
        );
198
    }
199
200
    /**
201
     * @param  IEnumType            $enumType
202
     * @throws \ReflectionException
203
     */
204
    public function writeEnumTypeElementHeader(IEnumType $enumType): void
205
    {
206
        $this->xmlWriter->startElement(CsdlConstants::Element_EnumType);
207
        $this->writeRequiredAttribute(
208
            CsdlConstants::Attribute_Name,
209
            $enumType->getName(),
210
            [EdmValueWriter::class, 'stringAsXml']
211
        );
212
        if ($enumType->getUnderlyingType()->getPrimitiveKind() != PrimitiveTypeKind::Int32()) {
213
            $this->writeRequiredAttribute(
214
                CsdlConstants::Attribute_UnderlyingType,
215
                $enumType->getUnderlyingType(),
216
                [$this, 'typeDefinitionAsXml']
217
            );
218
        }
219
220
        $this->writeOptionalAttribute(
221
            CsdlConstants::Attribute_IsFlags,
222
            $enumType->isFlags(),
223
            CsdlConstants::Default_IsFlags,
224
            [EdmValueWriter::class, 'booleanAsXml']
225
        );
226
    }
227
228
    public function writeDocumentationElement(IDocumentation $documentation): void
229
    {
230
        $this->xmlWriter->startElement(CsdlConstants::Element_Documentation);
231
232
        $summary = $documentation->getSummary();
233
        if (null !== $summary && !empty($summary)) {
234
            $this->xmlWriter->startElement(CsdlConstants::Element_Summary);
235
            $this->xmlWriter->text($summary);
236
            $this->writeEndElement();
237
        }
238
239
        $descript = $documentation->getDescription();
240
        if (null !== $descript && !empty($descript)) {
241
            $this->xmlWriter->startElement(CsdlConstants::Element_LongDescription);
242
            $this->xmlWriter->text($descript);
243
            $this->writeEndElement();
244
        }
245
246
        $this->writeEndElement();
247
    }
248
249
    /**
250
     * @param  IEntitySet           $entitySet
251
     * @param  INavigationProperty  $property
252
     * @throws \ReflectionException
253
     */
254
    public function writeAssociationSetEndElementHeader(IEntitySet $entitySet, INavigationProperty $property): void
255
    {
256
        $this->xmlWriter->startElement(CsdlConstants::Element_End);
257
        $this->writeRequiredAttribute(
258
            CsdlConstants::Attribute_Role,
259
            $this->model->getAssociationEndName($property),
260
            [EdmValueWriter::class, 'stringAsXml']
261
        );
262
        $this->writeRequiredAttribute(
263
            CsdlConstants::Attribute_EntitySet,
264
            $entitySet->getName(),
265
            [EdmValueWriter::class, 'stringAsXml']
266
        );
267
    }
268
269
    /**
270
     * @param  INavigationProperty  $associationEnd
271
     * @throws \ReflectionException
272
     */
273
    public function writeAssociationEndElementHeader(INavigationProperty $associationEnd): void
274
    {
275
        $this->xmlWriter->startElement(CsdlConstants::Element_End);
276
        $declaringType = $associationEnd->getDeclaringType();
277
        assert($declaringType instanceof IEntityType);
278
        $this->writeRequiredAttribute(
279
            CsdlConstants::Attribute_Type,
280
            $declaringType->fullName(),
281
            [EdmValueWriter::class, 'stringAsXml']
282
        );
283
        $this->writeRequiredAttribute(
284
            CsdlConstants::Attribute_Role,
285
            $this->model->getAssociationEndName($associationEnd),
286
            [EdmValueWriter::class, 'stringAsXml']
287
        );
288
        $this->writeRequiredAttribute(
289
            CsdlConstants::Attribute_Multiplicity,
290
            $associationEnd->multiplicity(),
291
            [self::class, 'multiplicityAsXml']
292
        );
293
    }
294
295
    /**
296
     * @param  IEntityContainer     $container
297
     * @throws \ReflectionException
298
     */
299
    public function writeEntityContainerElementHeader(IEntityContainer $container): void
300
    {
301
        $this->xmlWriter->startElement(CsdlConstants::Element_EntityContainer);
302
        $this->writeRequiredAttribute(
303
            CsdlConstants::Attribute_Name,
304
            $container->getName(),
305
            [EdmValueWriter::class, 'stringAsXml']
306
        );
307
        if ($container->isDefault()) {
308
            $this->xmlWriter->writeAttributeNs(
309
                CsdlConstants::Prefix_ODataMetadata,
310
                CsdlConstants::Attribute_IsDefaultEntityContainer,
311
                null,
312
                'true'
313
            );
314
        }
315
        if ($container->isLazyLoadEnabled()) {
316
            $this->xmlWriter->writeAttributeNs(
317
                CsdlConstants::Prefix_Annotations,
318
                CsdlConstants::Attribute_LazyLoadingEnabled,
319
                null,
320
                'true'
321
            );
322
        }
323
    }
324
325
    /**
326
     * @param  IEntitySet           $entitySet
327
     * @throws \ReflectionException
328
     */
329
    public function writeEntitySetElementHeader(IEntitySet $entitySet): void
330
    {
331
        $this->xmlWriter->startElement(CsdlConstants::Element_EntitySet);
332
        $this->writeRequiredAttribute(
333
            CsdlConstants::Attribute_Name,
334
            $entitySet->getName(),
335
            [EdmValueWriter::class, 'stringAsXml']
336
        );
337
        $this->writeRequiredAttribute(
338
            CsdlConstants::Attribute_EntityType,
339
            $entitySet->getElementType()->fullName(),
340
            [EdmValueWriter::class, 'stringAsXml']
341
        );
342
    }
343
344
    /**
345
     * @param  IEntityType          $entityType
346
     * @throws \ReflectionException
347
     */
348
    public function writeEntityTypeElementHeader(IEntityType $entityType): void
349
    {
350
        $this->xmlWriter->startElement(CsdlConstants::Element_EntityType);
351
        $this->writeRequiredAttribute(
352
            CsdlConstants::Attribute_Name,
353
            $entityType->getName(),
354
            [EdmValueWriter::class, 'stringAsXml']
355
        );
356
        $this->writeOptionalAttribute(
357
            CsdlConstants::Attribute_BaseType,
358
            $entityType->baseEntityType(),
359
            null,
360
            [$this, 'typeDefinitionAsXml']
361
        );
362
        $this->writeOptionalAttribute(
363
            CsdlConstants::Attribute_Abstract,
364
            $entityType->isAbstract(),
365
            CsdlConstants::Default_Abstract,
366
            [EdmValueWriter::class, 'booleanAsXml']
367
        );
368
        $this->writeOptionalAttribute(
369
            CsdlConstants::Attribute_OpenType,
370
            $entityType->isOpen(),
371
            CsdlConstants::Default_OpenType,
372
            [EdmValueWriter::class, 'booleanAsXml']
373
        );
374
    }
375
376
    public function writeDeclaredKeyPropertiesElementHeader(): void
377
    {
378
        $this->xmlWriter->startElement(CsdlConstants::Element_Key);
379
    }
380
381
    /**
382
     * @param  IStructuralProperty  $property
383
     * @throws \ReflectionException
384
     */
385
    public function writePropertyRefElement(IStructuralProperty $property): void
386
    {
387
        $this->xmlWriter->startElement(CsdlConstants::Element_PropertyRef);
388
        $this->writeRequiredAttribute(
389
            CsdlConstants::Attribute_Name,
390
            $property->getName(),
391
            [EdmValueWriter::class, 'stringAsXml']
392
        );
393
        $this->writeEndElement();
394
    }
395
396
    /**
397
     * @param  INavigationProperty  $member
398
     * @throws \ReflectionException
399
     */
400
    public function writeNavigationPropertyElementHeader(INavigationProperty $member): void
401
    {
402
        $this->xmlWriter->startElement(CsdlConstants::Element_NavigationProperty);
403
        $this->writeRequiredAttribute(
404
            CsdlConstants::Attribute_Name,
405
            $member->getName(),
406
            [EdmValueWriter::class, 'stringAsXml']
407
        );
408
        $this->writeRequiredAttribute(
409
            CsdlConstants::Attribute_Relationship,
410
            $this->model->getAssociationFullName($member),
411
            [EdmValueWriter::class, 'stringAsXml']
412
        );
413
        $this->writeRequiredAttribute(
414
            CsdlConstants::Attribute_ToRole,
415
            $this->model->getAssociationEndName($member->getPartner()),
416
            [EdmValueWriter::class, 'stringAsXml']
417
        );
418
        $this->writeRequiredAttribute(
419
            CsdlConstants::Attribute_FromRole,
420
            $this->model->getAssociationEndName($member),
421
            [EdmValueWriter::class, 'stringAsXml']
422
        );
423
        $this->writeOptionalAttribute(
424
            CsdlConstants::Attribute_ContainsTarget,
425
            $member->containsTarget(),
426
            CsdlConstants::Default_ContainsTarget,
427
            [EdmValueWriter::class, 'booleanAsXml']
428
        );
429
    }
430
431
    /**
432
     * @param  string               $elementName
433
     * @param  OnDeleteAction       $operationAction
434
     * @throws \ReflectionException
435
     */
436
    public function writeOperationActionElement(string $elementName, OnDeleteAction $operationAction): void
437
    {
438
        $this->xmlWriter->startElement($elementName);
439
        $this->writeRequiredAttribute(
440
            CsdlConstants::Attribute_Action,
441
            strval($operationAction),
442
            [EdmValueWriter::class, 'stringAsXml']
443
        );
444
        $this->writeEndElement();
445
    }
446
447
    /**
448
     * @param  EdmSchema            $schema
449
     * @param  string|null          $alias
450
     * @param  array                $mappings
451
     * @throws \ReflectionException
452
     */
453
    public function writeSchemaElementHeader(EdmSchema $schema, ?string $alias, array $mappings): void
454
    {
455
        $xmlNamespace = self::getCsdlNamespace($this->version);
0 ignored issues
show
Unused Code introduced by
The assignment to $xmlNamespace is dead and can be removed.
Loading history...
456
        $this->xmlWriter->startElement(CsdlConstants::Element_Schema);
457
        $this->writeOptionalAttribute(
458
            CsdlConstants::Attribute_Namespace,
459
            $schema->getNamespace(),
460
            '',
461
            [EdmValueWriter::class, 'stringAsXml']
462
        );
463
        $this->writeOptionalAttribute(
464
            CsdlConstants::Attribute_Alias,
465
            $alias,
466
            null,
467
            [EdmValueWriter::class, 'stringAsXml']
468
        );
469
        foreach ($mappings as $mappingKey => $mappingValue) {
470
            $this->xmlWriter->writeAttributeNs(EdmConstants::XmlNamespacePrefix, $mappingKey, null, $mappingValue);
471
        }
472
    }
473
474
    /**
475
     * @param  string               $annotationsTarget
476
     * @throws \ReflectionException
477
     */
478
    public function writeAnnotationsElementHeader(string $annotationsTarget): void
479
    {
480
        $this->xmlWriter->startElement(CsdlConstants::Element_Annotations);
481
        $this->writeRequiredAttribute(
482
            CsdlConstants::Attribute_Target,
483
            $annotationsTarget,
484
            [EdmValueWriter::class, 'stringAsXml']
485
        );
486
    }
487
488
    /**
489
     * @param  IStructuralProperty  $property
490
     * @param  bool                 $inlineType
491
     * @throws \ReflectionException
492
     */
493
    public function writeStructuralPropertyElementHeader(IStructuralProperty $property, bool $inlineType): void
494
    {
495
        $this->xmlWriter->startElement(CsdlConstants::Element_Property);
496
        $this->writeRequiredAttribute(
497
            CsdlConstants::Attribute_Name,
498
            $property->getName(),
499
            [EdmValueWriter::class, 'stringAsXml']
500
        );
501
        if ($inlineType) {
502
            $this->writeRequiredAttribute(
503
                CsdlConstants::Attribute_Type,
504
                $property->getType(),
505
                [$this, 'typeReferenceAsXml']
506
            );
507
        }
508
509
        $this->writeOptionalAttribute(
510
            CsdlConstants::Attribute_ConcurrencyMode,
511
            $property->getConcurrencyMode(),
512
            CsdlConstants::$Default_ConcurrencyMode,
513
            [self::class, 'concurrencyModeAsXml']
514
        );
515
        $this->writeOptionalAttribute(
516
            CsdlConstants::Attribute_DefaultValue,
517
            $property->getDefaultValueString(),
518
            null,
519
            [EdmValueWriter::class, 'stringAsXml']
520
        );
521
    }
522
523
    /**
524
     * @param  IEnumMember          $member
525
     * @throws \ReflectionException
526
     */
527
    public function writeEnumMemberElementHeader(IEnumMember $member): void
528
    {
529
        $this->xmlWriter->startElement(CsdlConstants::Element_Member);
530
        $this->writeRequiredAttribute(
531
            CsdlConstants::Attribute_Name,
532
            $member->getName(),
533
            [EdmValueWriter::class, 'stringAsXml']
534
        );
535
        $isExplicit = $member->isValueExplicit($this->model);
536
        if (null === $isExplicit || $isExplicit) {
537
            $this->writeRequiredAttribute(
538
                CsdlConstants::Attribute_Value,
539
                $member->getValue(),
540
                [EdmValueWriter::class, 'primitiveValueAsXml']
541
            );
542
        }
543
    }
544
545
    /**
546
     * @param  ITypeReference       $reference
547
     * @throws \ReflectionException
548
     */
549
    public function writeNullableAttribute(ITypeReference $reference): void
550
    {
551
        $this->writeOptionalAttribute(
552
            CsdlConstants::Attribute_Nullable,
553
            $reference->getNullable(),
554
            CsdlConstants::Default_Nullable,
555
            [EdmValueWriter::class, 'booleanAsXml']
556
        );
557
    }
558
559
    /**
560
     * @param  IBinaryTypeReference $reference
561
     * @throws \ReflectionException
562
     */
563
    public function writeBinaryTypeAttributes(IBinaryTypeReference $reference): void
564
    {
565
        if ($reference->isUnBounded()) {
566
            $this->writeRequiredAttribute(
567
                CsdlConstants::Attribute_MaxLength,
568
                CsdlConstants::Value_Max,
569
                [EdmValueWriter::class, 'stringAsXml']
570
            );
571
        } else {
572
            $this->writeOptionalAttribute(
573
                CsdlConstants::Attribute_MaxLength,
574
                $reference->getMaxLength(),
575
                null,
576
                [EdmValueWriter::class, 'intAsXml']
577
            );
578
        }
579
580
        $this->writeOptionalAttribute(
581
            CsdlConstants::Attribute_FixedLength,
582
            $reference->isFixedLength(),
583
            null,
584
            [EdmValueWriter::class, 'booleanAsXml']
585
        );
586
    }
587
588
    /**
589
     * @param  IDecimalTypeReference $reference
590
     * @throws \ReflectionException
591
     */
592
    public function writeDecimalTypeAttributes(IDecimalTypeReference $reference): void
593
    {
594
        $this->writeOptionalAttribute(
595
            CsdlConstants::Attribute_Precision,
596
            $reference->getPrecision(),
597
            null,
598
            [EdmValueWriter::class, 'intAsXml']
599
        );
600
        $this->writeOptionalAttribute(
601
            CsdlConstants::Attribute_Scale,
602
            $reference->getScale(),
603
            null,
604
            [EdmValueWriter::class, 'intAsXml']
605
        );
606
    }
607
608
    /**
609
     * @param  ISpatialTypeReference $reference
610
     * @throws \ReflectionException
611
     */
612
    public function writeSpatialTypeAttributes(ISpatialTypeReference $reference): void
613
    {
614
        $this->writeRequiredAttribute(
615
            CsdlConstants::Attribute_Srid,
616
            $reference->getSpatialReferenceIdentifier(),
617
            [self::class, 'sridAsXml']
618
        );
619
    }
620
621
    /**
622
     * @param  IStringTypeReference $reference
623
     * @throws \ReflectionException
624
     */
625
    public function writeStringTypeAttributes(IStringTypeReference $reference): void
626
    {
627
        $this->writeOptionalAttribute(
628
            CsdlConstants::Attribute_Collation,
629
            $reference->getCollation(),
630
            null,
631
            [EdmValueWriter::class, 'stringAsXml']
632
        );
633
        if ($reference->isUnbounded()) {
634
            $this->writeRequiredAttribute(
635
                CsdlConstants::Attribute_MaxLength,
636
                CsdlConstants::Value_Max,
637
                [EdmValueWriter::class, 'stringAsXml']
638
            );
639
        } else {
640
            $this->writeOptionalAttribute(
641
                CsdlConstants::Attribute_MaxLength,
642
                $reference->getMaxLength(),
643
                null,
644
                [EdmValueWriter::class, 'intAsXml']
645
            );
646
        }
647
648
        $this->writeOptionalAttribute(
649
            CsdlConstants::Attribute_FixedLength,
650
            $reference->isFixedLength(),
651
            null,
652
            [EdmValueWriter::class, 'booleanAsXml']
653
        );
654
        $this->writeOptionalAttribute(
655
            CsdlConstants::Attribute_Unicode,
656
            $reference->isUnicode(),
657
            null,
658
            [EdmValueWriter::class, 'booleanAsXml']
659
        );
660
    }
661
662
    /**
663
     * @param  ITemporalTypeReference $reference
664
     * @throws \ReflectionException
665
     */
666
    public function writeTemporalTypeAttributes(ITemporalTypeReference $reference): void
667
    {
668
        $this->writeOptionalAttribute(
669
            CsdlConstants::Attribute_Precision,
670
            $reference->getPrecision(),
671
            null,
672
            [EdmValueWriter::class, 'intAsXml']
673
        );
674
    }
675
676
    public function writeReferentialConstraintElementHeader(INavigationProperty $constraint): void
677
    {
678
        $this->xmlWriter->startElement(CsdlConstants::Element_ReferentialConstraint);
679
    }
680
681
    /**
682
     * @param  INavigationProperty  $end
683
     * @throws \ReflectionException
684
     */
685
    public function writeReferentialConstraintPrincipalEndElementHeader(INavigationProperty $end): void
686
    {
687
        $this->xmlWriter->startElement(CsdlConstants::Element_Principal);
688
        $this->writeRequiredAttribute(
689
            CsdlConstants::Attribute_Role,
690
            $this->model->getAssociationEndName($end),
691
            [EdmValueWriter::class, 'stringAsXml']
692
        );
693
    }
694
695
    /**
696
     * @param  INavigationProperty  $end
697
     * @throws \ReflectionException
698
     */
699
    public function writeReferentialConstraintDependentEndElementHeader(INavigationProperty $end): void
700
    {
701
        $this->xmlWriter->startElement(CsdlConstants::Element_Dependent);
702
        $this->writeRequiredAttribute(
703
            CsdlConstants::Attribute_Role,
704
            $this->model->getAssociationEndName($end),
705
            [EdmValueWriter::class, 'stringAsXml']
706
        );
707
    }
708
709
    /**
710
     * @param  string               $usingNamespace
711
     * @param  string               $alias
712
     * @throws \ReflectionException
713
     */
714
    public function writeNamespaceUsingElement(string $usingNamespace, string $alias): void
715
    {
716
        $this->xmlWriter->startElement(CsdlConstants::Element_Using);
717
        $this->writeRequiredAttribute(
718
            CsdlConstants::Attribute_Namespace,
719
            $usingNamespace,
720
            [EdmValueWriter::class, 'stringAsXml']
721
        );
722
        $this->writeRequiredAttribute(CsdlConstants::Attribute_Alias, $alias, [EdmValueWriter::class, 'stringAsXml']);
723
        $this->writeEndElement();
724
    }
725
726
    /**
727
     * @param  IDirectValueAnnotation                                 $annotation
728
     * @throws \AlgoWeb\ODataMetadata\Exception\NotSupportedException
729
     */
730
    public function writeAnnotationStringAttribute(IDirectValueAnnotation $annotation): void
731
    {
732
        $edmValue = $annotation->getValue();
733
        if ($edmValue instanceof IPrimitiveValue) {
734
            $this->xmlWriter->writeAttributeNs(
735
                '',
736
                $annotation->getName(),
737
                $annotation->getNamespaceUri(),
738
                EdmValueWriter::primitiveValueAsXml($edmValue)
739
            );
740
        }
741
    }
742
743
    public function writeAnnotationStringElement(IDirectValueAnnotation $annotation): void
744
    {
745
        $edmValue = $annotation->getValue();
746
        if ($edmValue instanceof IStringValue) {
747
            $this->xmlWriter->writeRaw($edmValue->getValue());
748
        }
749
    }
750
751
    /**
752
     * @param  IFunction            $function
753
     * @param  bool                 $inlineReturnType
754
     * @throws \ReflectionException
755
     */
756
    public function writeFunctionElementHeader(IFunction $function, bool $inlineReturnType): void
757
    {
758
        $this->xmlWriter->startElement(CsdlConstants::Element_Function);
759
        $this->writeRequiredAttribute(
760
            CsdlConstants::Attribute_Name,
761
            $function->getName(),
762
            [EdmValueWriter::class, 'stringAsXml']
763
        );
764
        if ($inlineReturnType) {
765
            $this->writeRequiredAttribute(
766
                CsdlConstants::Attribute_ReturnType,
767
                $function->getReturnType(),
768
                [$this, 'typeReferenceAsXml']
769
            );
770
        }
771
    }
772
773
    public function writeDefiningExpressionElement(string $expression): void
774
    {
775
        $this->xmlWriter->startElement(CsdlConstants::Element_DefiningExpression);
776
        $this->xmlWriter->text($expression);
777
        $this->xmlWriter->endElement();
778
    }
779
780
    public function writeReturnTypeElementHeader()
781
    {
782
        $this->xmlWriter->startElement(CsdlConstants::Element_ReturnType);
783
    }
784
785
    /**
786
     * @param  IFunctionImport      $functionImport
787
     * @throws \ReflectionException
788
     */
789
    public function writeFunctionImportElementHeader(IFunctionImport $functionImport): void
790
    {
791
        // functionImport can't be Composable and sideEffecting at the same time.
792
        if ($functionImport->isComposable() && $functionImport->isSideEffecting()) {
793
            throw new InvalidOperationException(
794
                StringConst::EdmModel_Validator_Semantic_ComposableFunctionImportCannotBeSideEffecting(
795
                    $functionImport->getName()
796
                )
797
            );
798
        }
799
800
        $this->xmlWriter->startElement(CsdlConstants::Element_FunctionImport);
801
        $this->writeRequiredAttribute(
802
            CsdlConstants::Attribute_Name,
803
            $functionImport->getName(),
804
            [EdmValueWriter::class, 'stringAsXml']
805
        );
806
        $this->writeOptionalAttribute(
807
            CsdlConstants::Attribute_ReturnType,
808
            $functionImport->getReturnType(),
809
            null,
810
            [$this, 'typeReferenceAsXml']
811
        );
812
813
        // IsSideEffecting is optional, however its default applies to non-composable function imports only.
814
        // Composable function imports can't be side-effecting, so we don't emit false.
815
        if (!$functionImport->isComposable() &&
816
            $functionImport->isSideEffecting() != CsdlConstants::Default_IsSideEffecting) {
817
            $this->writeRequiredAttribute(
818
                CsdlConstants::Attribute_IsSideEffecting,
819
                $functionImport->isSideEffecting(),
820
                [EdmValueWriter::class, 'booleanAsXml']
821
            );
822
        }
823
824
        $this->writeOptionalAttribute(
825
            CsdlConstants::Attribute_IsComposable,
826
            $functionImport->isComposable(),
827
            CsdlConstants::Default_IsComposable,
828
            [EdmValueWriter::class, 'booleanAsXml']
829
        );
830
        $this->writeOptionalAttribute(
831
            CsdlConstants::Attribute_IsBindable,
832
            $functionImport->isBindable(),
833
            CsdlConstants::Default_IsBindable,
834
            [EdmValueWriter::class, 'booleanAsXml']
835
        );
836
        $entitySetReference = $functionImport->getEntitySet();
837
        if (null !== $functionImport->getEntitySet()) {
838
            if ($entitySetReference instanceof IEntitySetReferenceExpression) {
839
                $this->writeOptionalAttribute(
840
                    CsdlConstants::Attribute_EntitySet,
841
                    $entitySetReference->getReferencedEntitySet()->getName(),
842
                    null,
843
                    [EdmValueWriter::class, 'stringAsXml']
844
                );
845
            } else {
846
                $pathExpression = $functionImport->getEntitySet();
847
                if ($pathExpression instanceof IPathExpression) {
848
                    $this->writeOptionalAttribute(
849
                        CsdlConstants::Attribute_EntitySetPath,
850
                        $pathExpression->getPath(),
851
                        null,
852
                        [self::class, 'pathAsXml']
853
                    );
854
                } else {
855
                    throw new InvalidOperationException(
856
                        StringConst::EdmModel_Validator_Semantic_FunctionImportEntitySetExpressionIsInvalid(
857
                            $functionImport->getName()
858
                        )
859
                    );
860
                }
861
            }
862
        }
863
    }
864
865
    /**
866
     * @param  IFunctionParameter   $parameter
867
     * @param  bool                 $inlineType
868
     * @throws \ReflectionException
869
     */
870
    public function writeFunctionParameterElementHeader(IFunctionParameter $parameter, bool $inlineType): void
871
    {
872
        $this->xmlWriter->startElement(CsdlConstants::Element_Parameter);
873
        $this->writeRequiredAttribute(
874
            CsdlConstants::Attribute_Name,
875
            $parameter->getName(),
876
            [EdmValueWriter::class, 'stringAsXml']
877
        );
878
        if ($inlineType) {
879
            $this->writeRequiredAttribute(
880
                CsdlConstants::Attribute_Type,
881
                $parameter->getType(),
882
                [$this, 'typeReferenceAsXml']
883
            );
884
        }
885
886
        $this->writeOptionalAttribute(
887
            CsdlConstants::Attribute_Mode,
888
            $parameter->getMode(),
889
            CsdlConstants::$Default_FunctionParameterMode,
890
            [self::class, 'functionParameterModeAsXml']
891
        );
892
    }
893
894
    /**
895
     * @param  ICollectionType      $collectionType
896
     * @param  bool                 $inlineType
897
     * @throws \ReflectionException
898
     */
899
    public function writeCollectionTypeElementHeader(ICollectionType $collectionType, bool $inlineType): void
900
    {
901
        $this->xmlWriter->startElement(CsdlConstants::Element_CollectionType);
902
        if ($inlineType) {
903
            $this->writeRequiredAttribute(
904
                CsdlConstants::Attribute_ElementType,
905
                $collectionType->getElementType(),
906
                [$this, 'typeReferenceAsXml']
907
            );
908
        }
909
    }
910
911
    public function writeRowTypeElementHeader(): void
912
    {
913
        $this->xmlWriter->startElement(CsdlConstants::Element_RowType);
914
    }
915
916
    /**
917
     * @param  IExpression          $expression
918
     * @throws \ReflectionException
919
     */
920
    public function writeInlineExpression(IExpression $expression): void
921
    {
922
        if (method_exists($expression, 'getValue')) {
923
            EdmUtil::checkArgumentNull($expression->getValue(), 'expression->getValue');
924
        }
925
        switch ($expression->getExpressionKind()) {
926
            case ExpressionKind::BinaryConstant():
927
                assert($expression instanceof IBinaryConstantExpression);
928
                $this->writeRequiredAttribute(
929
                    CsdlConstants::Attribute_Binary,
930
                    $expression->getValue(),
931
                    [EdmValueWriter::class, 'binaryAsXml']
932
                );
933
                break;
934
            case ExpressionKind::BooleanConstant():
935
                assert($expression instanceof IBooleanConstantExpression);
936
                $this->writeRequiredAttribute(
937
                    CsdlConstants::Attribute_Bool,
938
                    $expression->getValue(),
939
                    [EdmValueWriter::class, 'booleanAsXml']
940
                );
941
                break;
942
            case ExpressionKind::DateTimeConstant():
943
                assert($expression instanceof IDateTimeConstantExpression);
944
                $this->writeRequiredAttribute(
945
                    CsdlConstants::Attribute_DateTime,
946
                    $expression->getValue(),
947
                    [EdmValueWriter::class, 'dateTimeAsXml']
948
                );
949
                break;
950
            case ExpressionKind::DateTimeOffsetConstant():
951
                assert($expression instanceof IDateTimeOffsetConstantExpression);
952
                $this->writeRequiredAttribute(
953
                    CsdlConstants::Attribute_DateTimeOffset,
954
                    $expression->getValue(),
955
                    [EdmValueWriter::class, 'dateTimeOffsetAsXml']
956
                );
957
                break;
958
            case ExpressionKind::DecimalConstant():
959
                assert($expression instanceof IDecimalConstantExpression);
960
                $this->writeRequiredAttribute(
961
                    CsdlConstants::Attribute_Decimal,
962
                    $expression->getValue(),
963
                    [EdmValueWriter::class, 'decimalAsXml']
964
                );
965
                break;
966
            case ExpressionKind::FloatingConstant():
967
                assert($expression instanceof IFloatingConstantExpression);
968
                $this->writeRequiredAttribute(
969
                    CsdlConstants::Attribute_Float,
970
                    $expression->getValue(),
971
                    [EdmValueWriter::class, 'floatAsXml']
972
                );
973
                break;
974
            case ExpressionKind::GuidConstant():
975
                assert($expression instanceof IGuidConstantExpression);
976
                $this->writeRequiredAttribute(
977
                    CsdlConstants::Attribute_Guid,
978
                    $expression->getValue(),
979
                    [EdmValueWriter::class, 'guidAsXml']
980
                );
981
                break;
982
            case ExpressionKind::IntegerConstant():
983
                assert($expression instanceof IIntegerConstantExpression);
984
                $this->writeRequiredAttribute(
985
                    CsdlConstants::Attribute_Int,
986
                    $expression->getValue(),
987
                    [EdmValueWriter::class, 'longAsXml']
988
                );
989
                break;
990
            case ExpressionKind::Path():
991
                assert($expression instanceof IPathExpression);
992
                $this->writeRequiredAttribute(
993
                    CsdlConstants::Attribute_Path,
994
                    $expression->getPath(),
995
                    [self::class, 'pathAsXml']
996
                );
997
                break;
998
            case ExpressionKind::StringConstant():
999
                assert($expression instanceof IStringConstantExpression);
1000
                $this->writeRequiredAttribute(
1001
                    CsdlConstants::Attribute_String,
1002
                    $expression->getValue(),
1003
                    [EdmValueWriter::class, 'stringAsXml']
1004
                );
1005
                break;
1006
            case ExpressionKind::TimeConstant():
1007
                assert($expression instanceof ITimeConstantExpression);
1008
                $this->writeRequiredAttribute(
1009
                    CsdlConstants::Attribute_Time,
1010
                    $expression->getValue(),
1011
                    [EdmValueWriter::class, 'timeAsXml']
1012
                );
1013
                break;
1014
            default:
1015
                throw new InvalidOperationException(
1016
                    StringConst::UnknownEnumVal_ExpressionKind($expression->getExpressionKind()->getKey())
1017
                );
1018
        }
1019
    }
1020
1021
    /**
1022
     * @param  IValueAnnotation     $annotation
1023
     * @param  bool                 $isInline
1024
     * @throws \ReflectionException
1025
     */
1026
    public function writeValueAnnotationElementHeader(IValueAnnotation $annotation, bool $isInline): void
1027
    {
1028
        $this->xmlWriter->startElement(CsdlConstants::Element_ValueAnnotation);
1029
        $this->writeRequiredAttribute(CsdlConstants::Attribute_Term, $annotation->getTerm(), [$this, 'termAsXml']);
1030
        $this->writeOptionalAttribute(
1031
            CsdlConstants::Attribute_Qualifier,
1032
            $annotation->getQualifier(),
1033
            null,
1034
            [EdmValueWriter::class, 'stringAsXml']
1035
        );
1036
        if ($isInline) {
1037
            $this->writeInlineExpression($annotation->getValue());
1038
        }
1039
    }
1040
1041
    /**
1042
     * @param  ITypeAnnotation      $annotation
1043
     * @throws \ReflectionException
1044
     */
1045
    public function writeTypeAnnotationElementHeader(ITypeAnnotation $annotation): void
1046
    {
1047
        $this->xmlWriter->startElement(CsdlConstants::Element_TypeAnnotation);
1048
        $this->writeRequiredAttribute(CsdlConstants::Attribute_Term, $annotation->getTerm(), [$this, 'termAsXml']);
1049
        $this->writeOptionalAttribute(
1050
            CsdlConstants::Attribute_Qualifier,
1051
            $annotation->getQualifier(),
1052
            null,
1053
            [EdmValueWriter::class, 'stringAsXml']
1054
        );
1055
    }
1056
1057
    /**
1058
     * @param  IPropertyValueBinding $value
1059
     * @param  bool                  $isInline
1060
     * @throws \ReflectionException
1061
     */
1062
    public function writePropertyValueElementHeader(IPropertyValueBinding $value, bool $isInline): void
1063
    {
1064
        $this->xmlWriter->startElement(CsdlConstants::Element_PropertyValue);
1065
        $this->writeRequiredAttribute(
1066
            CsdlConstants::Attribute_Property,
1067
            $value->getBoundProperty()->getName(),
1068
            [EdmValueWriter::class, 'stringAsXml']
1069
        );
1070
        if ($isInline) {
1071
            $this->writeInlineExpression($value->getValue());
1072
        }
1073
    }
1074
1075
    /**
1076
     * @param  IRecordExpression    $expression
1077
     * @throws \ReflectionException
1078
     */
1079
    public function writeRecordExpressionElementHeader(IRecordExpression $expression)
1080
    {
1081
        $this->xmlWriter->startElement(CsdlConstants::Element_Record);
1082
        $this->writeOptionalAttribute(
1083
            CsdlConstants::Attribute_Type,
1084
            $expression->getDeclaredType(),
1085
            null,
1086
            [$this, 'typeReferenceAsXml']
1087
        );
1088
    }
1089
1090
    /**
1091
     * @param  IPropertyConstructor $constructor
1092
     * @param  bool                 $isInline
1093
     * @throws \ReflectionException
1094
     */
1095
    public function writePropertyConstructorElementHeader(IPropertyConstructor $constructor, bool $isInline): void
1096
    {
1097
        EdmUtil::checkArgumentNull($constructor->getName(), 'constructor->getName');
1098
        $this->xmlWriter->startElement(CsdlConstants::Element_PropertyValue);
1099
        $this->writeRequiredAttribute(
1100
            CsdlConstants::Attribute_Property,
1101
            $constructor->getName(),
1102
            [EdmValueWriter::class, 'stringAsXml']
1103
        );
1104
        if ($isInline) {
1105
            EdmUtil::checkArgumentNull($constructor->getValue(), 'constructor->getValue()');
1106
            $this->writeInlineExpression($constructor->getValue());
1107
        }
1108
    }
1109
1110
    public function writeStringConstantExpressionElement(IStringConstantExpression $expression): void
1111
    {
1112
        EdmUtil::checkArgumentNull($expression->getValue(), 'expression->getValue');
1113
        $this->xmlWriter->startElement(CsdlConstants::Element_String);
1114
        $this->xmlWriter->text(EdmValueWriter::stringAsXml($expression->getValue()));
1115
        $this->writeEndElement();
1116
    }
1117
1118
    public function writeBinaryConstantExpressionElement(IBinaryConstantExpression $expression): void
1119
    {
1120
        EdmUtil::checkArgumentNull($expression->getValue(), 'expression->getValue');
1121
        $this->xmlWriter->startElement(CsdlConstants::Element_String);
1122
        $this->xmlWriter->text(EdmValueWriter::binaryAsXml($expression->getValue()));
1123
        $this->writeEndElement();
1124
    }
1125
1126
    public function writeBooleanConstantExpressionElement(IBooleanConstantExpression $expression): void
1127
    {
1128
        $this->xmlWriter->startElement(CsdlConstants::Element_Bool);
1129
        $this->xmlWriter->text(EdmValueWriter::booleanAsXml($expression->getValue()));
1130
        $this->writeEndElement();
1131
    }
1132
1133
    public function writeNullConstantExpressionElement(INullExpression $expression): void
1134
    {
1135
        $this->xmlWriter->startElement(CsdlConstants::Element_Null);
1136
        $this->writeEndElement();
1137
    }
1138
1139
    public function writeDateTimeConstantExpressionElement(IDateTimeConstantExpression $expression): void
1140
    {
1141
        $this->xmlWriter->startElement(CsdlConstants::Element_DateTime);
1142
        $this->xmlWriter->text(EdmValueWriter::dateTimeAsXml($expression->getValue()));
1143
        $this->writeEndElement();
1144
    }
1145
1146
    public function writeDateTimeOffsetConstantExpressionElement(IDateTimeOffsetConstantExpression $expression): void
1147
    {
1148
        $this->xmlWriter->startElement(CsdlConstants::Element_DateTimeOffset);
1149
        $this->xmlWriter->text(EdmValueWriter::dateTimeOffsetAsXml($expression->getValue()));
1150
        $this->writeEndElement();
1151
    }
1152
1153
    public function writeDecimalConstantExpressionElement(IDecimalConstantExpression $expression): void
1154
    {
1155
        $this->xmlWriter->startElement(CsdlConstants::Element_Decimal);
1156
        $this->xmlWriter->text(EdmValueWriter::decimalAsXml($expression->getValue()));
1157
        $this->writeEndElement();
1158
    }
1159
1160
    public function writeFloatingConstantExpressionElement(IFloatingConstantExpression $expression): void
1161
    {
1162
        $this->xmlWriter->startElement(CsdlConstants::Element_Float);
1163
        $this->xmlWriter->text(EdmValueWriter::floatAsXml($expression->getValue()));
1164
        $this->writeEndElement();
1165
    }
1166
1167
    /**
1168
     * @param  IApplyExpression     $expression
1169
     * @param  bool                 $isFunction
1170
     * @throws \ReflectionException
1171
     */
1172
    public function writeFunctionApplicationElementHeader(IApplyExpression $expression, bool $isFunction): void
1173
    {
1174
        $this->xmlWriter->startElement(CsdlConstants::Element_Apply);
1175
        if ($isFunction) {
1176
            $appliedFunction = $expression->getAppliedFunction();
1177
            assert($appliedFunction instanceof IFunctionReferenceExpression);
1178
            $this->writeRequiredAttribute(
1179
                CsdlConstants::Attribute_Function,
1180
                $appliedFunction->getReferencedFunction(),
1181
                [$this, 'functionAsXml']
1182
            );
1183
        }
1184
    }
1185
1186
    public function writeGuidConstantExpressionElement(IGuidConstantExpression $expression): void
1187
    {
1188
        $this->xmlWriter->startElement(CsdlConstants::Element_Guid);
1189
        $this->xmlWriter->text(EdmValueWriter::guidAsXml($expression->getValue()));
1190
        $this->writeEndElement();
1191
    }
1192
1193
    public function writeIntegerConstantExpressionElement(IIntegerConstantExpression $expression): void
1194
    {
1195
        $this->xmlWriter->startElement(CsdlConstants::Element_Int);
1196
        $this->xmlWriter->text(EdmValueWriter::longAsXml($expression->getValue()));
1197
        $this->writeEndElement();
1198
    }
1199
1200
    public function writePathExpressionElement(IPathExpression $expression): void
1201
    {
1202
        $this->xmlWriter->startElement(CsdlConstants::Element_Path);
1203
        $this->xmlWriter->text(self::pathAsXml($expression->getPath()));
1204
        $this->writeEndElement();
1205
    }
1206
1207
    public function writeIfExpressionElementHeader(IIfExpression $expression): void
1208
    {
1209
        $this->xmlWriter->startElement(CsdlConstants::Element_If);
1210
    }
1211
1212
    public function writeCollectionExpressionElementHeader(ICollectionExpression $expression): void
1213
    {
1214
        $this->xmlWriter->startElement(CsdlConstants::Element_Collection);
1215
    }
1216
1217
    /**
1218
     * @param  ILabeledExpression   $labeledElement
1219
     * @throws \ReflectionException
1220
     */
1221
    public function writeLabeledElementHeader(ILabeledExpression $labeledElement): void
1222
    {
1223
        $this->xmlWriter->startElement(CsdlConstants::Element_LabeledElement);
1224
        $this->writeRequiredAttribute(
1225
            CsdlConstants::Attribute_Name,
1226
            $labeledElement->getName(),
1227
            [EdmValueWriter::class, 'stringAsXml']
1228
        );
1229
    }
1230
1231
    /**
1232
     * @param  IIsTypeExpression    $expression
1233
     * @param  bool                 $inlineType
1234
     * @throws \ReflectionException
1235
     */
1236
    public function writeIsTypeExpressionElementHeader(IIsTypeExpression $expression, bool $inlineType): void
1237
    {
1238
        $this->xmlWriter->startElement(CsdlConstants::Element_IsType);
1239
        if ($inlineType) {
1240
            $this->writeRequiredAttribute(
1241
                CsdlConstants::Attribute_Type,
1242
                $expression->getType(),
1243
                [$this, 'typeReferenceAsXml']
1244
            );
1245
        }
1246
    }
1247
1248
    /**
1249
     * @param  IAssertTypeExpression $expression
1250
     * @param  bool                  $inlineType
1251
     * @throws \ReflectionException
1252
     */
1253
    public function writeAssertTypeExpressionElementHeader(IAssertTypeExpression $expression, bool $inlineType): void
1254
    {
1255
        $this->xmlWriter->startElement(CsdlConstants::Element_AssertType);
1256
        if ($inlineType) {
1257
            $this->writeRequiredAttribute(
1258
                CsdlConstants::Attribute_Type,
1259
                $expression->getType(),
1260
                [$this, 'typeReferenceAsXml']
1261
            );
1262
        }
1263
    }
1264
1265
    /**
1266
     * @param  IEntitySetReferenceExpression $expression
1267
     * @throws \ReflectionException
1268
     */
1269
    public function writeEntitySetReferenceExpressionElement(IEntitySetReferenceExpression $expression): void
1270
    {
1271
        $this->xmlWriter->startElement(CsdlConstants::Element_EntitySetReference);
1272
        $this->writeRequiredAttribute(
1273
            CsdlConstants::Attribute_Name,
1274
            $expression->getReferencedEntitySet(),
1275
            [self::class, 'entitySetAsXml']
1276
        );
1277
        $this->writeEndElement();
1278
    }
1279
1280
    /**
1281
     * @param  IParameterReferenceExpression $expression
1282
     * @throws \ReflectionException
1283
     */
1284
    public function writeParameterReferenceExpressionElement(IParameterReferenceExpression $expression): void
1285
    {
1286
        $this->xmlWriter->startElement(CsdlConstants::Element_ParameterReference);
1287
        $this->writeRequiredAttribute(
1288
            CsdlConstants::Attribute_Name,
1289
            $expression->getReferencedParameter(),
1290
            [self::class, 'parameterAsXml']
1291
        );
1292
        $this->writeEndElement();
1293
    }
1294
1295
    /**
1296
     * @param  IFunctionReferenceExpression $expression
1297
     * @throws \ReflectionException
1298
     */
1299
    public function writeFunctionReferenceExpressionElement(IFunctionReferenceExpression $expression): void
1300
    {
1301
        $this->xmlWriter->startElement(CsdlConstants::Element_FunctionReference);
1302
        $this->writeRequiredAttribute(
1303
            CsdlConstants::Attribute_Name,
1304
            $expression->getReferencedFunction(),
1305
            [$this, 'functionAsXml']
1306
        );
1307
        $this->writeEndElement();
1308
    }
1309
1310
    /**
1311
     * @param  IEnumMemberReferenceExpression $expression
1312
     * @throws \ReflectionException
1313
     */
1314
    public function writeEnumMemberReferenceExpressionElement(IEnumMemberReferenceExpression $expression): void
1315
    {
1316
        $this->xmlWriter->startElement(CsdlConstants::Element_EnumMemberReference);
1317
        $this->writeRequiredAttribute(
1318
            CsdlConstants::Attribute_Name,
1319
            $expression->getReferencedEnumMember(),
1320
            [self::class, 'enumMemberAsXml']
1321
        );
1322
        $this->writeEndElement();
1323
    }
1324
1325
    /**
1326
     * @param  IPropertyReferenceExpression $expression
1327
     * @throws \ReflectionException
1328
     */
1329
    public function writePropertyReferenceExpressionElementHeader(IPropertyReferenceExpression $expression): void
1330
    {
1331
        $this->xmlWriter->startElement(CsdlConstants::Element_PropertyReference);
1332
        $this->writeRequiredAttribute(
1333
            CsdlConstants::Attribute_Name,
1334
            $expression->getReferencedProperty(),
1335
            [self::class, 'propertyAsXml']
1336
        );
1337
    }
1338
1339
    public function writeEndElement(): void
1340
    {
1341
        $this->xmlWriter->endElement();
1342
    }
1343
1344
    /**
1345
     * @param  string               $attribute
1346
     * @param  mixed                $value
1347
     * @param  mixed                $defaultValue
1348
     * @param  callable             $toXml
1349
     * @throws \ReflectionException
1350
     */
1351
    public function writeOptionalAttribute(string $attribute, $value, $defaultValue, callable $toXml): void
1352
    {
1353
        $stem = is_array($toXml) ? new ReflectionMethod(...$toXml) :
0 ignored issues
show
Bug introduced by
$toXml is expanded, but the parameter $objectOrMethod of ReflectionMethod::__construct() does not expect variable arguments. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1353
        $stem = is_array($toXml) ? new ReflectionMethod(/** @scrutinizer ignore-type */ ...$toXml) :
Loading history...
1354
            new ReflectionFunction($toXml);
1355
        /* @noinspection PhpUnhandledExceptionInspection suppressing exceptions for asserts.*/
1356
        assert(
1357
            1 === count(($stem)->getParameters()),
1358
            '$toXml should be a callable taking one parameter of mixed type'
1359
        );
1360
        $stemType = $stem->getReturnType();
1361
        $name     = $stemType instanceof ReflectionNamedType ?
1362
            $stemType->getName() :
1363
            strval($stemType);
1364
        /* @noinspection PhpUnhandledExceptionInspection suppressing exceptions for asserts.*/
1365
        assert(
1366
            'string' === $name,
1367
            '$toXml should be a callable returning a string'
1368
        );
1369
        if ($value !== $defaultValue) {
1370
            $this->xmlWriter->writeAttribute($attribute, $toXml($value));
1371
        }
1372
    }
1373
1374
    /**
1375
     * @param  string               $attribute
1376
     * @param  mixed                $value
1377
     * @param  callable             $toXml
1378
     * @throws \ReflectionException
1379
     */
1380
    public function writeRequiredAttribute(string $attribute, $value, callable $toXml): void
1381
    {
1382
        $stem = is_array($toXml) ? new ReflectionMethod(...$toXml) :
0 ignored issues
show
Bug introduced by
$toXml is expanded, but the parameter $objectOrMethod of ReflectionMethod::__construct() does not expect variable arguments. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1382
        $stem = is_array($toXml) ? new ReflectionMethod(/** @scrutinizer ignore-type */ ...$toXml) :
Loading history...
1383
            new ReflectionFunction($toXml);
1384
        /* @noinspection PhpUnhandledExceptionInspection suppressing exceptions for asserts.*/
1385
        assert(
1386
            1 === count($stem->getParameters()),
1387
            '$toXml should be a callable taking one parameter of mixed type'
1388
        );
1389
        $stemType = $stem->getReturnType();
1390
        $name     = $stemType instanceof ReflectionNamedType ?
1391
            $stemType->getName() :
1392
            strval($stemType);
1393
        /* @noinspection PhpUnhandledExceptionInspection suppressing exceptions for asserts.*/
1394
        assert(
1395
            'string' === $name,
1396
            '$toXml should be a callable returning a string'
1397
        );
1398
        $this->xmlWriter->writeAttribute($attribute, $toXml($value));
1399
    }
1400
1401
    /**
1402
     * @param  Multiplicity              $endKind
1403
     * @throws InvalidOperationException
1404
     * @return string
1405
     */
1406
    private static function multiplicityAsXml(Multiplicity $endKind): string
1407
    {
1408
        switch ($endKind) {
1409
            case Multiplicity::Many():
1410
                return CsdlConstants::Value_EndMany;
1411
            case Multiplicity::One():
1412
                return CsdlConstants::Value_EndRequired;
1413
            case Multiplicity::ZeroOrOne():
1414
                return CsdlConstants::Value_EndOptional;
1415
            default:
1416
                throw new InvalidOperationException(StringConst::UnknownEnumVal_Multiplicity($endKind->getKey()));
1417
        }
1418
    }
1419
1420
    /**
1421
     * @param  FunctionParameterMode     $mode
1422
     * @throws InvalidOperationException
1423
     * @return string
1424
     */
1425
    private static function functionParameterModeAsXml(FunctionParameterMode $mode): string
1426
    {
1427
        switch ($mode) {
1428
            case FunctionParameterMode::In():
1429
                return CsdlConstants::Value_ModeIn;
1430
            case FunctionParameterMode::InOut():
1431
                return CsdlConstants::Value_ModeInOut;
1432
            case FunctionParameterMode::Out():
1433
                return CsdlConstants::Value_ModeOut;
1434
            default:
1435
                throw new InvalidOperationException(StringConst::UnknownEnumVal_FunctionParameterMode($mode->getKey()));
1436
        }
1437
    }
1438
1439
    /**
1440
     * @param  ConcurrencyMode           $mode
1441
     * @throws InvalidOperationException
1442
     * @return string
1443
     */
1444
    private static function concurrencyModeAsXml(ConcurrencyMode $mode): string
1445
    {
1446
        switch ($mode) {
1447
            case ConcurrencyMode::Fixed():
1448
                return CsdlConstants::Value_Fixed;
1449
            case ConcurrencyMode::None():
1450
                return CsdlConstants::Value_None;
1451
            default:
1452
                throw new InvalidOperationException(StringConst::UnknownEnumVal_ConcurrencyMode($mode->getKey()));
1453
        }
1454
    }
1455
1456
    /**
1457
     * @param  string[] $path
1458
     * @return string
1459
     */
1460
    private static function pathAsXml(array $path): string
1461
    {
1462
        return implode('/', $path);
1463
    }
1464
1465
    private static function parameterAsXml(IFunctionParameter $parameter): string
1466
    {
1467
        return $parameter->getName() ?? '';
1468
    }
1469
1470
    private static function propertyAsXml(IProperty $property): string
1471
    {
1472
        return $property->getName() ?? '';
1473
    }
1474
1475
    private static function enumMemberAsXml(IEnumMember $member): string
1476
    {
1477
        return $member->getDeclaringType()->fullName() . '/' . $member->getName();
1478
    }
1479
1480
    private static function entitySetAsXml(IEntitySet $set): string
1481
    {
1482
        $stem = $set->getContainer() ? $set->getContainer()->fullName() : '';
1483
1484
        return $stem . '/' . $set->getName();
1485
    }
1486
1487
    private static function sridAsXml(?int $i): string
1488
    {
1489
        return $i !== null ? strval($i) : CsdlConstants::Value_SridVariable;
1490
    }
1491
1492
    /**
1493
     * @param  Version                   $edmVersion
1494
     * @throws InvalidOperationException
1495
     * @return string
1496
     */
1497
    private static function getCsdlNamespace(Version $edmVersion): string
1498
    {
1499
        $namespaces = CsdlConstants::versionToEdmNamespace($edmVersion);
1500
        if ($namespaces !== null) {
1501
            return $namespaces;
1502
        }
1503
1504
        throw new InvalidOperationException(StringConst::Serializer_UnknownEdmVersion());
1505
    }
1506
1507
    private function serializationName(ISchemaElement $element): string
1508
    {
1509
        EdmUtil::checkArgumentNull($element->getNamespace(), 'element->getNamespace');
1510
        if ($this->namespaceAliasMappings != null) {
1511
            if (array_key_exists($element->getNamespace(), $this->namespaceAliasMappings)) {
1512
                return $this->namespaceAliasMappings[$element->getNamespace()] . '.' . $element->getName();
1513
            }
1514
        }
1515
1516
        return $element->fullName();
1517
    }
1518
1519
    private function typeReferenceAsXml(ITypeReference $type): string
1520
    {
1521
        if ($type->isCollection()) {
1522
            $collectionReference   = $type->asCollection();
1523
            $elementTypeDefinition = $collectionReference->elementType()->getDefinition();
1524
            assert(
1525
                $elementTypeDefinition instanceof ISchemaElement,
1526
                'Cannot inline parameter type if not a named element or collection of named elements'
1527
            );
1528
            return CsdlConstants::Value_Collection . '(' . $this->serializationName($elementTypeDefinition) . ')';
1529
        } elseif ($type->isEntityReference()) {
1530
            $entityReferenceDefinitionType = $type->asEntityReference()->entityReferenceDefinition()->getEntityType();
1531
            return CsdlConstants::Value_Ref . '(' . $this->serializationName($entityReferenceDefinitionType) . ')';
0 ignored issues
show
Bug introduced by
It seems like $entityReferenceDefinitionType can also be of type null; however, parameter $element of AlgoWeb\ODataMetadata\Cs...er::serializationName() does only seem to accept AlgoWeb\ODataMetadata\Interfaces\ISchemaElement, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1531
            return CsdlConstants::Value_Ref . '(' . $this->serializationName(/** @scrutinizer ignore-type */ $entityReferenceDefinitionType) . ')';
Loading history...
1532
        }
1533
        $typeDefinition = $type->getDefinition();
1534
        assert(
1535
            $typeDefinition instanceof ISchemaElement,
1536
            'Cannot inline parameter type if not a named element or collection of named elements'
1537
        );
1538
        return $this->serializationName($typeDefinition);
1539
    }
1540
1541
    private function typeDefinitionAsXml(ISchemaType $type): string
1542
    {
1543
        return $this->serializationName($type);
1544
    }
1545
1546
    private function functionAsXml(IFunction $function): string
1547
    {
1548
        return $this->serializationName($function);
1549
    }
1550
1551
    private function termAsXml(?ITerm $term): string
1552
    {
1553
        if ($term == null) {
1554
            return '';
1555
        }
1556
1557
        return $this->serializationName($term);
1558
    }
1559
}
1560