ResourceType   F
last analyzed

Complexity

Total Complexity 92

Size/Duplication

Total Lines 896
Duplicated Lines 2.23 %

Coupling/Cohesion

Components 1
Dependencies 16

Importance

Changes 0
Metric Value
wmc 92
lcom 1
cbo 16
dl 20
loc 896
rs 1.263
c 0
b 0
f 0

31 Methods

Rating   Name   Duplication   Size   Complexity  
C __construct() 0 47 7
A getBaseType() 0 4 1
A hasBaseType() 0 4 1
A getCustomState() 0 4 1
A setCustomState() 0 4 1
A getInstanceType() 0 4 1
A getName() 0 4 1
A getNamespace() 0 4 1
A getFullName() 0 4 1
A isAbstract() 0 4 1
A getResourceTypeKind() 0 4 1
A isMediaLinkEntry() 0 4 1
A setMediaLinkEntry() 0 10 2
C addProperty() 9 51 9
A getPropertiesDeclaredOnThisType() 0 4 1
A getAllProperties() 0 14 3
B getKeyProperties() 0 19 5
A getETagProperties() 0 14 4
A hasETagProperties() 0 5 1
A resolvePropertyDeclaredOnThisType() 0 8 2
A resolveProperty() 0 8 2
B addNamedStream() 11 30 4
A getNamedStreamsDeclaredOnThisType() 0 4 1
A getAllNamedStreams() 0 16 3
A tryResolveNamedStreamDeclaredOnThisTypeByName() 0 8 2
A tryResolveNamedStreamByName() 0 8 2
A hasNamedStream() 0 14 2
C hasBagProperty() 0 73 11
A validateType() 0 11 3
A isAssignableFrom() 0 13 3
D getPrimitiveResourceType() 0 101 14

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like ResourceType often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ResourceType, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace POData\Providers\Metadata;
4
5
use POData\Providers\Metadata\Type\Binary;
6
use POData\Providers\Metadata\Type\Boolean;
7
use POData\Providers\Metadata\Type\Byte;
8
use POData\Providers\Metadata\Type\DateTime;
9
use POData\Providers\Metadata\Type\Decimal;
10
use POData\Providers\Metadata\Type\Double;
11
use POData\Providers\Metadata\Type\Guid;
12
use POData\Providers\Metadata\Type\Int16;
13
use POData\Providers\Metadata\Type\Int32;
14
use POData\Providers\Metadata\Type\Int64;
15
use POData\Providers\Metadata\Type\SByte;
16
use POData\Providers\Metadata\Type\Single;
17
use POData\Providers\Metadata\Type\StringType;
18
use POData\Providers\Metadata\Type\TypeCode;
19
use POData\Providers\Metadata\Type\EdmPrimitiveType;
20
use POData\Providers\Metadata\Type\IType;
21
use POData\Common\Messages;
22
use POData\Common\InvalidOperationException;
23
use POData\Common;
24
25
/**
26
 * Class ResourceType
27
 *
28
 * A type to describe an entity type, complex type or primitive type.
29
 *
30
 * @package POData\Providers\Metadata
31
 */
