Example #1
0
 /**
  * Creates new intstance of ResourceSet
  * 
  * @param string       $name         Name of the resource set (entity set)  
  * @param ResourceType $resourceType ResourceType describing the resource 
  *                                   this entity set holds
  * 
  * @throws InvalidArgumentException
  */
 public function __construct($name, ResourceType $resourceType)
 {
     if ($resourceType->getResourceTypeKind() != ResourceTypeKind::ENTITY) {
         throw new \InvalidArgumentException(Messages::resourceSetContainerMustBeAssociatedWithEntityType());
     }
     $this->_name = $name;
     $this->_resourceType = $resourceType;
 }
Example #2
0
 /**
  * Gets the namespace of the given resource type, 
  * if it is null, then default to the container namespace. 
  * 
  * @param ResourceType $resourceType The resource type
  * 
  * @return string The namespace of the resource type.
  */
 protected function getResourceTypeNamespace(ResourceType $resourceType)
 {
     $resourceTypeNamespace = $resourceType->getNamespace();
     if (empty($resourceTypeNamespace)) {
         $resourceTypeNamespace = $this->metadataQueryproviderWrapper->getContainerNamespace();
     }
     return $resourceTypeNamespace;
 }
 /**
  * Call-back for property access expression
  * 
  * @param PropertyAccessExpression $expression The property access expression
  * 
  * @return string
  */
 public function onPropertyAccessExpression($expression)
 {
     $parent = $expression;
     $variable = null;
     $entityTypeName = $this->_resourceType->getName();
     $propertyName = $parent->getResourceProperty()->getName();
     if (is_array($this->_entityMapping)) {
         if (array_key_exists($entityTypeName, $this->_entityMapping)) {
             if (array_key_exists($propertyName, $this->_entityMapping[$entityTypeName])) {
                 return $this->_entityMapping[$entityTypeName][$propertyName];
             }
         }
     }
     return $propertyName;
 }
