/**
  * Iterate over the resource type of the given resource set, 
  * derived resource types base resource types and complex types 
  * used in these resource types and cache them.
  * 
  * @param ResourceSetWrapper $resourceSetWrapper The resource set to inspect
  * 
  * @return void
  * 
  * @throws InvalidOperationException Throws exception in floowing cases:
  * (1) If IDSMP::getDerivedTypes returns any type other than null or array
  * (2) If Named streams are found on derived types
  */
 private function _populateResourceTypeForSet(ResourceSetWrapper $resourceSetWrapper)
 {
     $derivedTypes = $this->metadataQueryproviderWrapper->getDerivedTypes($resourceSetWrapper->getResourceType());
     if (!is_null($derivedTypes)) {
         if (!is_array($derivedTypes)) {
             throw new InvalidOperationException(Messages::metadataAssociationTypeSetInvalidGetDerivedTypesReturnType($resourceSetWrapper->getName()));
         }
         //Populate Resource type for derived types and
         //complex types in derived types
         foreach ($derivedTypes as $derivedType) {
             if ($derivedType->hasNamedStream()) {
                 throw new InvalidOperationException(Messages::metadataResourceTypeSetNamedStreamsOnDerivedEntityTypesNotSupported($resourceSetWrapper->getName(), $derivedType->getFullName()));
             }
             $this->_populateResourceTypes($derivedType);
             $this->_populateComplexTypes($derivedType);
         }
     }
     //Populate Resource type for for this type and
     //base types and complex types in this type and base types
     $resourceType = $resourceSetWrapper->getResourceType();
     while ($resourceType != null) {
         $this->_populateResourceTypes($resourceType);
         $this->_populateComplexTypes($resourceType);
         $resourceType = $resourceType->getBaseType();
     }
 }
 /**
  * Gets the visible resource properties for the given resource type from 
  * the given resource set wrapper.
  * 
  * @param ResourceSetWrapper &$resourceSetWrapper Resource set wrapper in 
  *                                                question.
  * @param ResourceType       &$resourceType       Resource type in question.
  * 
  * @return array(string, ResourceProperty) Collection of visible resource 
  *     properties from the given resource set wrapper and resource type.
  */
 public function getResourceProperties(ResourceSetWrapper &$resourceSetWrapper, ResourceType &$resourceType)
 {
     if ($resourceType->getResourceTypeKind() == ResourceTypeKind::ENTITY) {
         $cacheKey = $resourceSetWrapper->getName() . '_' . $resourceType->getFullName();
         if (array_key_exists($cacheKey, $this->_resourcePropertyCache)) {
             return $this->_resourcePropertyCache[$cacheKey];
         }
         $this->_resourcePropertyCache[$cacheKey] = array();
         foreach ($resourceType->getAllProperties() as $resourceProperty) {
             //Check whether this is a visible navigation property
             if ($resourceProperty->getTypeKind() == ResourceTypeKind::ENTITY && !is_null($this->getResourceSetWrapperForNavigationProperty($resourceSetWrapper, $resourceType, $resourceProperty))) {
                 $this->_resourcePropertyCache[$cacheKey][$resourceProperty->getName()] = $resourceProperty;
             } else {
                 //primitive, bag or complex property
                 $this->_resourcePropertyCache[$cacheKey][$resourceProperty->getName()] = $resourceProperty;
             }
         }
         return $this->_resourcePropertyCache[$cacheKey];
     } else {
         //Complex resource type
         return $resourceType->getAllProperties();
     }
 }
 /**
  * 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;
 }