32
class ResourceType
33
{
34
    /**
35
     * Name of the resource described by this class instance.
36
     * 
37
     * @var string
38
     */
39
    private $_name;
40
41
    /**
42
     * Namespace name in which resource described by this class instance
43
     * belongs to.
44
     * 
45
     * @var string
46
     */
47
    private $_namespaceName;
48
49
    /**
50
     * The fully qualified name of the resource described by this class instance.
51
     * 
52
     * @var string
53
     */
54
    private $_fullName;
55
56
    /**
57
     * The type the resource described by this class instance.
58
     * Note: either Entity or Complex Type
59
     * 
60
     * @var  ResourceTypeKind
61
     */
62
    private $_resourceTypeKind;
63
64
    /**
65
     * @var boolean
66
     */
67
    private $_abstractType;
68
69
    /**
70
     * Refrence to ResourceType instance for base type, if any.
71
     * 
72
     * @var ResourceType
73
     */
74
    private $_baseType;
75
76
    /** 
77
     * Collection of ResourceProperty for all properties declared on the
78
     * resource described by this class instance (This does not include 
79
     * base type properties).
80
     * 
81
     * @var ResourceProperty[] indexed by name
82
     */
83
    private $_propertiesDeclaredOnThisType = array();
84
85
    /**
86
     * Collection of ResourceStreamInfo for all named streams declared on 
87
     * the resource described by this class instance (This does not include 
88
     * base type properties).
89
     * 
90
     * @var ResourceStreamInfo[] indexed by name
91
     */
92
    private $_namedStreamsDeclaredOnThisType = array();
93
94
    /**
95
     * Collection of ReflectionProperty instances for each property declared 
96
     * on this type
97
     * 
98
     * @var array(ResourceProperty, ReflectionProperty)
99
     */
100
    private $_propertyInfosDeclaredOnThisType = array();
0 ignored issues
show
Unused Code introduced by
The property $_propertyInfosDeclaredOnThisType is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
101
102
    /**
103
     * Collection of ResourceProperty for all properties declared on this type.
104
     * and base types.
105
     * 
106
     * @var ResourceProperty[] indexed by name
107
     */    
108
    private $_allProperties = array();
109
110
    /**
111
     * Collection of ResourceStreamInfo for all named streams declared on this type.
112
     * and base types
113
     * 
114
     * @var ResourceStreamInfo[]
115
     */
116
    private $_allNamedStreams = array();
117
118
    /**      
119
     * Collection of properties which has etag defined subset of $_allProperties
120
     * @var ResourceProperty[]
121
     */
122
    private $_etagProperties = array();
123
124
    /** 
125
     * Collection of key properties subset of $_allProperties
126
     * 
127
     * @var ResourceProperty[]
128
     */
129
    private $_keyProperties = array();
130
131
    /**      
132
     * Whether the resource type described by this class instance is a MLE or not
133
     * 
134
     * @var boolean
135
     */
136
    private $_isMediaLinkEntry = false;
137
138
    /**
139
     * Whether the resource type described by this class instance has bag properties
140
     * Note: This has been initialized with null, later in hasBagProperty method,
141
     * this flag will be set to boolean value
142
     * 
143
     * @var boolean
144
     */
145
    private $_hasBagProperty = null;
146
147
    /**
148
     * Whether the resource type described by this class instance has named streams
149
     * Note: This has been intitialized with null, later in hasNamedStreams method, 
150
     * this flag will be set to boolean value
151
     * 
152
     * @var boolean
153
     */
154
    private $_hasNamedStreams = null;
155
156
    /**     
157
     * ReflectionClass (for complex/Entity) or IType (for Primitive) instance for 
158
     * the resource (type) described by this class instance
159
     * 
160
     * @var \ReflectionClass|IType
161
     */
162
    private $_type;
163
164
    /**
165
     * To store any custom information related to this class instance
166
     * 
167
     * @var Object
168
     */
169
    private $_customState; 
170
171
    /**
172
     * Array to detect looping in bag's complex type  
173
     * 
174
     * @var array
175
     */
176
    private $_arrayToDetectLoopInComplexBag;
177
178
    /**
179
     * Create new instance of ResourceType
180
     * 
181
     * @param \ReflectionClass|IType $instanceType     Instance type for the resource,
182
     *                                                for entity and 
183
     *                                                complex this will 
184
     *                                                be 'ReflectionClass' and for 
185
     *                                                primitive type this 
186
     *                                                will be IType
187
     * @param ResourceTypeKind      $resourceTypeKind Kind of resource (Entity, Complex or Primitive)
188
     * @param string                $name             Name of the resource
189
     * @param string                $namespaceName    Namespace of the resource
190
     * @param ResourceType          $baseType         Base type of the resource, if exists
191
     *
192
     * @param boolean               $isAbstract       Whether resource is abstract
193
     * 
194
     * @throws \InvalidArgumentException
195
     */
196
    public function __construct(
197
	    $instanceType,
198
	    $resourceTypeKind,
199
	    $name,
200
        $namespaceName = null,
201
        ResourceType $baseType = null,
202
        $isAbstract = false
203
    ) {
204
        $this->_type = $instanceType;
205
        if ($resourceTypeKind == ResourceTypeKind::PRIMITIVE) {
206
            if ($baseType != null) {
207
                throw new \InvalidArgumentException(
208
                    Messages::resourceTypeNoBaseTypeForPrimitive()
209
                );
210
            }
211
212
            if ($isAbstract) {
213
                throw new \InvalidArgumentException(
214
                    Messages::resourceTypeNoAbstractForPrimitive()
215
                );
216
            }
217
218
            if (!($instanceType instanceof IType)) {
219
                throw new \InvalidArgumentException(
220
                    Messages::resourceTypeTypeShouldImplementIType('$instanceType')
221
                );
222
            }
223
        } else {
224
            if (!($instanceType instanceof \ReflectionClass)) {
225
                throw new \InvalidArgumentException(
226
                    Messages::resourceTypeTypeShouldReflectionClass('$instanceType')
227
                );
228
            }
229
        }
230
231
        $this->_resourceTypeKind = $resourceTypeKind;
232
        $this->_name = $name;
233
        $this->_baseType = $baseType;        
234
        $this->_namespaceName = $namespaceName; 
235
        $this->_fullName 
236
            = is_null($namespaceName) ? $name : $namespaceName . '.' . $name;
237
        $this->_abstractType = $isAbstract;
238
        $this->_isMediaLinkEntry = false;
239
        $this->_customState = null;
240
        $this->_arrayToDetectLoopInComplexBag = array();
241
        //TODO: Set MLE if base type has MLE Set
242
    }
243
244
    /**
245
     * Get reference to ResourceType for base class
246
     * 
247
     * @return ResourceType
248
     */
249
    public function getBaseType()
250
    {
251
        return $this->_baseType;
252
    }
253
254
    /**
255
     * To check whether this resource type has base type
256
     * 
257
     * @return boolean True if base type is defined, false otherwise
258
     */
259
    public function hasBaseType()
260
    {
261
        return !is_null($this->_baseType);
262
    }
263
264
    /**
265
     * To get custom state object for this type
266
     * 
267
     * @return object
268
     */
269
    public function getCustomState()
270
    {
271
        return $this->_customState;
272
    }
273
274
    /**
275
     * To set custom state object for this type
276
     * 
277
     * @param Object $object The custom object.
278
     * 
279
     * @return void
280
     */
281
    public function setCustomState($object)
282
    {
283
        $this->_customState = $object;
284
    }
285
286
    /**
287
     * Get the instance type. If the resource type describes a complex or entity type
288
     * then this function returns refernece to ReflectionClass instance for the type.
289
     * If resource type describes a primitive type then this function returns ITYpe.
290
     * 
291
     * @return \ReflectionClass|IType
292
     */
293
    public function getInstanceType()
294
    {
295
        return $this->_type;
296
    }
297
298
    /**
299
     * Get name of the type described by this resource type
300
     * 
301
     * @return string
302
     */
303
    public function getName()
304
    {
305
        return $this->_name;
306
    }
307
308
    /**
309
     * Get the namespace under which the type described by this resource type is
310
     * defined.
311
     * 
312
     * @return string
313
     */
314
    public function getNamespace()
315
    {
316
        return $this->_namespaceName;
317
    }
318
319
    /**
320
     * Get full name (namespacename.typename) of the type described by this resource 
321
     * type.
322
     * 
323
     * @return string
324
     */
325
    public function getFullName()
326
    {
327
        return $this->_fullName;
328
    }
329
330
    /**
331
     * To check whether the type described by this resource type is abstract or not
332
     * 
333
     * @return boolean True if type is abstract else False
334
     */
335
    public function isAbstract()
336
    {
337
        return $this->_abstractType;
338
    }
339
340
    /**
341
     * To get the kind of type described by this resource class
342
     * 
343
     * @return ResourceTypeKind
344
     */
345
    public function getResourceTypeKind()
346
    {
347
        return $this->_resourceTypeKind;
348
    }
349
350
    /**
351
     * To check whether the type described by this resource type is MLE
352
     * 
353
     * @return boolean True if type is MLE else False
354
     */
355
    public function isMediaLinkEntry()
356
    {
357
        return $this->_isMediaLinkEntry;
358
    }
359
360
    /**
361
     * Set the resource type as MLE or non-MLE
362
     * 
363
     * @param boolean $isMLE True to set as MLE, false for non-MLE
364
     * 
365
     * @return void
366
     */
367
    public function setMediaLinkEntry($isMLE)
368
    {
369
        if ($this->_resourceTypeKind != ResourceTypeKind::ENTITY) {
370
            throw new InvalidOperationException(
371
                Messages::resourceTypeHasStreamAttributeOnlyAppliesToEntityType()
372
            );
373
        }
374
375
        $this->_isMediaLinkEntry = $isMLE;
376
    }
377
378
    /**      
379
     * Add a property belongs to this resource type instance
380
     * 
381
     * @param ResourceProperty $property Property to add
382
     * 
383
     * @throws InvalidOperationException
384
     * @return void
385
     */
386
    public function addProperty(ResourceProperty $property)
387
    {
388
        if ($this->_resourceTypeKind == ResourceTypeKind::PRIMITIVE) {
389
            throw new InvalidOperationException(
390
                Messages::resourceTypeNoAddPropertyForPrimitive()
391
            );
392
        }
393
394
        $name = $property->getName();
395 View Code Duplication
        foreach (array_keys($this->_propertiesDeclaredOnThisType) as $propertyName) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
396
            if (strcasecmp($propertyName, $name) == 0) {
397
                throw new InvalidOperationException(
398
                    Messages::resourceTypePropertyWithSameNameAlreadyExists(
399
                        $propertyName, $this->_name
400
                    )
401
                );
402
            }
403
        }
404
405
        if ($property->isKindOf(ResourcePropertyKind::KEY)) {
406
            if ($this->_resourceTypeKind != ResourceTypeKind::ENTITY) {
407
                throw new InvalidOperationException(
408
                    Messages::resourceTypeKeyPropertiesOnlyOnEntityTypes()
409
                );
410
            }
411
412
            if ($this->_baseType != null) {
413
                throw new InvalidOperationException(
414
                    Messages::resourceTypeNoKeysInDerivedTypes()
415
                );
416
            }
417
        }
418
419
        if ($property->isKindOf(ResourcePropertyKind::ETAG) 
420
            && ($this->_resourceTypeKind != ResourceTypeKind::ENTITY)
421
        ) {
422
            throw new InvalidOperationException(
423
                Messages::resourceTypeETagPropertiesOnlyOnEntityTypes()
424
            );
425
        }
426
427
        //Check for Base class properties
428
        $this->_propertiesDeclaredOnThisType[$name] = $property;
429
        // Set $this->_allProperties to null, this is very important because the 
430
        // first call to getAllProperties will initilaize $this->_allProperties, 
431
        // further call to getAllProperties will not reinitialize _allProperties
432
        // so if addProperty is called after calling getAllProperties then the 
433
        // property just added will not be reflected in $this->_allProperties
434
        unset($this->_allProperties);
435
        $this->_allProperties = array();
436
    }