Example #4
0
 /**
  * Returns the etag for the given resource.
  * Note: This function will not add W\" prefix and " suffix, its callers
  * repsonsability.
  *
  * @param mixed        &$entryObject  Resource for which etag value needs to 
  *                                    be returned
  * @param ResourceType &$resourceType Resource type of the $entryObject
  * 
  * @return string/NULL ETag value for the given resource (with values encoded 
  *                     for use in a URI) there are etag properties, NULL if 
  *                     there is no etag property.
  */
 protected function getETagForEntry(&$entryObject, ResourceType &$resourceType)
 {
     $eTag = null;
     $comma = null;
     foreach ($resourceType->getETagProperties() as $eTagProperty) {
         $type = $eTagProperty->getInstanceType();
         self::assert(!is_null($type) && array_search('ODataProducer\\Providers\\Metadata\\Type\\IType', class_implements($type)) !== false, '!is_null($type) 
             && array_search(\'ODataProducer\\Providers\\Metadata\\Type\\IType\', class_implements($type)) !== false');
         $value = null;
         try {
             $reflectionProperty = new \ReflectionProperty($entryObject, $eTagProperty->getName());
             $value = $reflectionProperty->getValue($entryObject);
         } catch (\ReflectionException $reflectionException) {
             throw ODataException::createInternalServerError(Messages::dataServiceFailedToAccessProperty($eTagProperty->getName(), $resourceType->getName()));
         }
         if (is_null($value)) {
             $eTag = $eTag . $comma . 'null';
         } else {
             $eTag = $eTag . $comma . $type->convertToOData($value);
         }
         $comma = ',';
     }
     if (!is_null($eTag)) {
         // If eTag is made up of datetime or string properties then the above
         // IType::converToOData will perform utf8 and url encode. But we don't
         // want this for eTag value.
         $eTag = urldecode(utf8_decode($eTag));
         return rtrim($eTag, ',');
     }
     return null;
 }
Example #5
0
 /**
  * Gets full name of this type in EDM namespace
  * Note: implementation of IType::getFullTypeName
  * 
  * @return string
  */
 public function getFullTypeName()
 {
     return $this->_resourceType->getFullName();
 }
 /**
  * Validates the given instance of ResourceType
  * 
  * @param ResourceType $resourceType The ResourceType to validate
  * 
  * @return ResourceType
  * 
  * @throws ODataException Exception if $resourceType is invalid
  */
 private function _validateResourceType(ResourceType $resourceType)
 {
     $cacheKey = $resourceType->getName();
     if (array_key_exists($cacheKey, $this->_resourceTypeCache)) {
         return $this->_resourceTypeCache[$cacheKey];
     }
     //TODO: Do validation if any for the ResourceType
     $this->_resourceTypeCache[$cacheKey] = $resourceType;
     return $resourceType;
 }
 /**
  * Retrieve the complex type(s) used in the given resource type and cache them.
  * 
  * @param ResourceType $resourceType The resource type to inspect
  * 
  * @return void
  * 
  * @throws InvalidOperationException If found any complex type bag property 
  * with derived type(s) 
  */
 private function _populateComplexTypes(ResourceType $resourceType)
 {
     foreach ($resourceType->getPropertiesDeclaredOnThisType() as $property) {
         if ($property->isKindOf(ResourcePropertyKind::COMPLEX_TYPE)) {
             if ($property->isKindOf(ResourcePropertyKind::BAG)) {
                 //Validate the bag complex type
                 //as it should not have derived type
                 if ($this->metadataQueryproviderWrapper->hasDerivedTypes($resourceType)) {
                     throw new InvalidOperationException(Messages::metadataResourceTypeSetBagOfComplexTypeWithDerivedTypes($resourceType->getFullName()));
                 }
             }
             if ($this->_populateResourceTypes($property->getResourceType())) {
                 $this->_populateComplexTypes($property->getResourceType());
             }
         }
     }
 }
 /**
  * Gets and validate the ResourceAssociationSet instance for the 
  * given source resource association end
  * This function first searches the ResourceAssociationSet cache, 
  * if found return it otherwise 
  * get the association set from metadata wrapper, 
  * validate, cache and return it.
  * 
  * @param ResourceSetWrapper $resourceSet        Resource set of the 
  *                                               source association end
  * @param ResourceType       $resourceType       Resource type of the 
  *                                               source association end
  * @param ResourceProperty   $navigationProperty Resource property of the 
  *                                               source association end
  * 
  * @return ResourceAssociationSet/NULL The association set instance for the 
  *                                     given association set end,
  *                                     NULL if the metadata wrapper 
  *                                     returns NULL 
  *                                     (either IDSMP implementation
  *                                     returns null or 
  *                                     target resource set is invisible)
  *                                  
  * @throws InvalidOperationException If validation of AssociationSet fails
  * @throws ODataException If validation fails at 
  * MetadataQueryProviderWrapper::getResourceAssociationSet
  */
 private function _getResourceAssociationSet(ResourceSetWrapper $resourceSet, ResourceType $resourceType, ResourceProperty $navigationProperty)
 {
     $associationSetLookupKey = $resourceSet->getName() . '_' . $resourceType->getFullName() . '_' . $navigationProperty->getName();
     if (array_key_exists($associationSetLookupKey, $this->_resourceAssociationSets)) {
         return $this->_resourceAssociationSets[$associationSetLookupKey];
     }
     $resourceAssociationSet = $this->metadataQueryproviderWrapper->getResourceAssociationSet($resourceSet, $resourceType, $navigationProperty);
     if (is_null($resourceAssociationSet)) {
         //Either the related ResourceSet is invisible or IDSMP implementation returns null
         return null;
     }
     /**
      * @var ResourceAssociationSetEnd
      */
     $relatedEnd = $resourceAssociationSet->getRelatedResourceAssociationSetEnd($resourceSet->getResourceSet(), $resourceType, $navigationProperty);
     //For bidirectional relationship IDSMP::getResourceAssociationSet should
     //return same association set when called from either end
     if (!is_null($relatedEnd->getResourceProperty())) {
         //No need to check whether the following call returns NULL,
         //because the above call
         //MetadataQueryproviderWrapper::getResourceAssociationSet
         //causes the metadata wrapper to check the visibility of
         //related resource set and cache the corrosponding wrapper.
         //If found invisible it would have return NULL,
         //which we are any way handling above.
         $relatedResourceSetWrapper = $this->metadataQueryproviderWrapper->validateResourceSetAndGetWrapper($relatedEnd->getResourceSet());
         $reverseResourceAssociationSet = $this->metadataQueryproviderWrapper->getResourceAssociationSet($relatedResourceSetWrapper, $relatedEnd->getResourceType(), $relatedEnd->getResourceProperty());
         if (is_null($reverseResourceAssociationSet) || !is_null($reverseResourceAssociationSet) && $resourceAssociationSet->getName() != $reverseResourceAssociationSet->getName()) {
             throw new InvalidOperationException(Messages::metadataAssociationTypeSetBidirectionalAssociationMustReturnSameResourceAssociationSetFromBothEnd());
         }
     }
     $reverseAssociationSetLookupKey = null;
     if (!is_null($relatedEnd->getResourceProperty())) {
         $reverseAssociationSetLookupKey = $relatedEnd->getResourceSet()->getName() . '_' . $relatedEnd->getResourceProperty()->getResourceType()->getFullName() . '_' . $relatedEnd->getResourceProperty()->getName();
     } else {
         $reverseAssociationSetLookupKey = $relatedEnd->getResourceSet()->getName() . '_Null_' . $resourceType->getFullName() . '_' . $navigationProperty->getName();
     }
     if (array_key_exists($reverseAssociationSetLookupKey, $this->_resourceAssociationSets)) {
         throw new InvalidOperationException(Messages::metadataAssociationTypeSetMultipleAssociationSetsForTheSameAssociationTypeMustNotReferToSameEndSets($this->_resourceAssociationSets[$reverseAssociationSetLookupKey]->getName(), $resourceAssociationSet->getName(), $relatedEnd->getResourceSet()->getName()));
     }
     $this->_resourceAssociationSets[$associationSetLookupKey] = $resourceAssociationSet;
     $this->_resourceAssociationSets[$reverseAssociationSetLookupKey] = $resourceAssociationSet;
     return $resourceAssociationSet;
 }
 /**
  * Write all named streams in the given entity type
  * 
  * @param ResourceType $resourceType resource type
  * 
  * @return nothing
  */
 private function _writeNamedStreams(ResourceType $resourceType)
 {
     $namedStreams = $resourceType->getNamedStreamsDeclaredOnThisType();
     if (!empty($namedStreams)) {
         $this->_iOdataWriter->startElementNs(null, ODataConstants::DATAWEB_NAMEDSTREAMS_ELEMENT, ODataConstants::ODATA_METADATA_NAMESPACE);
         foreach ($namedStreams as $namedStreamName => $resourceStreamInfo) {
             $this->_iOdataWriter->startElementNs(null, ODataConstants::DATAWEB_NAMEDSTREAM_ELEMENT, ODataConstants::ODATA_METADATA_NAMESPACE);
             $this->_iOdataWriter->writeAttribute(ODataConstants::NAME, $resourceStreamInfo->getName());
             $this->_iOdataWriter->endElement();
         }
         $this->_iOdataWriter->endElement();
     }
 }
 /**
  * To add a navigation property (resource set or resource reference)
  * to a resource type
  * 
  * @param ResourceType         $resourceType         The resource type to add 
  *                                                   the resource reference 
  *                                                   or resource 
  *                                                   reference set property to
  * @param string               $name                 The name of the 
  *                                                   property to add
  * @param ResourceSet          $targetResourceSet    The resource set the 
  *                                                   resource reference
  *                                                   or reference 
  *                                                   set property 
  *                                                   ponits to
  * @param ResourcePropertyKind $resourcePropertyKind The property kind
  * 
  * @return void
  */
 private function _addReferencePropertyInternal(ResourceType $resourceType, $name, ResourceSet $targetResourceSet, $resourcePropertyKind)
 {
     try {
         $resourceType->getInstanceType()->getProperty($name);
     } catch (ReflectionException $exception) {
         throw new InvalidOperationException('Can\'t add a property which does not exist on the instance type.');
     }
     if (!($resourcePropertyKind == ResourcePropertyKind::RESOURCESET_REFERENCE || $resourcePropertyKind == ResourcePropertyKind::RESOURCE_REFERENCE)) {
         throw new InvalidOperationException('Property kind should be ResourceSetReference or ResourceReference');
     }
     $targetResourceType = $targetResourceSet->getResourceType();
     $resourceProperty = new ResourceProperty($name, null, $resourcePropertyKind, $targetResourceType);
     $resourceType->addProperty($resourceProperty);
     //Create instance of AssociationSet for this relationship
     $sourceResourceSet = $resourceType->getCustomState();
     if (is_null($sourceResourceSet)) {
         throw new InvalidOperationException('Failed to retrieve the custom state from ' . $resourceType->getName());
     }
     //Customer_Orders_Orders, Order_Customer_Customers
     //(source type::name _ source property::name _ target set::name)
     $assoicationSetKey = $resourceType->getName() . '_' . $name . '_' . $targetResourceSet->getName();
     $associationSet = new ResourceAssociationSet($assoicationSetKey, new ResourceAssociationSetEnd($sourceResourceSet, $resourceType, $resourceProperty), new ResourceAssociationSetEnd($targetResourceSet, $targetResourceSet->getResourceType(), null));
     $this->associationSets[$assoicationSetKey] = $associationSet;
 }
 private function _getOrderResourceType()
 {
     $orderResType = new ResourceType(new ReflectionClass('Order2'), ResourceTypeKind::ENTITY, 'Order', 'Northwind');
     $intResourceType = ResourceType::getPrimitiveResourceType(EdmPrimitiveType::INT32);
     $orderIDPrimProperty = new ResourceProperty('OrderID', null, ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::KEY, $intResourceType);
     $dateTimeResourceType = ResourceType::getPrimitiveResourceType(EdmPrimitiveType::DATETIME);
     $orderDatePrimProperty = new ResourceProperty('OrderDate', null, ResourcePropertyKind::PRIMITIVE, $dateTimeResourceType);
     $stringResourceType = ResourceType::getPrimitiveResourceType(EdmPrimitiveType::STRING);
     $orderShipNamePrimProperty = new ResourceProperty('ShipName', null, ResourcePropertyKind::PRIMITIVE, $stringResourceType);
     $orderResType->addProperty($orderIDPrimProperty);
     $orderResType->addProperty($orderDatePrimProperty);
     $orderResType->addProperty($orderShipNamePrimProperty);
     return $orderResType;
 }
Example #12
0
 /**
  * Get the instance type. If the property is of kind 'Complex', 
  * 'ResourceReference' or 'ResourceSetReference' then this function returns 
  * refernece to ReflectionClass instance for the type. If the property of 
  * kind 'Primitive' then this function returns ITYpe instance for the type.
  * 
  * @return ReflectionClass/IType
  */
 public function getInstanceType()
 {
     return $this->_propertyResourceType->getInstanceType();
 }
 /**
  * Returns the etag for the given resource.
  * 
  * @param mixed        &$entryObject  Resource for which etag value 
  *                                    needs to be returned
  * @param ResourceType &$resourceType Resource type of the $entryObject
  * 
  * @return string/NULL ETag value for the given resource 
  * (with values encoded for use in a URI)
  * if there are etag properties, NULL if there is no etag property.
  */
 protected function getETagForEntry(&$entryObject, ResourceType &$resourceType)
 {
     $eTag = null;
     $comma = null;
     foreach ($resourceType->getETagProperties() as $eTagProperty) {
         $type = $eTagProperty->getInstanceType();
         $this->assert(!is_null($type) && array_search('ODataProducer\\Providers\\Metadata\\Type\\IType', class_implements($type)) !== false, '!is_null($type) 
             && array_search(\'ODataProducer\\Providers\\Metadata\\Type\\IType\', class_implements($type)) !== false');
         $value = $this->getPropertyValue($entryObject, $resourceType, $eTagProperty);
         if (is_null($value)) {
             $eTag = $eTag . $comma . 'null';
         } else {
             $eTag = $eTag . $comma . $type->convertToOData($value);
         }
         $comma = ',';
     }
     if (!is_null($eTag)) {
         // If eTag is made up of datetime or string properties then the above
         // IType::converToOData will perform utf8 and url encode. But we don't
         // want this for eTag value.
         $eTag = urldecode(utf8_decode($eTag));
         return ODataConstants::HTTP_WEAK_ETAG_PREFIX . rtrim($eTag, ',') . '"';
     }
     return null;
 }
Example #14
0
 /**
  * This function perform the following tasks with the help of internal helper
  * functions
  * (1) Read the orderby clause and perform basic syntax errors
  * (2) Build 'Order By Tree', creates anonymous sorter function for each leaf 
  *     node and check for error
  * (3) Build 'OrderInfo' structure, holds information about the navigation 
  *     properties used in the orderby clause (if any) and orderby path if 
  *     IDSQP implementor want to perform sorting
  * (4) Build top level anonymous sorter function
  * (4) Release resources hold by the 'Order By Tree'
  * (5) Create 'InternalOrderInfo' structure, which wraps 'OrderInfo' and top 
  *     level sorter function 
  * 
  * @param ResourceSetWrapper           $resourceSetWrapper ResourceSetWrapper for
  *                                                         the resource targetted
  *                                                         by resource path.
  * @param ResourceType                 $resourceType       ResourceType for the 
  *                                                         resource targetted
  *                                                         by resource path.
  * @param string                       $orderBy            The orderby clause.
  * @param MetadataQueryProviderWrapper $providerWrapper    Reference to the 
  *                                                         wrapper for IDSQP
  *                                                         and IDSMP impl.
  * 
  * @return InternalOrderByInfo
  * 
  * @throws ODataException If any error occur while parsing orderby clause
  */
 public static function parseOrderByClause(ResourceSetWrapper $resourceSetWrapper, ResourceType $resourceType, $orderBy, MetadataQueryProviderWrapper $providerWrapper)
 {
     $orderByParser = new OrderByParser($providerWrapper);
     try {
         $orderByParser->_dummyObject = $resourceType->getInstanceType()->newInstance();
     } catch (\ReflectionException $reflectionException) {
         throw ODataException::createInternalServerError(Messages::orderByParserFailedToCreateDummyObject());
     }
     $orderByParser->_rootOrderByNode = new OrderByRootNode($resourceSetWrapper, $resourceType);
     $orderByPathSegments = $orderByParser->_readOrderBy($orderBy);
     $orderByParser->_buildOrderByTree($orderByPathSegments);
     $orderByParser->_createOrderInfo($orderByPathSegments);
     $orderByParser->_generateTopLevelComparisonFunction();
     //Recursively release the resources
     $orderByParser->_rootOrderByNode->free();
     //creates internal order info wrapper
     $internalOrderInfo = new InternalOrderByInfo($orderByParser->_orderByInfo, $orderByParser->_comparisonFunctions, $orderByParser->_topLevelComparisonFunction, $orderByParser->_dummyObject);
     unset($orderByParser->_orderByInfo);
     unset($orderByParser->_topLevelComparisonFunction);
     return $internalOrderInfo;
 }
 /**
  * Convert the given primitive value to string.
  * Note: This method will not handle null primitive value.
  *
  * @param ResourceType &$primtiveResourceType Type of the primitive property
  *                                            whose value need to be converted.
  * @param mixed        $primitiveValue        Primitive value to convert.
  * @param string       &$stringValue          On return, this parameter will
  *                                            contain converted value.
  *
  * @return void
  */
 private function _primitiveToString(ResourceType &$primtiveResourceType, $primitiveValue, &$stringValue)
 {
     $type = $primtiveResourceType->getInstanceType();
     if ($type instanceof Boolean) {
         $stringValue = $primitiveValue === true ? 'true' : 'false';
     } else {
         if ($type instanceof Binary) {
             $stringValue = base64_encode($primitiveValue);
         } else {
             if ($type instanceof OString) {
                 $stringValue = utf8_encode($primitiveValue);
             } else {
                 $stringValue = strval($primitiveValue);
             }
         }
     }
 }
Example #16
0
 /**
  * Validate this KeyDescriptor, If valid, this function populates
  * _validatedNamedValues array with key as keyName and value as an array of
  * key value and key type.
  *
  * @param string       $segmentAsString The segment in the form identifer
  *                                      (keyPredicate) which this descriptor
  *                                      represents
  * @param ResourceType $resourceType    The type of the idenfier in the segment
  *
  * @return void
  *
  * @throws ODataException If validation fails.
  */
 public function validate($segmentAsString, ResourceType $resourceType)
 {
     if ($this->isEmpty()) {
         $this->_validatedNamedValues = array();
         return;
     }
     $keyProperties = $resourceType->getKeyProperties();
     $keyPropertiesCount = count($keyProperties);
     if (!empty($this->_namedValues)) {
         if (count($this->_namedValues) != $keyPropertiesCount) {
             ODataException::createSyntaxError(Messages::keyDescriptorKeyCountNotMatching($segmentAsString, $keyPropertiesCount, count($this->_namedValues)));
         }
         foreach ($keyProperties as $keyName => $keyResourceProperty) {
             if (!array_key_exists($keyName, $this->_namedValues)) {
                 $keysAsString = null;
                 foreach (array_keys($keyProperties) as $key) {
                     $keysAsString .= $key . ', ';
                 }
                 $keysAsString = rtrim($keysAsString, ' ,');
                 ODataException::createSyntaxError(Messages::keyDescriptorMissingKeys($segmentAsString, $keysAsString));
             }
             $typeProvided = $this->_namedValues[$keyName][1];
             $expectedType = $keyResourceProperty->getInstanceType();
             if (!$expectedType->isCompatibleWith($typeProvided)) {
                 ODataException::createSyntaxError(Messages::keyDescriptorInCompatibleKeyType($segmentAsString, $keyName, $expectedType->getFullTypeName(), $typeProvided->getFullTypeName()));
             }
             $this->_validatedNamedValues[$keyName] = $this->_namedValues[$keyName];
         }
     } else {
         if (count($this->_positionalValues) != $keyPropertiesCount) {
             ODataException::createSyntaxError(Messages::keyDescriptorKeyCountNotMatching($segmentAsString, $keyPropertiesCount, count($this->_positionalValues)));
         }
         $i = 0;
         foreach ($keyProperties as $keyName => $keyResourceProperty) {
             $typeProvided = $this->_positionalValues[$i][1];
             $expectedType = $keyResourceProperty->getInstanceType();
             if (!$expectedType->isCompatibleWith($typeProvided)) {
                 ODataException::createSyntaxError(Messages::keyDescriptorInCompatibleKeyTypeAtPosition($segmentAsString, $keyResourceProperty->getName(), $i, $expectedType->getFullTypeName(), $typeProvided->getFullTypeName()));
             }
             $this->_validatedNamedValues[$keyName] = $this->_positionalValues[$i];
             $i++;
         }
     }
 }
 /**
  * To check this relationship belongs to a specfic entity property
  *  
  * @param ResourceType          $resourceType     The type of the entity
  * @param ResourceProperty/NULL $resourceProperty The property in the entity
  * 
  * @return boolean
  */
 public function isBelongsTo(ResourceType $resourceType, $resourceProperty)
 {
     $flag1 = is_null($resourceProperty);
     $flag2 = is_null($this->_resourceProperty);
     if ($flag1 != $flag2) {
         return false;
     }
     if ($flag1 === true) {
         return strcmp($resourceType->getFullName(), $this->_resourceType->getFullName()) == 0;
     }
     return strcmp($resourceType->getFullName(), $this->_resourceType->getFullName()) == 0 && strcmp($resourceProperty->getName(), $this->_resourceProperty->getName()) == 0;
 }
Example #18
0
 /**
  * Check this resource type instance has bag property associated with it
  * Note: This is an internal method used by library. Devs don't use this.
  *
  * @param array(mixed) &$arrayToDetectLoopInComplexType array for detecting loop.
  *
  * @return boolean true if resource type instance has bag property else false
  */
 public function hasBagProperty(&$arrayToDetectLoopInComplexType)
 {
     // Note: Calling this method will initialize _bagProperties
     // and _hasBagProperty flag to a boolean value
     // from null depending on the current state of
     // _propertiesDeclaredOnThisType array, so method
     // should be called only after adding all properties
     if (is_null($this->_hasBagProperty)) {
         if ($this->_baseType != null && $this->_baseType->hasBagProperty($arrayToDetectLoopInComplexType)) {
             $this->_hasBagProperty = true;
         } else {
             foreach ($this->_propertiesDeclaredOnThisType as $resourceProperty) {
                 $hasBagInComplex = false;
                 if ($resourceProperty->isKindOf(ResourcePropertyKind::COMPLEX_TYPE)) {
                     //We can say current ResouceType ("this")
                     //is contains a bag property if:
                     //1. It contain a property of kind bag.
                     //2. It contains a normal complex property
                     //with a sub property of kind bag.
                     //The second case can be further expanded, i.e.
                     //if the normal complex property
                     //has a normal complex sub property with a
                     //sub property of kind bag.
                     //So for complex type we recursively call this
                     //function to check for bag.
                     //Shown below how looping can happen in complex type:
                     //Customer ResourceType (id1)
                     //{
                     //  ....
                     //  Address: Address ResourceType (id2)
                     //  {
                     //    .....
                     //    AltAddress: Address ResourceType (id2)
                     //    {
                     //      ...
                     //    }
                     //  }
                     //}
                     //
                     //Here the resource type of Customer::Address and
                     //Customer::Address::AltAddress
                     //are same, this is a loop, we need to detect
                     //this and avoid infinite recursive loop.
                     //
                     $count = count($arrayToDetectLoopInComplexType);
                     $foundLoop = false;
                     for ($i = 0; $i < $count; $i++) {
                         if ($arrayToDetectLoopInComplexType[$i] === $resourceProperty->getResourceType()) {
                             $foundLoop = true;
                             break;
                         }
                     }
                     if (!$foundLoop) {
                         $arrayToDetectLoopInComplexType[$count] = $resourceProperty->getResourceType();
                         $hasBagInComplex = $resourceProperty->getResourceType()->hasBagProperty($arrayToDetectLoopInComplexType);
                         unset($arrayToDetectLoopInComplexType[$count]);
                     }
                 }
                 if ($resourceProperty->isKindOf(ResourcePropertyKind::BAG) || $hasBagInComplex) {
                     $this->_hasBagProperty = true;
                     break;
                 }
             }
         }
     }
     return $this->_hasBagProperty;
 }
 /**
  * To check this relationship belongs to a specfic resource set, type 
  * and property
  * 
  * @param ResourceSet      $resourceSet      Resource set for the association
  *                                           end
  * @param ResourceType     $resourceType     Resource type for the association
  *                                           end
  * @param ResourceProperty $resourceProperty Resource property for the 
  *                                           association end
  * 
  * @return boolean
  */
 public function isBelongsTo(ResourceSet $resourceSet, ResourceType $resourceType, ResourceProperty $resourceProperty)
 {
     return strcmp($resourceSet->getName(), $this->_resourceSet->getName()) == 0 && $this->_resourceType->isAssignableFrom($resourceType) && (is_null($resourceProperty) && is_null($this->_resourceProperty) || !is_null($resourceProperty) && !is_null($this->_resourceProperty) && strcmp($resourceProperty->getName(), $this->_resourceProperty->getName()) == 0);
 }