1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace POData\Providers\Metadata; |
4
|
|
|
|
5
|
|
|
use InvalidArgumentException; |
6
|
|
|
use POData\Common\InvalidOperationException; |
7
|
|
|
use POData\Common\Messages; |
8
|
|
|
use POData\Providers\Metadata\Type\Binary; |
9
|
|
|
use POData\Providers\Metadata\Type\Boolean; |
10
|
|
|
use POData\Providers\Metadata\Type\Byte; |
11
|
|
|
use POData\Providers\Metadata\Type\DateTime; |
12
|
|
|
use POData\Providers\Metadata\Type\Decimal; |
13
|
|
|
use POData\Providers\Metadata\Type\Double; |
14
|
|
|
use POData\Providers\Metadata\Type\EdmPrimitiveType; |
15
|
|
|
use POData\Providers\Metadata\Type\Guid; |
16
|
|
|
use POData\Providers\Metadata\Type\Int16; |
17
|
|
|
use POData\Providers\Metadata\Type\Int32; |
18
|
|
|
use POData\Providers\Metadata\Type\Int64; |
19
|
|
|
use POData\Providers\Metadata\Type\IType; |
20
|
|
|
use POData\Providers\Metadata\Type\SByte; |
21
|
|
|
use POData\Providers\Metadata\Type\Single; |
22
|
|
|
use POData\Providers\Metadata\Type\StringType; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* Class ResourceType. |
26
|
|
|
* |
27
|
|
|
* A type to describe an entity type, complex type or primitive type. |
28
|
|
|
*/ |
29
|
|
|
abstract class ResourceType |
30
|
|
|
{ |
31
|
|
|
/** |
32
|
|
|
* Name of the resource described by this class instance. |
33
|
|
|
* |
34
|
|
|
* @var string |
35
|
|
|
*/ |
36
|
|
|
private $name; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* Namespace name in which resource described by this class instance |
40
|
|
|
* belongs to. |
41
|
|
|
* |
42
|
|
|
* @var string |
43
|
|
|
*/ |
44
|
|
|
private $namespaceName; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* The fully qualified name of the resource described by this class instance. |
48
|
|
|
* |
49
|
|
|
* @var string |
50
|
|
|
*/ |
51
|
|
|
private $fullName; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* The type the resource described by this class instance. |
55
|
|
|
* Note: either Entity or Complex Type. |
56
|
|
|
* |
57
|
|
|
* @var ResourceTypeKind |
58
|
|
|
*/ |
59
|
|
|
protected $resourceTypeKind; |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* @var bool |
63
|
|
|
*/ |
64
|
|
|
private $abstractType; |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* Refrence to ResourceType instance for base type, if any. |
68
|
|
|
* |
69
|
|
|
* @var ResourceType |
70
|
|
|
*/ |
71
|
|
|
private $baseType; |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* Collection of ResourceProperty for all properties declared on the |
75
|
|
|
* resource described by this class instance (This does not include |
76
|
|
|
* base type properties). |
77
|
|
|
* |
78
|
|
|
* @var ResourceProperty[] indexed by name |
79
|
|
|
*/ |
80
|
|
|
private $propertiesDeclaredOnThisType = []; |
81
|
|
|
|
82
|
|
|
/** |
83
|
|
|
* Collection of ResourceStreamInfo for all named streams declared on |
84
|
|
|
* the resource described by this class instance (This does not include |
85
|
|
|
* base type properties). |
86
|
|
|
* |
87
|
|
|
* @var ResourceStreamInfo[] indexed by name |
88
|
|
|
*/ |
89
|
|
|
private $namedStreamsDeclaredOnThisType = []; |
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* Collection of ResourceProperty for all properties declared on this type. |
93
|
|
|
* and base types. |
94
|
|
|
* |
95
|
|
|
* @var ResourceProperty[] indexed by name |
96
|
|
|
*/ |
97
|
|
|
private $allProperties = []; |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* Collection of ResourceStreamInfo for all named streams declared on this type. |
101
|
|
|
* and base types. |
102
|
|
|
* |
103
|
|
|
* @var ResourceStreamInfo[] |
104
|
|
|
*/ |
105
|
|
|
private $allNamedStreams = []; |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* Collection of properties which has etag defined subset of $_allProperties. |
109
|
|
|
* |
110
|
|
|
* @var ResourceProperty[] |
111
|
|
|
*/ |
112
|
|
|
private $eTagProperties = []; |
113
|
|
|
|
114
|
|
|
/** |
115
|
|
|
* Collection of key properties subset of $_allProperties. |
116
|
|
|
* |
117
|
|
|
* @var ResourceProperty[] |
118
|
|
|
*/ |
119
|
|
|
private $keyProperties = []; |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* Whether the resource type described by this class instance is a MLE or not. |
123
|
|
|
* |
124
|
|
|
* @var bool |
125
|
|
|
*/ |
126
|
|
|
private $isMediaLinkEntry = false; |
127
|
|
|
|
128
|
|
|
/** |
129
|
|
|
* Whether the resource type described by this class instance has bag properties |
130
|
|
|
* Note: This has been initialized with null, later in hasBagProperty method, |
131
|
|
|
* this flag will be set to boolean value. |
132
|
|
|
* |
133
|
|
|
* @var bool |
134
|
|
|
*/ |
135
|
|
|
private $hasBagProperty = null; |
136
|
|
|
|
137
|
|
|
/** |
138
|
|
|
* Whether the resource type described by this class instance has named streams |
139
|
|
|
* Note: This has been intitialized with null, later in hasNamedStreams method, |
140
|
|
|
* this flag will be set to boolean value. |
141
|
|
|
* |
142
|
|
|
* @var bool |
143
|
|
|
*/ |
144
|
|
|
private $hasNamedStreams = null; |
145
|
|
|
|
146
|
|
|
/** |
147
|
|
|
* ReflectionClass (for complex/Entity) or IType (for Primitive) instance for |
148
|
|
|
* the resource (type) described by this class instance. |
149
|
|
|
* |
150
|
|
|
* @var \ReflectionClass|IType|string |
151
|
|
|
*/ |
152
|
|
|
private $type; |
153
|
|
|
|
154
|
|
|
/** |
155
|
|
|
* To store any custom information related to this class instance. |
156
|
|
|
* |
157
|
|
|
* @var object |
158
|
|
|
*/ |
159
|
|
|
private $customState; |
160
|
|
|
|
161
|
|
|
/** |
162
|
|
|
* Array to detect looping in bag's complex type. |
163
|
|
|
* |
164
|
|
|
* @var array |
165
|
|
|
*/ |
166
|
|
|
private $arrayToDetectLoopInComplexBag; |
167
|
|
|
|
168
|
|
|
/** |
169
|
|
|
* Create new instance of ResourceType. |
170
|
|
|
* |
171
|
|
|
* @param \ReflectionClass|IType $instanceType Instance type for the resource, |
172
|
|
|
* for entity and |
173
|
|
|
* complex this will |
174
|
|
|
* be 'ReflectionClass' and for |
175
|
|
|
* primitive type this |
176
|
|
|
* will be IType |
177
|
|
|
* @param ResourceTypeKind $resourceTypeKind Kind of resource (Entity, Complex or Primitive) |
178
|
|
|
* @param string $name Name of the resource |
179
|
|
|
* @param string $namespaceName Namespace of the resource |
180
|
|
|
* @param ResourceType $baseType Base type of the resource, if exists |
181
|
|
|
* @param bool $isAbstract Whether resource is abstract |
182
|
|
|
* |
183
|
|
|
* @throws \InvalidArgumentException |
184
|
|
|
*/ |
185
|
|
|
protected function __construct( |
186
|
|
|
$instanceType, |
187
|
|
|
$resourceTypeKind, |
188
|
|
|
$name, |
189
|
|
|
$namespaceName = null, |
190
|
|
|
ResourceType $baseType = null, |
191
|
|
|
$isAbstract = false |
192
|
|
|
) { |
193
|
|
|
$this->type = $instanceType; |
194
|
|
|
$this->resourceTypeKind = $resourceTypeKind; |
195
|
|
|
$this->name = $name; |
196
|
|
|
$this->baseType = $baseType; |
197
|
|
|
$this->namespaceName = $namespaceName; |
198
|
|
|
$this->fullName = is_null($namespaceName) ? $name : $namespaceName . '.' . $name; |
199
|
|
|
$this->abstractType = $isAbstract; |
200
|
|
|
$this->isMediaLinkEntry = false; |
201
|
|
|
$this->customState = null; |
202
|
|
|
$this->arrayToDetectLoopInComplexBag = []; |
203
|
|
|
//TODO: Set MLE if base type has MLE Set |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
/** |
207
|
|
|
* Get reference to ResourceType for base class. |
208
|
|
|
* |
209
|
|
|
* @return ResourceType |
210
|
|
|
*/ |
211
|
|
|
public function getBaseType() |
212
|
|
|
{ |
213
|
|
|
return $this->baseType; |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
/** |
217
|
|
|
* To check whether this resource type has base type. |
218
|
|
|
* |
219
|
|
|
* @return bool True if base type is defined, false otherwise |
220
|
|
|
*/ |
221
|
|
|
public function hasBaseType() |
222
|
|
|
{ |
223
|
|
|
return !is_null($this->baseType); |
224
|
|
|
} |
225
|
|
|
|
226
|
|
|
/** |
227
|
|
|
* To get custom state object for this type. |
228
|
|
|
* |
229
|
|
|
* @return object |
230
|
|
|
*/ |
231
|
|
|
public function getCustomState() |
232
|
|
|
{ |
233
|
|
|
return $this->customState; |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
/** |
237
|
|
|
* To set custom state object for this type. |
238
|
|
|
* |
239
|
|
|
* @param ResourceSet $object The custom object |
240
|
|
|
*/ |
241
|
|
|
public function setCustomState($object) |
242
|
|
|
{ |
243
|
|
|
$this->customState = $object; |
244
|
|
|
} |
245
|
|
|
|
246
|
|
|
/** |
247
|
|
|
* Get the instance type. If the resource type describes a complex or entity type, |
248
|
|
|
* then this function returns reference to ReflectionClass instance for the type. |
249
|
|
|
* If resource type describes a primitive type, then this function returns ITYpe. |
250
|
|
|
* |
251
|
|
|
* @return \ReflectionClass|IType |
252
|
|
|
*/ |
253
|
|
|
public function getInstanceType() |
254
|
|
|
{ |
255
|
|
|
if (is_string($this->type)) { |
256
|
|
|
$this->__wakeup(); |
257
|
|
|
} |
258
|
|
|
|
259
|
|
|
return $this->type; |
260
|
|
|
} |
261
|
|
|
|
262
|
|
|
/** |
263
|
|
|
* Get name of the type described by this resource type. |
264
|
|
|
* |
265
|
|
|
* @return string |
266
|
|
|
*/ |
267
|
|
|
public function getName() |
268
|
|
|
{ |
269
|
|
|
return $this->name; |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
/** |
273
|
|
|
* Get the namespace under which the type described by this resource type is |
274
|
|
|
* defined. |
275
|
|
|
* |
276
|
|
|
* @return string |
277
|
|
|
*/ |
278
|
|
|
public function getNamespace() |
279
|
|
|
{ |
280
|
|
|
return $this->namespaceName; |
281
|
|
|
} |
282
|
|
|
|
283
|
|
|
/** |
284
|
|
|
* Get full name (namespacename.typename) of the type described by this resource |
285
|
|
|
* type. |
286
|
|
|
* |
287
|
|
|
* @return string |
288
|
|
|
*/ |
289
|
|
|
public function getFullName() |
290
|
|
|
{ |
291
|
|
|
return $this->fullName; |
292
|
|
|
} |
293
|
|
|
|
294
|
|
|
/** |
295
|
|
|
* To check whether the type described by this resource type is abstract or not. |
296
|
|
|
* |
297
|
|
|
* @return bool True if type is abstract else False |
298
|
|
|
*/ |
299
|
|
|
public function isAbstract() |
300
|
|
|
{ |
301
|
|
|
return $this->abstractType; |
302
|
|
|
} |
303
|
|
|
|
304
|
|
|
/** |
305
|
|
|
* To get the kind of type described by this resource class. |
306
|
|
|
* |
307
|
|
|
* @return ResourceTypeKind |
308
|
|
|
*/ |
309
|
|
|
public function getResourceTypeKind() |
310
|
|
|
{ |
311
|
|
|
return $this->resourceTypeKind; |
312
|
|
|
} |
313
|
|
|
|
314
|
|
|
/** |
315
|
|
|
* To check whether the type described by this resource type is MLE. |
316
|
|
|
* |
317
|
|
|
* @return bool True if type is MLE else False |
318
|
|
|
*/ |
319
|
|
|
public function isMediaLinkEntry() |
320
|
|
|
{ |
321
|
|
|
return $this->isMediaLinkEntry; |
322
|
|
|
} |
323
|
|
|
|
324
|
|
|
/** |
325
|
|
|
* Set the resource type as MLE or non-MLE. |
326
|
|
|
* |
327
|
|
|
* @param bool $isMLE True to set as MLE, false for non-MLE |
328
|
|
|
*/ |
329
|
|
|
public function setMediaLinkEntry($isMLE) |
330
|
|
|
{ |
331
|
|
|
if (ResourceTypeKind::ENTITY != $this->resourceTypeKind) { |
332
|
|
|
throw new InvalidOperationException( |
333
|
|
|
Messages::resourceTypeHasStreamAttributeOnlyAppliesToEntityType() |
334
|
|
|
); |
335
|
|
|
} |
336
|
|
|
|
337
|
|
|
$this->isMediaLinkEntry = $isMLE; |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
/** |
341
|
|
|
* Add a property belongs to this resource type instance. |
342
|
|
|
* |
343
|
|
|
* @param ResourceProperty $property Property to add |
344
|
|
|
* @param bool $throw Throw exception on name collision? |
345
|
|
|
* |
346
|
|
|
* @throws InvalidOperationException |
347
|
|
|
*/ |
348
|
|
|
public function addProperty(ResourceProperty $property, $throw = true) |
349
|
|
|
{ |
350
|
|
|
if (ResourceTypeKind::PRIMITIVE == $this->resourceTypeKind) { |
351
|
|
|
throw new InvalidOperationException( |
352
|
|
|
Messages::resourceTypeNoAddPropertyForPrimitive() |
353
|
|
|
); |
354
|
|
|
} |
355
|
|
|
|
356
|
|
|
$name = $property->getName(); |
357
|
|
View Code Duplication |
foreach (array_keys($this->propertiesDeclaredOnThisType) as $propertyName) { |
|
|
|
|
358
|
|
|
if (0 == strcasecmp($propertyName, $name)) { |
359
|
|
|
if (false === $throw) { |
360
|
|
|
return; |
361
|
|
|
} |
362
|
|
|
throw new InvalidOperationException( |
363
|
|
|
Messages::resourceTypePropertyWithSameNameAlreadyExists( |
364
|
|
|
$propertyName, |
365
|
|
|
$this->name |
366
|
|
|
) |
367
|
|
|
); |
368
|
|
|
} |
369
|
|
|
} |
370
|
|
|
|
371
|
|
|
if ($property->isKindOf(ResourcePropertyKind::KEY)) { |
372
|
|
|
if (ResourceTypeKind::ENTITY != $this->resourceTypeKind) { |
373
|
|
|
throw new InvalidOperationException( |
374
|
|
|
Messages::resourceTypeKeyPropertiesOnlyOnEntityTypes() |
375
|
|
|
); |
376
|
|
|
} |
377
|
|
|
|
378
|
|
|
if (null != $this->baseType) { |
379
|
|
|
throw new InvalidOperationException( |
380
|
|
|
Messages::resourceTypeNoKeysInDerivedTypes() |
381
|
|
|
); |
382
|
|
|
} |
383
|
|
|
} |
384
|
|
|
|
385
|
|
|
if ($property->isKindOf(ResourcePropertyKind::ETAG) |
386
|
|
|
&& (ResourceTypeKind::ENTITY != $this->resourceTypeKind) |
387
|
|
|
) { |
388
|
|
|
throw new InvalidOperationException( |
389
|
|
|
Messages::resourceTypeETagPropertiesOnlyOnEntityTypes() |
390
|
|
|
); |
391
|
|
|
} |
392
|
|
|
|
393
|
|
|
//Check for Base class properties |
394
|
|
|
$this->propertiesDeclaredOnThisType[$name] = $property; |
395
|
|
|
// Set $this->allProperties to null, this is very important because the |
396
|
|
|
// first call to getAllProperties will initilaize $this->allProperties, |
397
|
|
|
// further call to getAllProperties will not reinitialize _allProperties |
398
|
|
|
// so if addProperty is called after calling getAllProperties then the |
399
|
|
|
// property just added will not be reflected in $this->allProperties |
400
|
|
|
unset($this->allProperties); |
401
|
|
|
$this->allProperties = []; |
402
|
|
|
} |
403
|
|
|
|
404
|
|
|
/** |
405
|
|
|
* Get collection properties belongs to this resource type (excluding base class |
406
|
|
|
* properties). This function returns empty array in case of resource type |
407
|
|
|
* for primitive types. |
408
|
|
|
* |
409
|
|
|
* @return ResourceProperty[] |
410
|
|
|
*/ |
411
|
|
|
public function getPropertiesDeclaredOnThisType() |
412
|
|
|
{ |
413
|
|
|
return $this->propertiesDeclaredOnThisType; |
414
|
|
|
} |
415
|
|
|
|
416
|
|
|
/** |
417
|
|
|
* Get collection properties belongs to this resource type including base class |
418
|
|
|
* properties. This function returns empty array in case of resource type |
419
|
|
|
* for primitive types. |
420
|
|
|
* |
421
|
|
|
* @return ResourceProperty[] |
422
|
|
|
*/ |
423
|
|
|
public function getAllProperties() |
424
|
|
|
{ |
425
|
|
|
if (empty($this->allProperties)) { |
426
|
|
|
if (null != $this->baseType) { |
427
|
|
|
$this->allProperties = $this->baseType->getAllProperties(); |
428
|
|
|
} |
429
|
|
|
|
430
|
|
|
$this->allProperties = array_merge( |
431
|
|
|
$this->allProperties, $this->propertiesDeclaredOnThisType |
432
|
|
|
); |
433
|
|
|
} |
434
|
|
|
|
435
|
|
|
return $this->allProperties; |
436
|
|
|
} |
437
|
|
|
|
438
|
|
|
/** |
439
|
|
|
* Get collection key properties belongs to this resource type. This |
440
|
|
|
* function returns non-empty array only for resource type representing |
441
|
|
|
* an entity type. |
442
|
|
|
* |
443
|
|
|
* @return ResourceProperty[] |
444
|
|
|
*/ |
445
|
|
|
public function getKeyProperties() |
446
|
|
|
{ |
447
|
|
|
if (empty($this->keyProperties)) { |
448
|
|
|
$baseType = $this; |
449
|
|
|
while (null != $baseType->baseType) { |
450
|
|
|
$baseType = $baseType->baseType; |
451
|
|
|
} |
452
|
|
|
|
453
|
|
|
foreach ($baseType->propertiesDeclaredOnThisType as $propertyName => $resourceProperty) { |
454
|
|
|
if ($resourceProperty->isKindOf(ResourcePropertyKind::KEY)) { |
455
|
|
|
$this->keyProperties[$propertyName] = $resourceProperty; |
456
|
|
|
} |
457
|
|
|
} |
458
|
|
|
} |
459
|
|
|
|
460
|
|
|
return $this->keyProperties; |
461
|
|
|
} |
462
|
|
|
|
463
|
|
|
/** |
464
|
|
|
* Get collection of e-tag properties belongs to this type. |
465
|
|
|
* |
466
|
|
|
* @return ResourceProperty[] |
467
|
|
|
*/ |
468
|
|
|
public function getETagProperties() |
469
|
|
|
{ |
470
|
|
|
if (empty($this->eTagProperties)) { |
471
|
|
|
foreach ($this->getAllProperties() as $propertyName => $resourceProperty) { |
472
|
|
|
if ($resourceProperty->isKindOf(ResourcePropertyKind::ETAG)) { |
473
|
|
|
$this->eTagProperties[$propertyName] = $resourceProperty; |
474
|
|
|
} |
475
|
|
|
} |
476
|
|
|
} |
477
|
|
|
|
478
|
|
|
return $this->eTagProperties; |
479
|
|
|
} |
480
|
|
|
|
481
|
|
|
/** |
482
|
|
|
* To check this type has any eTag properties. |
483
|
|
|
* |
484
|
|
|
* @return bool |
485
|
|
|
*/ |
486
|
|
|
public function hasETagProperties() |
487
|
|
|
{ |
488
|
|
|
$properties = $this->getETagProperties(); |
489
|
|
|
|
490
|
|
|
return !empty($properties); |
491
|
|
|
} |
492
|
|
|
|
493
|
|
|
/** |
494
|
|
|
* Try to get ResourceProperty for a property defined for this resource type |
495
|
|
|
* excluding base class properties. |
496
|
|
|
* |
497
|
|
|
* @param string $propertyName The name of the property to resolve |
498
|
|
|
* |
499
|
|
|
* @return ResourceProperty|null |
500
|
|
|
*/ |
501
|
|
|
public function resolvePropertyDeclaredOnThisType($propertyName) |
502
|
|
|
{ |
503
|
|
|
if (array_key_exists($propertyName, $this->propertiesDeclaredOnThisType)) { |
504
|
|
|
return $this->propertiesDeclaredOnThisType[$propertyName]; |
505
|
|
|
} |
506
|
|
|
return null; |
507
|
|
|
} |
508
|
|
|
|
509
|
|
|
/** |
510
|
|
|
* Try to get ResourceProperty for a property defined for this resource type |
511
|
|
|
* including base class properties. |
512
|
|
|
* |
513
|
|
|
* @param string $propertyName The name of the property to resolve |
514
|
|
|
* |
515
|
|
|
* @return ResourceProperty|null |
516
|
|
|
*/ |
517
|
|
|
public function resolveProperty($propertyName) |
518
|
|
|
{ |
519
|
|
|
if (array_key_exists($propertyName, $this->getAllProperties())) { |
520
|
|
|
return $this->allProperties[$propertyName]; |
521
|
|
|
} |
522
|
|
|
return null; |
523
|
|
|
} |
524
|
|
|
|
525
|
|
|
/** |
526
|
|
|
* Add a named stream belongs to this resource type instance. |
527
|
|
|
* |
528
|
|
|
* @param ResourceStreamInfo $namedStream ResourceStreamInfo instance describing the named stream to add |
529
|
|
|
* |
530
|
|
|
* @throws InvalidOperationException |
531
|
|
|
*/ |
532
|
|
|
public function addNamedStream(ResourceStreamInfo $namedStream) |
533
|
|
|
{ |
534
|
|
|
if ($this->resourceTypeKind != ResourceTypeKind::ENTITY) { |
535
|
|
|
throw new InvalidOperationException( |
536
|
|
|
Messages::resourceTypeNamedStreamsOnlyApplyToEntityType() |
537
|
|
|
); |
538
|
|
|
} |
539
|
|
|
|
540
|
|
|
$name = $namedStream->getName(); |
541
|
|
View Code Duplication |
foreach (array_keys($this->namedStreamsDeclaredOnThisType) as $namedStreamName) { |
|
|
|
|
542
|
|
|
if (0 == strcasecmp($namedStreamName, $name)) { |
543
|
|
|
throw new InvalidOperationException( |
544
|
|
|
Messages::resourceTypeNamedStreamWithSameNameAlreadyExists( |
545
|
|
|
$name, |
546
|
|
|
$this->name |
547
|
|
|
) |
548
|
|
|
); |
549
|
|
|
} |
550
|
|
|
} |
551
|
|
|
|
552
|
|
|
$this->namedStreamsDeclaredOnThisType[$name] = $namedStream; |
553
|
|
|
// Set $this->allNamedStreams to null, the first call to getAllNamedStreams |
554
|
|
|
// will initialize $this->allNamedStreams, further call to |
555
|
|
|
// getAllNamedStreams will not reinitialize _allNamedStreams |
556
|
|
|
// so if addNamedStream is called after calling getAllNamedStreams then the |
557
|
|
|
// property just added will not be reflected in $this->allNamedStreams |
558
|
|
|
unset($this->allNamedStreams); |
559
|
|
|
$this->allNamedStreams = []; |
560
|
|
|
} |
561
|
|
|
|
562
|
|
|
/** |
563
|
|
|
* Get collection of ResourceStreamInfo describing the named streams belongs |
564
|
|
|
* to this resource type (excluding base class properties). |
565
|
|
|
* |
566
|
|
|
* @return ResourceStreamInfo[] |
567
|
|
|
*/ |
568
|
|
|
public function getNamedStreamsDeclaredOnThisType() |
569
|
|
|
{ |
570
|
|
|
return $this->namedStreamsDeclaredOnThisType; |
571
|
|
|
} |
572
|
|
|
|
573
|
|
|
/** |
574
|
|
|
* Get collection of ResourceStreamInfo describing the named streams belongs |
575
|
|
|
* to this resource type including base class named streams. |
576
|
|
|
* |
577
|
|
|
* @return ResourceStreamInfo[] |
578
|
|
|
*/ |
579
|
|
|
public function getAllNamedStreams() |
580
|
|
|
{ |
581
|
|
|
if (empty($this->allNamedStreams)) { |
582
|
|
|
if (null != $this->baseType) { |
583
|
|
|
$this->allNamedStreams = $this->baseType->getAllNamedStreams(); |
584
|
|
|
} |
585
|
|
|
|
586
|
|
|
$this->allNamedStreams |
587
|
|
|
= array_merge( |
588
|
|
|
$this->allNamedStreams, |
589
|
|
|
$this->namedStreamsDeclaredOnThisType |
590
|
|
|
); |
591
|
|
|
} |
592
|
|
|
|
593
|
|
|
return $this->allNamedStreams; |
594
|
|
|
} |
595
|
|
|
|
596
|
|
|
/** |
597
|
|
|
* Try to get ResourceStreamInfo for a named stream defined for this |
598
|
|
|
* resource type excluding base class named streams. |
599
|
|
|
* |
600
|
|
|
* @param string $namedStreamName Name of the named stream to resolve |
601
|
|
|
* |
602
|
|
|
* @return ResourceStreamInfo|null |
603
|
|
|
*/ |
604
|
|
|
public function tryResolveNamedStreamDeclaredOnThisTypeByName($namedStreamName) |
605
|
|
|
{ |
606
|
|
|
if (array_key_exists($namedStreamName, $this->namedStreamsDeclaredOnThisType)) { |
607
|
|
|
return $this->namedStreamsDeclaredOnThisType[$namedStreamName]; |
608
|
|
|
} |
609
|
|
|
return null; |
610
|
|
|
} |
611
|
|
|
|
612
|
|
|
/** |
613
|
|
|
* Try to get ResourceStreamInfo for a named stream defined for this resource |
614
|
|
|
* type including base class named streams. |
615
|
|
|
* |
616
|
|
|
* @param string $namedStreamName Name of the named stream to resolve |
617
|
|
|
* |
618
|
|
|
* @return ResourceStreamInfo|null |
619
|
|
|
*/ |
620
|
|
|
public function tryResolveNamedStreamByName($namedStreamName) |
621
|
|
|
{ |
622
|
|
|
if (array_key_exists($namedStreamName, $this->getAllNamedStreams())) { |
623
|
|
|
return $this->allNamedStreams[$namedStreamName]; |
624
|
|
|
} |
625
|
|
|
return null; |
626
|
|
|
} |
627
|
|
|
|
628
|
|
|
/** |
629
|
|
|
* Check this resource type instance has named stream associated with it |
630
|
|
|
* Note: This is an internal method used by library. Devs don't use this. |
631
|
|
|
* |
632
|
|
|
* @return bool true if resource type instance has named stream else false |
633
|
|
|
*/ |
634
|
|
|
public function hasNamedStream() |
635
|
|
|
{ |
636
|
|
|
// Note: Calling this method will initialize _allNamedStreams |
637
|
|
|
// and _hasNamedStreams flag to a boolean value |
638
|
|
|
// from null depending on the current state of _allNamedStreams |
639
|
|
|
// array, so method should be called only after adding all |
640
|
|
|
// named streams |
641
|
|
|
if (is_null($this->hasNamedStreams)) { |
642
|
|
|
$this->getAllNamedStreams(); |
643
|
|
|
$this->hasNamedStreams = !empty($this->allNamedStreams); |
644
|
|
|
} |
645
|
|
|
|
646
|
|
|
return $this->hasNamedStreams; |
647
|
|
|
} |
648
|
|
|
|
649
|
|
|
/** |
650
|
|
|
* Check this resource type instance has bag property associated with it |
651
|
|
|
* Note: This is an internal method used by library. Devs don't use this. |
652
|
|
|
* |
653
|
|
|
* @param array &$arrayToDetectLoopInComplexType array for detecting loop |
654
|
|
|
* |
655
|
|
|
* @return bool|null true if resource type instance has bag property else false |
656
|
|
|
*/ |
657
|
|
|
public function hasBagProperty(&$arrayToDetectLoopInComplexType) |
658
|
|
|
{ |
659
|
|
|
// Note: Calling this method will initialize _bagProperties |
660
|
|
|
// and _hasBagProperty flag to a boolean value |
661
|
|
|
// from null depending on the current state of |
662
|
|
|
// _propertiesDeclaredOnThisType array, so method |
663
|
|
|
// should be called only after adding all properties |
664
|
|
|
if (!is_null($this->hasBagProperty)) { |
665
|
|
|
return $this->hasBagProperty; |
666
|
|
|
} |
667
|
|
|
|
668
|
|
|
if (null != $this->baseType && $this->baseType->hasBagProperty($arrayToDetectLoopInComplexType)) { |
669
|
|
|
$this->hasBagProperty = true; |
670
|
|
|
} else { |
671
|
|
|
foreach ($this->propertiesDeclaredOnThisType as $resourceProperty) { |
672
|
|
|
$hasBagInComplex = false; |
673
|
|
|
if ($resourceProperty->isKindOf(ResourcePropertyKind::COMPLEX_TYPE)) { |
674
|
|
|
//We can say current ResouceType ("this") |
675
|
|
|
//is contains a bag property if: |
676
|
|
|
//1. It contain a property of kind bag. |
677
|
|
|
//2. It contains a normal complex property |
678
|
|
|
//with a sub property of kind bag. |
679
|
|
|
//The second case can be further expanded, i.e. |
680
|
|
|
//if the normal complex property |
681
|
|
|
//has a normal complex sub property with a |
682
|
|
|
//sub property of kind bag. |
683
|
|
|
//So for complex type we recursively call this |
684
|
|
|
//function to check for bag. |
685
|
|
|
//Shown below how looping can happen in complex type: |
686
|
|
|
//Customer ResourceType (id1) |
687
|
|
|
//{ |
688
|
|
|
// .... |
689
|
|
|
// Address: Address ResourceType (id2) |
690
|
|
|
// { |
691
|
|
|
// ..... |
692
|
|
|
// AltAddress: Address ResourceType (id2) |
693
|
|
|
// { |
694
|
|
|
// ... |
695
|
|
|
// } |
696
|
|
|
// } |
697
|
|
|
//} |
698
|
|
|
|
699
|
|
|
//Here the resource type of Customer::Address and |
700
|
|
|
//Customer::Address::AltAddress |
701
|
|
|
//are same, this is a loop, we need to detect |
702
|
|
|
//this and avoid infinite recursive loop. |
703
|
|
|
|
704
|
|
|
$count = count($arrayToDetectLoopInComplexType); |
705
|
|
|
$foundLoop = false; |
706
|
|
|
for ($i = 0; $i < $count; ++$i) { |
707
|
|
|
if ($arrayToDetectLoopInComplexType[$i] === $resourceProperty->getResourceType()) { |
708
|
|
|
$foundLoop = true; |
709
|
|
|
break; |
710
|
|
|
} |
711
|
|
|
} |
712
|
|
|
|
713
|
|
|
if (!$foundLoop) { |
714
|
|
|
$arrayToDetectLoopInComplexType[$count] = $resourceProperty->getResourceType(); |
715
|
|
|
$hasBagInComplex = $resourceProperty |
716
|
|
|
->getResourceType() |
717
|
|
|
->hasBagProperty($arrayToDetectLoopInComplexType); |
718
|
|
|
unset($arrayToDetectLoopInComplexType[$count]); |
719
|
|
|
} |
720
|
|
|
} |
721
|
|
|
|
722
|
|
|
if ($resourceProperty->isKindOf(ResourcePropertyKind::BAG) || $hasBagInComplex) { |
723
|
|
|
$this->hasBagProperty = true; |
724
|
|
|
break; |
725
|
|
|
} |
726
|
|
|
} |
727
|
|
|
} |
728
|
|
|
|
729
|
|
|
return $this->hasBagProperty; |
730
|
|
|
} |
731
|
|
|
|
732
|
|
|
/** |
733
|
|
|
* Validate the type. |
734
|
|
|
* |
735
|
|
|
* |
736
|
|
|
* @throws InvalidOperationException |
737
|
|
|
*/ |
738
|
|
|
public function validateType() |
739
|
|
|
{ |
740
|
|
|
$keyProperties = $this->getKeyProperties(); |
741
|
|
|
if (($this->resourceTypeKind == ResourceTypeKind::ENTITY) && empty($keyProperties)) { |
742
|
|
|
throw new InvalidOperationException( |
743
|
|
|
Messages::resourceTypeMissingKeyPropertiesForEntity( |
744
|
|
|
$this->getFullName() |
745
|
|
|
) |
746
|
|
|
); |
747
|
|
|
} |
748
|
|
|
} |
749
|
|
|
|
750
|
|
|
/** |
751
|
|
|
* To check the type described by this resource type is assignable from |
752
|
|
|
* a type described by another resource type. Or this type is a sub-type |
753
|
|
|
* of (derived from the) given resource type. |
754
|
|
|
* |
755
|
|
|
* @param ResourceType $resourceType Another resource type |
756
|
|
|
* |
757
|
|
|
* @return bool |
758
|
|
|
*/ |
759
|
|
|
public function isAssignableFrom(ResourceType $resourceType) |
760
|
|
|
{ |
761
|
|
|
$base = $this; |
762
|
|
|
while (null != $base) { |
763
|
|
|
if ($resourceType == $base) { |
764
|
|
|
return true; |
765
|
|
|
} |
766
|
|
|
|
767
|
|
|
$base = $base->baseType; |
768
|
|
|
} |
769
|
|
|
|
770
|
|
|
return false; |
771
|
|
|
} |
772
|
|
|
|
773
|
|
|
/** |
774
|
|
|
* Get predefined ResourceType for a primitive type. |
775
|
|
|
* |
776
|
|
|
* @param EdmPrimitiveType $typeCode Typecode of primitive type |
777
|
|
|
* |
778
|
|
|
* @throws InvalidArgumentException |
779
|
|
|
* |
780
|
|
|
* @return ResourceType |
781
|
|
|
*/ |
782
|
|
|
public static function getPrimitiveResourceType($typeCode) |
783
|
|
|
{ |
784
|
|
|
switch ($typeCode) { |
785
|
|
|
case EdmPrimitiveType::BINARY: |
786
|
|
|
return new ResourcePrimitiveType(new Binary()); |
787
|
|
|
case EdmPrimitiveType::BOOLEAN: |
788
|
|
|
return new ResourcePrimitiveType(new Boolean()); |
789
|
|
|
case EdmPrimitiveType::BYTE: |
790
|
|
|
return new ResourcePrimitiveType(new Byte()); |
791
|
|
|
case EdmPrimitiveType::DATETIME: |
792
|
|
|
return new ResourcePrimitiveType(new DateTime()); |
793
|
|
|
case EdmPrimitiveType::DECIMAL: |
794
|
|
|
return new ResourcePrimitiveType(new Decimal()); |
795
|
|
|
case EdmPrimitiveType::DOUBLE: |
796
|
|
|
return new ResourcePrimitiveType(new Double()); |
797
|
|
|
case EdmPrimitiveType::GUID: |
798
|
|
|
return new ResourcePrimitiveType(new Guid()); |
799
|
|
|
case EdmPrimitiveType::INT16: |
800
|
|
|
return new ResourcePrimitiveType(new Int16()); |
801
|
|
|
case EdmPrimitiveType::INT32: |
802
|
|
|
return new ResourcePrimitiveType(new Int32()); |
803
|
|
|
case EdmPrimitiveType::INT64: |
804
|
|
|
return new ResourcePrimitiveType(new Int64()); |
805
|
|
|
case EdmPrimitiveType::SBYTE: |
806
|
|
|
return new ResourcePrimitiveType(new SByte()); |
807
|
|
|
case EdmPrimitiveType::SINGLE: |
808
|
|
|
return new ResourcePrimitiveType(new Single()); |
809
|
|
|
case EdmPrimitiveType::STRING: |
810
|
|
|
return new ResourcePrimitiveType(new StringType()); |
811
|
|
|
default: |
812
|
|
|
throw new \InvalidArgumentException( |
813
|
|
|
Messages::commonNotValidPrimitiveEDMType( |
814
|
|
|
'$typeCode', |
815
|
|
|
'getPrimitiveResourceType' |
816
|
|
|
) |
817
|
|
|
); |
818
|
|
|
} |
819
|
|
|
} |
820
|
|
|
|
821
|
|
|
/** |
822
|
|
|
* @param string $property |
823
|
|
|
*/ |
824
|
|
|
public function setPropertyValue($entity, $property, $value) |
825
|
|
|
{ |
826
|
|
|
\POData\Common\ReflectionHandler::setProperty($entity, $property, $value); |
827
|
|
|
|
828
|
|
|
return $this; |
829
|
|
|
} |
830
|
|
|
|
831
|
|
|
public function getPropertyValue($entity, $property) |
832
|
|
|
{ |
833
|
|
|
return \POData\Common\ReflectionHandler::getProperty($entity, $property); |
834
|
|
|
} |
835
|
|
|
|
836
|
|
|
public function __sleep() |
837
|
|
|
{ |
838
|
|
|
if (null == $this->type || $this->type instanceof \POData\Providers\Metadata\Type\IType) { |
839
|
|
|
return array_keys(get_object_vars($this)); |
840
|
|
|
} |
841
|
|
|
$this->type = $this->type->name; |
842
|
|
|
$result = array_keys(get_object_vars($this)); |
843
|
|
|
|
844
|
|
|
return $result; |
845
|
|
|
} |
846
|
|
|
|
847
|
|
|
public function __wakeup() |
848
|
|
|
{ |
849
|
|
|
if (is_string($this->type)) { |
850
|
|
|
$this->type = new \ReflectionClass($this->type); |
851
|
|
|
} |
852
|
|
|
|
853
|
|
|
assert( |
854
|
|
|
$this->type instanceof \ReflectionClass || $this->type instanceof IType, |
855
|
|
|
'_type neither instance of reflection class nor IType' |
856
|
|
|
); |
857
|
|
|
} |
858
|
|
|
} |
859
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.