437
438
    /**
439
     * Get collection properties belongs to this resource type (excluding base class
440
     * properties). This function returns  empty array in case of resource type
441
     * for primitive types.
442
     * 
443
     * @return ResourceProperty[]
444
     */
445
    public function getPropertiesDeclaredOnThisType()
446
    {
447
        return $this->_propertiesDeclaredOnThisType;
448
    }
449
450
    /**
451
     * Get collection properties belongs to this resource type including base class
452
     * properties. This function returns  empty array in case of resource type
453
     * for primitive types.
454
     * 
455
     * @return ResourceProperty[]
456
     */
457
    public function getAllProperties()
458
    {
459
        if (empty($this->_allProperties)) {
460
            if ($this->_baseType != null) {
461
                $this->_allProperties = $this->_baseType->getAllProperties();
462
            }
463
464
            $this->_allProperties = array_merge(
465
                $this->_allProperties, $this->_propertiesDeclaredOnThisType
466
            );
467
        }
468
469
        return $this->_allProperties;
470
    }
471
472
    /**     
473
     * Get collection key properties belongs to this resource type. This 
474
     * function returns non-empty array only for resource type representing 
475
     * an entity type.
476
     *  
477
     * @return ResourceProperty[]
478
     */
479
    public function getKeyProperties()
