/**
  * Evaluate the property annotations and amend the metadata accordingly.
  *
  * @param ClassMetadataInfo $metadata
  * @return void
  * @throws MappingException
  */
 protected function evaluatePropertyAnnotations(ClassMetadataInfo $metadata)
 {
     $className = $metadata->name;
     $class = $metadata->getReflectionClass();
     $classSchema = $this->getClassSchema($className);
     foreach ($class->getProperties() as $property) {
         if (!$classSchema->hasProperty($property->getName()) || $classSchema->isPropertyTransient($property->getName()) || $metadata->isMappedSuperclass && !$property->isPrivate() || $metadata->isInheritedField($property->getName()) || $metadata->isInheritedAssociation($property->getName())) {
             continue;
         }
         $propertyMetaData = $classSchema->getProperty($property->getName());
         $mapping = array();
         $mapping['fieldName'] = $property->getName();
         $mapping['columnName'] = strtolower($property->getName());
         $mapping['targetEntity'] = $propertyMetaData['type'];
         $joinColumns = $this->evaluateJoinColumnAnnotations($property);
         // Field can only be annotated with one of:
         // @OneToOne, @OneToMany, @ManyToOne, @ManyToMany, @Column (optional)
         if ($oneToOneAnnotation = $this->reader->getPropertyAnnotation($property, 'Doctrine\\ORM\\Mapping\\OneToOne')) {
             if ($oneToOneAnnotation->targetEntity) {
                 $mapping['targetEntity'] = $oneToOneAnnotation->targetEntity;
             }
             if ($oneToOneAnnotation->inversedBy !== null || $oneToOneAnnotation->mappedBy === null) {
                 $mapping['joinColumns'] = $this->buildJoinColumnsIfNeeded($joinColumns, $mapping, $property);
             }
             $mapping['mappedBy'] = $oneToOneAnnotation->mappedBy;
             $mapping['inversedBy'] = $oneToOneAnnotation->inversedBy;
             if ($oneToOneAnnotation->cascade) {
                 $mapping['cascade'] = $oneToOneAnnotation->cascade;
             } elseif ($this->isValueObject($mapping['targetEntity'], $className)) {
                 $mapping['cascade'] = array('persist');
             } elseif ($this->isAggregateRoot($mapping['targetEntity'], $className) === false) {
                 $mapping['cascade'] = array('all');
             }
             if ($oneToOneAnnotation->orphanRemoval) {
                 $mapping['orphanRemoval'] = $oneToOneAnnotation->orphanRemoval;
             } elseif ($this->isAggregateRoot($mapping['targetEntity'], $className) === false && $this->isValueObject($mapping['targetEntity'], $className) === false) {
                 $mapping['orphanRemoval'] = true;
             }
             $mapping['fetch'] = $this->getFetchMode($className, $oneToOneAnnotation->fetch);
             $metadata->mapOneToOne($mapping);
         } elseif ($oneToManyAnnotation = $this->reader->getPropertyAnnotation($property, 'Doctrine\\ORM\\Mapping\\OneToMany')) {
             $mapping['mappedBy'] = $oneToManyAnnotation->mappedBy;
             if ($oneToManyAnnotation->targetEntity) {
                 $mapping['targetEntity'] = $oneToManyAnnotation->targetEntity;
             } elseif (isset($propertyMetaData['elementType'])) {
                 $mapping['targetEntity'] = $propertyMetaData['elementType'];
             }
             if ($oneToManyAnnotation->cascade) {
                 $mapping['cascade'] = $oneToManyAnnotation->cascade;
             } elseif ($this->isValueObject($mapping['targetEntity'], $className)) {
                 $mapping['cascade'] = array('persist');
             } elseif ($this->isAggregateRoot($mapping['targetEntity'], $className) === false) {
                 $mapping['cascade'] = array('all');
             }
             $mapping['indexBy'] = $oneToManyAnnotation->indexBy;
             if ($oneToManyAnnotation->orphanRemoval) {
                 $mapping['orphanRemoval'] = $oneToManyAnnotation->orphanRemoval;
             } elseif ($this->isAggregateRoot($mapping['targetEntity'], $className) === false && $this->isValueObject($mapping['targetEntity'], $className) === false) {
                 $mapping['orphanRemoval'] = true;
             }
             $mapping['fetch'] = $this->getFetchMode($className, $oneToManyAnnotation->fetch);
             if ($orderByAnnotation = $this->reader->getPropertyAnnotation($property, 'Doctrine\\ORM\\Mapping\\OrderBy')) {
                 $mapping['orderBy'] = $orderByAnnotation->value;
             }
             $metadata->mapOneToMany($mapping);
         } elseif ($manyToOneAnnotation = $this->reader->getPropertyAnnotation($property, 'Doctrine\\ORM\\Mapping\\ManyToOne')) {
             if ($manyToOneAnnotation->targetEntity) {
                 $mapping['targetEntity'] = $manyToOneAnnotation->targetEntity;
             }
             $mapping['joinColumns'] = $this->buildJoinColumnsIfNeeded($joinColumns, $mapping, $property);
             if ($manyToOneAnnotation->cascade) {
                 $mapping['cascade'] = $manyToOneAnnotation->cascade;
             } elseif ($this->isValueObject($mapping['targetEntity'], $className)) {
                 $mapping['cascade'] = array('persist');
             } elseif ($this->isAggregateRoot($mapping['targetEntity'], $className) === false) {
                 $mapping['cascade'] = array('all');
             }
             $mapping['inversedBy'] = $manyToOneAnnotation->inversedBy;
             $mapping['fetch'] = $this->getFetchMode($className, $manyToOneAnnotation->fetch);
             $metadata->mapManyToOne($mapping);
         } elseif ($manyToManyAnnotation = $this->reader->getPropertyAnnotation($property, 'Doctrine\\ORM\\Mapping\\ManyToMany')) {
             if ($manyToManyAnnotation->targetEntity) {
                 $mapping['targetEntity'] = $manyToManyAnnotation->targetEntity;
             } elseif (isset($propertyMetaData['elementType'])) {
                 $mapping['targetEntity'] = $propertyMetaData['elementType'];
             }
             /** @var JoinTable $joinTableAnnotation */
             if ($joinTableAnnotation = $this->reader->getPropertyAnnotation($property, 'Doctrine\\ORM\\Mapping\\JoinTable')) {
                 $joinTable = $this->evaluateJoinTableAnnotation($joinTableAnnotation, $property, $className, $mapping);
             } else {
                 $joinColumns = array(array('name' => null, 'referencedColumnName' => null));
                 $joinTable = array('name' => $this->inferJoinTableNameFromClassAndPropertyName($className, $property->getName()), 'joinColumns' => $this->buildJoinColumnsIfNeeded($joinColumns, $mapping, $property, self::MAPPING_MM_REGULAR), 'inverseJoinColumns' => $this->buildJoinColumnsIfNeeded($joinColumns, $mapping, $property));
             }
             $mapping['joinTable'] = $joinTable;
             $mapping['mappedBy'] = $manyToManyAnnotation->mappedBy;
             $mapping['inversedBy'] = $manyToManyAnnotation->inversedBy;
             if ($manyToManyAnnotation->cascade) {
                 $mapping['cascade'] = $manyToManyAnnotation->cascade;
             } elseif ($this->isValueObject($mapping['targetEntity'], $className)) {
                 $mapping['cascade'] = array('persist');
             } elseif ($this->isAggregateRoot($mapping['targetEntity'], $className) === false) {
                 $mapping['cascade'] = array('all');
             }
             $mapping['indexBy'] = $manyToManyAnnotation->indexBy;
             $mapping['orphanRemoval'] = $manyToManyAnnotation->orphanRemoval;
             $mapping['fetch'] = $this->getFetchMode($className, $manyToManyAnnotation->fetch);
             if ($orderByAnnotation = $this->reader->getPropertyAnnotation($property, 'Doctrine\\ORM\\Mapping\\OrderBy')) {
                 $mapping['orderBy'] = $orderByAnnotation->value;
             }
             $metadata->mapManyToMany($mapping);
         } else {
             $mapping['nullable'] = false;
             /** @var Column $columnAnnotation */
             if ($columnAnnotation = $this->reader->getPropertyAnnotation($property, 'Doctrine\\ORM\\Mapping\\Column')) {
                 $mapping = $this->addColumnToMappingArray($columnAnnotation, $mapping);
             }
             if (!isset($mapping['type'])) {
                 switch ($propertyMetaData['type']) {
                     case 'DateTime':
                         $mapping['type'] = 'datetime';
                         break;
                     case 'string':
                     case 'integer':
                     case 'boolean':
                     case 'float':
                     case 'array':
                         $mapping['type'] = $propertyMetaData['type'];
                         break;
                     default:
                         if (strpos($propertyMetaData['type'], '\\') !== false) {
                             if ($this->reflectionService->isClassAnnotatedWith($propertyMetaData['type'], \TYPO3\Flow\Annotations\ValueObject::class)) {
                                 $mapping['type'] = 'object';
                             } elseif (class_exists($propertyMetaData['type'])) {
                                 throw MappingException::missingRequiredOption($property->getName(), 'OneToOne', sprintf('The property "%s" in class "%s" has a non standard data type and doesn\'t define the type of the relation. You have to use one of these annotations: @OneToOne, @OneToMany, @ManyToOne, @ManyToMany', $property->getName(), $className));
                             }
                         } else {
                             throw MappingException::propertyTypeIsRequired($className, $property->getName());
                         }
                 }
             }
             if ($this->reader->getPropertyAnnotation($property, 'Doctrine\\ORM\\Mapping\\Id') !== null) {
                 $mapping['id'] = true;
             }
             if ($generatedValueAnnotation = $this->reader->getPropertyAnnotation($property, 'Doctrine\\ORM\\Mapping\\GeneratedValue')) {
                 $metadata->setIdGeneratorType(constant('Doctrine\\ORM\\Mapping\\ClassMetadata::GENERATOR_TYPE_' . strtoupper($generatedValueAnnotation->strategy)));
             }
             if ($this->reflectionService->isPropertyAnnotatedWith($className, $property->getName(), 'Doctrine\\ORM\\Mapping\\Version')) {
                 $metadata->setVersionMapping($mapping);
             }
             $metadata->mapField($mapping);
             // Check for SequenceGenerator/TableGenerator definition
             if ($seqGeneratorAnnotation = $this->reader->getPropertyAnnotation($property, 'Doctrine\\ORM\\Mapping\\SequenceGenerator')) {
                 $metadata->setSequenceGeneratorDefinition(array('sequenceName' => $seqGeneratorAnnotation->sequenceName, 'allocationSize' => $seqGeneratorAnnotation->allocationSize, 'initialValue' => $seqGeneratorAnnotation->initialValue));
             } elseif ($this->reader->getPropertyAnnotation($property, 'Doctrine\\ORM\\Mapping\\TableGenerator') !== null) {
                 throw MappingException::tableIdGeneratorNotImplemented($className);
             } elseif ($customGeneratorAnnotation = $this->reader->getPropertyAnnotation($property, 'Doctrine\\ORM\\Mapping\\CustomIdGenerator')) {
                 $metadata->setCustomGeneratorDefinition(array('class' => $customGeneratorAnnotation->class));
             }
         }
     }
 }
 /**
  * Evaluate the property annotations and amend the metadata accordingly.
  *
  * @param ORM\ClassMetadataInfo $metadata
  * @return void
  * @throws ORM\MappingException
  */
 protected function evaluatePropertyAnnotations(ORM\ClassMetadataInfo $metadata)
 {
     $className = $metadata->name;
     $class = $metadata->getReflectionClass();
     $classSchema = $this->getClassSchema($className);
     foreach ($class->getProperties() as $property) {
         if (!$classSchema->hasProperty($property->getName()) || $classSchema->isPropertyTransient($property->getName()) || $metadata->isMappedSuperclass && !$property->isPrivate() || $metadata->isInheritedField($property->getName()) || $metadata->isInheritedAssociation($property->getName()) || $metadata->isInheritedEmbeddedClass($property->getName())) {
             continue;
         }
         $propertyMetaData = $classSchema->getProperty($property->getName());
         $mapping = [];
         $mapping['fieldName'] = $property->getName();
         $mapping['columnName'] = strtolower($property->getName());
         $mapping['targetEntity'] = $propertyMetaData['type'];
         $joinColumns = $this->evaluateJoinColumnAnnotations($property);
         // Field can only be annotated with one of:
         // @OneToOne, @OneToMany, @ManyToOne, @ManyToMany, @Column (optional)
         if ($oneToOneAnnotation = $this->reader->getPropertyAnnotation($property, ORM\OneToOne::class)) {
             if ($this->reader->getPropertyAnnotation($property, ORM\Id::class) !== null) {
                 $mapping['id'] = true;
             }
             if ($oneToOneAnnotation->targetEntity) {
                 $mapping['targetEntity'] = $oneToOneAnnotation->targetEntity;
             }
             if ($oneToOneAnnotation->inversedBy !== null || $oneToOneAnnotation->mappedBy === null) {
                 $mapping['joinColumns'] = $this->buildJoinColumnsIfNeeded($joinColumns, $mapping, $property);
             }
             $mapping['mappedBy'] = $oneToOneAnnotation->mappedBy;
             $mapping['inversedBy'] = $oneToOneAnnotation->inversedBy;
             if ($oneToOneAnnotation->cascade) {
                 $mapping['cascade'] = $oneToOneAnnotation->cascade;
             } elseif ($this->isValueObject($mapping['targetEntity'], $className)) {
                 $mapping['cascade'] = ['persist'];
             } elseif ($this->isAggregateRoot($mapping['targetEntity'], $className) === false) {
                 $mapping['cascade'] = ['all'];
             }
             if ($oneToOneAnnotation->orphanRemoval) {
                 $mapping['orphanRemoval'] = $oneToOneAnnotation->orphanRemoval;
             } elseif ($this->isAggregateRoot($mapping['targetEntity'], $className) === false && $this->isValueObject($mapping['targetEntity'], $className) === false) {
                 $mapping['orphanRemoval'] = true;
             }
             $mapping['fetch'] = $this->getFetchMode($className, $oneToOneAnnotation->fetch);
             $metadata->mapOneToOne($mapping);
         } elseif ($oneToManyAnnotation = $this->reader->getPropertyAnnotation($property, ORM\OneToMany::class)) {
             $mapping['mappedBy'] = $oneToManyAnnotation->mappedBy;
             if ($oneToManyAnnotation->targetEntity) {
                 $mapping['targetEntity'] = $oneToManyAnnotation->targetEntity;
             } elseif (isset($propertyMetaData['elementType'])) {
                 $mapping['targetEntity'] = $propertyMetaData['elementType'];
             }
             if ($oneToManyAnnotation->cascade) {
                 $mapping['cascade'] = $oneToManyAnnotation->cascade;
             } elseif ($this->isValueObject($mapping['targetEntity'], $className)) {
                 $mapping['cascade'] = ['persist'];
             } elseif ($this->isAggregateRoot($mapping['targetEntity'], $className) === false) {
                 $mapping['cascade'] = ['all'];
             }
             $mapping['indexBy'] = $oneToManyAnnotation->indexBy;
             if ($oneToManyAnnotation->orphanRemoval) {
                 $mapping['orphanRemoval'] = $oneToManyAnnotation->orphanRemoval;
             } elseif ($this->isAggregateRoot($mapping['targetEntity'], $className) === false && $this->isValueObject($mapping['targetEntity'], $className) === false) {
                 $mapping['orphanRemoval'] = true;
             }
             $mapping['fetch'] = $this->getFetchMode($className, $oneToManyAnnotation->fetch);
             if ($orderByAnnotation = $this->reader->getPropertyAnnotation($property, ORM\OrderBy::class)) {
                 $mapping['orderBy'] = $orderByAnnotation->value;
             }
             $metadata->mapOneToMany($mapping);
         } elseif ($manyToOneAnnotation = $this->reader->getPropertyAnnotation($property, ORM\ManyToOne::class)) {
             if ($this->reader->getPropertyAnnotation($property, ORM\Id::class) !== null) {
                 $mapping['id'] = true;
             }
             if ($manyToOneAnnotation->targetEntity) {
                 $mapping['targetEntity'] = $manyToOneAnnotation->targetEntity;
             }
             $mapping['joinColumns'] = $this->buildJoinColumnsIfNeeded($joinColumns, $mapping, $property);
             if ($manyToOneAnnotation->cascade) {
                 $mapping['cascade'] = $manyToOneAnnotation->cascade;
             } elseif ($this->isValueObject($mapping['targetEntity'], $className)) {
                 $mapping['cascade'] = ['persist'];
             } elseif ($this->isAggregateRoot($mapping['targetEntity'], $className) === false) {
                 $mapping['cascade'] = ['all'];
             }
             $mapping['inversedBy'] = $manyToOneAnnotation->inversedBy;
             $mapping['fetch'] = $this->getFetchMode($className, $manyToOneAnnotation->fetch);
             $metadata->mapManyToOne($mapping);
         } elseif ($manyToManyAnnotation = $this->reader->getPropertyAnnotation($property, ORM\ManyToMany::class)) {
             if ($manyToManyAnnotation->targetEntity) {
                 $mapping['targetEntity'] = $manyToManyAnnotation->targetEntity;
             } elseif (isset($propertyMetaData['elementType'])) {
                 $mapping['targetEntity'] = $propertyMetaData['elementType'];
             }
             /** @var ORM\JoinTable $joinTableAnnotation */
             if ($joinTableAnnotation = $this->reader->getPropertyAnnotation($property, ORM\JoinTable::class)) {
                 $joinTable = $this->evaluateJoinTableAnnotation($joinTableAnnotation, $property, $className, $mapping);
             } else {
                 $joinColumns = [['name' => null, 'referencedColumnName' => null]];
                 $joinTable = ['name' => $this->inferJoinTableNameFromClassAndPropertyName($className, $property->getName()), 'joinColumns' => $this->buildJoinColumnsIfNeeded($joinColumns, $mapping, $property, self::MAPPING_MM_REGULAR), 'inverseJoinColumns' => $this->buildJoinColumnsIfNeeded($joinColumns, $mapping, $property)];
             }
             $mapping['joinTable'] = $joinTable;
             $mapping['mappedBy'] = $manyToManyAnnotation->mappedBy;
             $mapping['inversedBy'] = $manyToManyAnnotation->inversedBy;
             if ($manyToManyAnnotation->cascade) {
                 $mapping['cascade'] = $manyToManyAnnotation->cascade;
             } elseif ($this->isValueObject($mapping['targetEntity'], $className)) {
                 $mapping['cascade'] = ['persist'];
             } elseif ($this->isAggregateRoot($mapping['targetEntity'], $className) === false) {
                 $mapping['cascade'] = ['all'];
             }
             $mapping['indexBy'] = $manyToManyAnnotation->indexBy;
             $mapping['orphanRemoval'] = $manyToManyAnnotation->orphanRemoval;
             $mapping['fetch'] = $this->getFetchMode($className, $manyToManyAnnotation->fetch);
             if ($orderByAnnotation = $this->reader->getPropertyAnnotation($property, ORM\OrderBy::class)) {
                 $mapping['orderBy'] = $orderByAnnotation->value;
             }
             $metadata->mapManyToMany($mapping);
         } elseif ($embeddedAnnotation = $this->reader->getPropertyAnnotation($property, ORM\Embedded::class)) {
             if ($embeddedAnnotation->class) {
                 $mapping['class'] = $embeddedAnnotation->class;
             } else {
                 // This will not happen currently, because "class" argument is required. It would be nice if that could be changed though.
                 $mapping['class'] = $mapping['targetEntity'];
             }
             $mapping['columnPrefix'] = $embeddedAnnotation->columnPrefix;
             $metadata->mapEmbedded($mapping);
         } else {
             $mapping['nullable'] = false;
             /** @var ORM\Column $columnAnnotation */
             if ($columnAnnotation = $this->reader->getPropertyAnnotation($property, ORM\Column::class)) {
                 $mapping = $this->addColumnToMappingArray($columnAnnotation, $mapping);
             }
             if (!isset($mapping['type'])) {
                 switch ($propertyMetaData['type']) {
                     case 'DateTime':
                         $mapping['type'] = 'datetime';
                         break;
                     case 'string':
                     case 'integer':
                     case 'boolean':
                     case 'float':
                     case 'array':
                         $mapping['type'] = $propertyMetaData['type'];
                         break;
                     default:
                         if (strpos($propertyMetaData['type'], '\\') !== false) {
                             if ($this->reflectionService->isClassAnnotatedWith($propertyMetaData['type'], Flow\ValueObject::class)) {
                                 $valueObjectAnnotation = $this->reflectionService->getClassAnnotation($propertyMetaData['type'], Flow\ValueObject::class);
                                 if ($valueObjectAnnotation->embedded === true) {
                                     $mapping['class'] = $propertyMetaData['type'];
                                     $mapping['columnPrefix'] = $mapping['columnName'];
                                     $metadata->mapEmbedded($mapping);
                                     // Leave switch and continue with next property
                                     continue 2;
                                 }
                                 $mapping['type'] = 'object';
                             } elseif (class_exists($propertyMetaData['type'])) {
                                 throw ORM\MappingException::missingRequiredOption($property->getName(), 'OneToOne', sprintf('The property "%s" in class "%s" has a non standard data type and doesn\'t define the type of the relation. You have to use one of these annotations: @OneToOne, @OneToMany, @ManyToOne, @ManyToMany', $property->getName(), $className));
                             }
                         } else {
                             throw ORM\MappingException::propertyTypeIsRequired($className, $property->getName());
                         }
                 }
             }
             if ($this->reader->getPropertyAnnotation($property, ORM\Id::class) !== null) {
                 $mapping['id'] = true;
             }
             if ($generatedValueAnnotation = $this->reader->getPropertyAnnotation($property, ORM\GeneratedValue::class)) {
                 $metadata->setIdGeneratorType(constant('Doctrine\\ORM\\Mapping\\ClassMetadata::GENERATOR_TYPE_' . strtoupper($generatedValueAnnotation->strategy)));
             }
             if ($this->reflectionService->isPropertyAnnotatedWith($className, $property->getName(), ORM\Version::class)) {
                 $metadata->setVersionMapping($mapping);
             }
             $metadata->mapField($mapping);
             // Check for SequenceGenerator/TableGenerator definition
             if ($seqGeneratorAnnotation = $this->reader->getPropertyAnnotation($property, ORM\SequenceGenerator::class)) {
                 $metadata->setSequenceGeneratorDefinition(['sequenceName' => $seqGeneratorAnnotation->sequenceName, 'allocationSize' => $seqGeneratorAnnotation->allocationSize, 'initialValue' => $seqGeneratorAnnotation->initialValue]);
             } elseif ($this->reader->getPropertyAnnotation($property, ORM\TableGenerator::class) !== null) {
                 throw ORM\MappingException::tableIdGeneratorNotImplemented($className);
             } elseif ($customGeneratorAnnotation = $this->reader->getPropertyAnnotation($property, ORM\CustomIdGenerator::class)) {
                 $metadata->setCustomGeneratorDefinition(['class' => $customGeneratorAnnotation->class]);
             }
         }
         // Evaluate @Cache annotation
         if (($cacheAnnotation = $this->reader->getPropertyAnnotation($property, ORM\Cache::class)) !== null) {
             $metadata->enableAssociationCache($mapping['fieldName'], array('usage' => constant('Doctrine\\ORM\\Mapping\\ClassMetadata::CACHE_USAGE_' . $cacheAnnotation->usage), 'region' => $cacheAnnotation->region));
         }
     }
 }