480
    {
481
        if (empty($this->_keyProperties)) {
482
            $baseType = $this;
483
            while ($baseType->_baseType != null) {
484
                $baseType = $baseType->_baseType;
485
            }
486
487
            foreach ($baseType->_propertiesDeclaredOnThisType 
488
                as $propertyName => $resourceProperty
489
            ) {
490
                if ($resourceProperty->isKindOf(ResourcePropertyKind::KEY)) {
491
                    $this->_keyProperties[$propertyName] = $resourceProperty;
492
                }
493
            }
494
        }
495
496
        return $this->_keyProperties;
497
    }
498
499
    /**
500
     * Get collection of e-tag properties belongs to this type.
501
     * 
502
     * @return ResourceProperty[]
503
     */
504
    public function getETagProperties()
505
    {
506
        if (empty ($this->_etagProperties)) {
507
            foreach ($this->getAllProperties() 
508
                as $propertyName => $resourceProperty
509
            ) {
510
                if ($resourceProperty->isKindOf(ResourcePropertyKind::ETAG)) {
511
                    $this->_etagProperties[$propertyName] = $resourceProperty;
512
                }
513
            }
514
        }
515
516
        return $this->_etagProperties;
517
    }
518
519
    /**
520
     * To check this type has any eTag properties
521
     * 
522
     * @return boolean
523
     */
524
    public function hasETagProperties()
525
    {
526
        $this->getETagProperties();
527
        return !empty($this->_etagProperties);
528
    }
529
530
    /**
531
     * Try to get ResourceProperty for a property defined for this resource type
532
     * excluding base class properties
533
     * 
534
     * @param string $propertyName The name of the property to resolve.
535
     * 
536
     * @return ResourceProperty|null
537
     */
538
    public function resolvePropertyDeclaredOnThisType($propertyName)
539
    {
540
        if (array_key_exists($propertyName, $this->_propertiesDeclaredOnThisType)) {
541
            return $this->_propertiesDeclaredOnThisType[$propertyName];
542
        }
543
544
        return null;
545
    }
546
547
    /** 
548
     * Try to get ResourceProperty for a property defined for this resource type
549
     * including base class properties
550
     * 
551
     * @param string $propertyName The name of the property to resolve.
552
     * 
553
     * @return ResourceProperty|null
554
     */
555
    public function resolveProperty($propertyName)
556
    {
557
        if (array_key_exists($propertyName, $this->getAllProperties())) {
558
            return $this->_allProperties[$propertyName];
559
        }
560
561
        return null;
562
    }
563
564
    /**
565
     * Add a named stream belongs to this resource type instance
566
     * 
567
     * @param ResourceStreamInfo $namedStream ResourceStreamInfo instance describing the named stream to add.
568
     *
569
     * @return void
570
     * 
571
     * @throws InvalidOperationException
572
     */
573
    public function addNamedStream(ResourceStreamInfo $namedStream)
574
    {
575
        if ($this->_resourceTypeKind != ResourceTypeKind::ENTITY) {
576
            throw new InvalidOperationException(
577
                Messages::resourceTypeNamedStreamsOnlyApplyToEntityType()
578
            );
579
        }
580
581
        $name = $namedStream->getName();
582 View Code Duplication
        foreach (array_keys($this->_namedStreamsDeclaredOnThisType) 
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
583
            as $namedStreamName
584
        ) {
585
            if (strcasecmp($namedStreamName, $name) == 0) {
586
                throw new InvalidOperationException(
587
                    Messages::resourceTypeNamedStreamWithSameNameAlreadyExists(
588
                        $name, $this->_name
589
                    )
590
                );
591
            }
592
        }
593
594
        $this->_namedStreamsDeclaredOnThisType[$name] = $namedStream;
595
        // Set $this->_allNamedStreams to null, the first call to getAllNamedStreams
596
        // will initialize $this->_allNamedStreams, further call to
597
        // getAllNamedStreams will not reinitialize _allNamedStreams
598
        // so if addNamedStream is called after calling getAllNamedStreams then the
599
        // property just added will not be reflected in $this->_allNamedStreams
600
        unset($this->_allNamedStreams);
601
        $this->_allNamedStreams = array();
602
    }
603
604
    /**
605
     * Get collection of ResourceStreamInfo describing the named streams belongs 
606
     * to this resource type (excluding base class properties)
607
     * 
608
     * @return ResourceStreamInfo[]
609
     */
610
    public function getNamedStreamsDeclaredOnThisType()
611
    {
612
        return $this->_namedStreamsDeclaredOnThisType;
613
    }
614
615
    /**
616
     * Get collection of ResourceStreamInfo describing the named streams belongs 
617
     * to this resource type including base class named streams.
618
     * 
619
     * @return ResourceStreamInfo[]
620
     */
621
    public function getAllNamedStreams()
622
    {
623
        if (empty($this->_allNamedStreams)) {
624
            if ($this->_baseType != null) {
625
                $this->_allNamedStreams = $this->_baseType->getAllNamedStreams();
626
            }
627
628
            $this->_allNamedStreams 
629
                = array_merge(
630
                    $this->_allNamedStreams, 
631
                    $this->_namedStreamsDeclaredOnThisType
632
                );
633
        }
634
635
        return $this->_allNamedStreams;
636
    }
637
638
    /** 
639
     * Try to get ResourceStreamInfo for a named stream defined for this
640
     * resource type excluding base class named streams
641
     * 
642
     * @param string $namedStreamName Name of the named stream to resolve.
643
     * 
644
     * @return ResourceStreamInfo|null
645
     */
646
    public function tryResolveNamedStreamDeclaredOnThisTypeByName($namedStreamName)
647
    {
648
        if (array_key_exists($namedStreamName, $this->_namedStreamsDeclaredOnThisType)) {
649
            return $this->_namedStreamsDeclaredOnThisType[$namedStreamName];
650
        }
651
652
        return null;
653
    }
654
655
    /** 
656
     * Try to get ResourceStreamInfo for a named stream defined for this resource 
657
     * type including base class named streams
658
     * 
659
     * @param string $namedStreamName Name of the named stream to resolve.
660
     * 
661
     * @return ResourceStreamInfo|NULL
662
     */
663
    public function tryResolveNamedStreamByName($namedStreamName)
664
    {
665
        if (array_key_exists($namedStreamName, $this->getAllNamedStreams())) {
666
            return $this->_allNamedStreams[$namedStreamName];
667
        }
668
669
        return null;
670
    }
671
672
    /**
673
     * Check this resource type instance has named stream associated with it
674
     * Note: This is an internal method used by library. Devs don't use this.
675
     * 
676
     * @return boolean true if resource type instance has named stream else false
677
     */
678
    public function hasNamedStream()
679
    {   
680
        // Note: Calling this method will initialize _allNamedStreams 
681
        // and _hasNamedStreams flag to a boolean value
682
        // from null depending on the current state of _allNamedStreams 
683
        // array, so method should be called only after adding all 
684
        // named streams
685
        if (is_null($this->_hasNamedStreams)) {
686
            $this->getAllNamedStreams();
687
            $this->_hasNamedStreams = !empty($this->_allNamedStreams);
688
        }
689
        
690
        return $this->_hasNamedStreams;
691
    }
692
693
    /**
694
     * Check this resource type instance has bag property associated with it
695
     * Note: This is an internal method used by library. Devs don't use this.
696
     * 
697
     * @param array &$arrayToDetectLoopInComplexType array for detecting loop.
698
     * 
699
     * @return boolean true if resource type instance has bag property else false
700
     */
701
    public function hasBagProperty(&$arrayToDetectLoopInComplexType)
702
    {        
703
        // Note: Calling this method will initialize _bagProperties 
704
        // and _hasBagProperty flag to a boolean value
705
        // from null depending on the current state of 
706
        // _propertiesDeclaredOnThisType array, so method
707
        // should be called only after adding all properties
708
        if (!is_null($this->_hasBagProperty)) {
709
            return $this->_hasBagProperty;
710
        }
711
712
        if ($this->_baseType != null && $this->_baseType->hasBagProperty($arrayToDetectLoopInComplexType)) {
713
            $this->_hasBagProperty = true;
714
        } else {
715
            foreach ($this->_propertiesDeclaredOnThisType as $resourceProperty) {
716
                $hasBagInComplex = false;
717
                if ($resourceProperty->isKindOf(ResourcePropertyKind::COMPLEX_TYPE)) {
718
                    //We can say current ResouceType ("this")
719
                    //is contains a bag property if:
720
                    //1. It contain a property of kind bag.
721
                    //2. It contains a normal complex property
722
                    //with a sub property of kind bag.
723
                    //The second case can be further expanded, i.e.
724
                    //if the normal complex property
725
                    //has a normal complex sub property with a
726
                    //sub property of kind bag.
727
                    //So for complex type we recursively call this
728
                    //function to check for bag.
729
                    //Shown below how looping can happen in complex type:
730
                    //Customer ResourceType (id1)
731
                    //{
732
                    //  ....
733
                    //  Address: Address ResourceType (id2)
734
                    //  {
735
                    //    .....
736
                    //    AltAddress: Address ResourceType (id2)
737
                    //    {
738
                    //      ...
739
                    //    }
740
                    //  }
741
                    //}
742
                    //
743
                    //Here the resource type of Customer::Address and
744
                    //Customer::Address::AltAddress
745
                    //are same, this is a loop, we need to detect
746
                    //this and avoid infinite recursive loop.
747
                    //
748
                    $count = count($arrayToDetectLoopInComplexType);
749
                    $foundLoop = false;
750
                    for ($i = 0; $i < $count; $i++) {
751
                        if ($arrayToDetectLoopInComplexType[$i] === $resourceProperty->getResourceType()) {
752
                            $foundLoop = true;
753
                            break;
754
                        }
755
                    }
756
757
                    if (!$foundLoop) {
758
                        $arrayToDetectLoopInComplexType[$count] = $resourceProperty->getResourceType();
759
                        $hasBagInComplex = $resourceProperty->getResourceType()->hasBagProperty($arrayToDetectLoopInComplexType);
760
                        unset($arrayToDetectLoopInComplexType[$count]);
761
                    }
762
                }
763
764
                if ($resourceProperty->isKindOf(ResourcePropertyKind::BAG) || $hasBagInComplex) {
765
                    $this->_hasBagProperty = true;
766
                    break;
767
                }
768
            }
769
        }
770
771
772
        return $this->_hasBagProperty;
773
    }
774
    
775
    /** 
776
     * Validate the type
777
     * 
778
     * @return void
779
     * 
780
     * @throws InvalidOperationException
781
     */
782
    public function validateType()
783
    {
784
        $keyProperties = $this->getKeyProperties();
785
        if (($this->_resourceTypeKind == ResourceTypeKind::ENTITY) && empty($keyProperties)) {
786
            throw new InvalidOperationException(
787
                Messages::resourceTypeMissingKeyPropertiesForEntity(
788
                    $this->getFullName()
789
                )
790
            );
791
        }
792
    }
793
794
    /**     
795
     * To check the type described by this resource type is assignable from 
796
     * a type described by another resource type. Or this type is a sub-type 
797
     * of (derived from the) given resource type
798
     * 
799
     * @param ResourceType $resourceType Another resource type.
800
     * 
801
     * @return boolean
802
     */
803
    public function isAssignableFrom(ResourceType $resourceType)
804
    {        
805
        $base = $this;
806
        while ($base != null) {
807
            if ($resourceType == $base) {
808
                return true;
809
            }
810
            
811
            $base = $base->_baseType;
812
        }
813
814
        return false;
815
    }
816
817
    /**     
818
     * Get predefined ResourceType for a primitive type
819
     *  
820
     * @param EdmPrimitiveType $typeCode Typecode of primitive type
821
     *
822
     * @return ResourceType
823
     * 
824
     * @throws InvalidArgumentException
825
     */
826
    public static function getPrimitiveResourceType($typeCode)
827
    {
828
        switch($typeCode) {
829
        case EdmPrimitiveType::BINARY:
830
            return new ResourceType(
831
                new Binary(), ResourceTypeKind::PRIMITIVE, 
832
                'Binary', 'Edm'
833
            );
834
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
835
        case EdmPrimitiveType::BOOLEAN:
836
            return new ResourceType(
837
                new Boolean(), 
838
                ResourceTypeKind::PRIMITIVE, 
839
                'Boolean', 'Edm'
840
            );
841
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
842
        case EdmPrimitiveType::BYTE:
843
            return new ResourceType(
844
                new Byte(), 
845
                ResourceTypeKind::PRIMITIVE, 
846
                'Byte', 'Edm'
847
            );
848
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
849
        case EdmPrimitiveType::DATETIME:
850
            return new ResourceType(
851
                new DateTime(), 
852
                ResourceTypeKind::PRIMITIVE, 
853
                'DateTime', 'Edm'
854
            );
855
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
856
        case EdmPrimitiveType::DECIMAL:
857
            return new ResourceType(
858
                new Decimal(), 
859
                ResourceTypeKind::PRIMITIVE, 
860
                'Decimal', 'Edm'
861
            );
862
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
863
        case EdmPrimitiveType::DOUBLE:
864
            return new ResourceType(
865
                new Double(), 
866
                ResourceTypeKind::PRIMITIVE, 
867
                'Double', 'Edm'
868
            );
869
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
870
        case EdmPrimitiveType::GUID:
871
            return new ResourceType(
872
                new Guid(), 
873
                ResourceTypeKind::PRIMITIVE, 
874
                'Guid', 'Edm'
875
            );
876
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
877
        case EdmPrimitiveType::INT16:
878
            return new ResourceType(
879
                new Int16(), 
880
                ResourceTypeKind::PRIMITIVE, 
881
                'Int16', 'Edm'
882
            );
883
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
884
        case EdmPrimitiveType::INT32:
885
            return new ResourceType(
886
                new Int32(), 
887
                ResourceTypeKind::PRIMITIVE, 
888
                'Int32', 'Edm'
889
            );
890
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
891
        case EdmPrimitiveType::INT64:
892
            return new ResourceType(
893
                new Int64(), 
894
                ResourceTypeKind::PRIMITIVE, 
895
                'Int64', 'Edm'
896
            );
897
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
898
        case EdmPrimitiveType::SBYTE:
899
            return new ResourceType(
900
                new SByte(), 
901
                ResourceTypeKind::PRIMITIVE, 
902
                'SByte', 'Edm'
903
            );
904
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
905
        case EdmPrimitiveType::SINGLE:
906
            return new ResourceType(
907
                new Single(), 
908
                ResourceTypeKind::PRIMITIVE, 
909
                'Single', 'Edm'
910
            );
911
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
912
        case EdmPrimitiveType::STRING:
913
            return new ResourceType(
914
                new StringType(),
915
                ResourceTypeKind::PRIMITIVE, 
916
                'String', 'Edm'
917
            );
918
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
919
        default:
920
            throw new \InvalidArgumentException(
921
                Messages::commonNotValidPrimitiveEDMType(
922
                    '$typeCode', 'getPrimitiveResourceType'
923
                )
924
            );
925
        }    
926
    }
927
}