@param Annotation\Annotation[] $propertyAnnotations

@return Mapping\ManyToManyAssociationMetadata
164649
private function convertReflectionPropertyToManyToManyAssociationMetadata(
        ReflectionProperty $reflectionProperty,
        array $propertyAnnotations,
        Mapping\ClassMetadata $classMetadata
    ) {
        $className       = $classMetadata->getClassName();
        $fieldName       = $reflectionProperty->getName();
        $manyToManyAnnot = $propertyAnnotations[Annotation\ManyToMany::class];

        if ($manyToManyAnnot->targetEntity === null) {
            throw Mapping\MappingException::missingTargetEntity($fieldName);
        }

        $assocMetadata = new Mapping\ManyToManyAssociationMetadata($fieldName);
        $targetEntity  = $manyToManyAnnot->targetEntity;

        $assocMetadata->setSourceEntity($className);
        $assocMetadata->setTargetEntity($targetEntity);
        $assocMetadata->setCascade($this->getCascade($className, $fieldName, $manyToManyAnnot->cascade));
        $assocMetadata->setOrphanRemoval($manyToManyAnnot->orphanRemoval);
        $assocMetadata->setFetchMode($this->getFetchMode($className, $manyToManyAnnot->fetch));

        if (! empty($manyToManyAnnot->mappedBy)) {
            $assocMetadata->setMappedBy($manyToManyAnnot->mappedBy);
        }

        if (! empty($manyToManyAnnot->inversedBy)) {
            $assocMetadata->setInversedBy($manyToManyAnnot->inversedBy);
        }

        if (! empty($manyToManyAnnot->indexBy)) {
            $assocMetadata->setIndexedBy($manyToManyAnnot->indexBy);
        }

        // Check for JoinTable
        if (isset($propertyAnnotations[Annotation\JoinTable::class])) {
            $joinTableAnnot    = $propertyAnnotations[Annotation\JoinTable::class];
            $joinTableMetadata = $this->convertJoinTableAnnotationToJoinTableMetadata(
                $reflectionProperty,
                $joinTableAnnot,
                $classMetadata
            );

            $assocMetadata->setJoinTable($joinTableMetadata);
        }

        // Check for OrderBy
        if (isset($propertyAnnotations[Annotation\OrderBy::class])) {
            $orderByAnnot = $propertyAnnotations[Annotation\OrderBy::class];

            $assocMetadata->setOrderBy($orderByAnnot->value);
        }

        // Check for Id
        if (isset($propertyAnnotations[Annotation\Id::class])) {
            $assocMetadata->setPrimaryKey(true);
        }

        // Check for Cache
        if (isset($propertyAnnotations[Annotation\Cache::class])) {
            $cacheAnnot    = $propertyAnnotations[Annotation\Cache::class];
            $cacheMetadata = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $classMetadata, $fieldName);

            $assocMetadata->setCache($cacheMetadata);
        }

        return $assocMetadata;
    }
368088
private function convertClassAnnotationsToClassMetadata(
        array $classAnnotations,
        ReflectionClass $reflectionClass,
        Mapping\ClassMetadata $parent
    ) {
        switch (true) {
            case isset($classAnnotations[Annotation\Entity::class]):
                return $this->convertClassAnnotationsToEntityClassMetadata(
                    $classAnnotations,
                    $reflectionClass,
                    $parent
                );

                break;

            case isset($classAnnotations[Annotation\MappedSuperclass::class]):
                return $this->convertClassAnnotationsToMappedSuperClassMetadata(
                    $classAnnotations,
                    $reflectionClass,
                    $parent
                );
            case isset($classAnnotations[Annotation\Embeddable::class]):
                return $this->convertClassAnnotationsToEntityClassMetadata(
                    $classAnnotations,
                    $reflectionClass,
                    $parent
                );
            default:
                throw Mapping\MappingException::classIsNotAValidEntityOrMappedSuperClass($reflectionClass->getName());
        }
    }
980053
private function convertPropertyAnnotationsToProperty(
        array $propertyAnnotations,
        ReflectionProperty $reflectionProperty,
        Mapping\ClassMetadata $metadata
    ) : ?Mapping\Property {
        switch (true) {
            case isset($propertyAnnotations[Annotation\Column::class]):
                return $this->convertReflectionPropertyToFieldMetadata(
                    $reflectionProperty,
                    $propertyAnnotations,
                    $metadata
                );
            case isset($propertyAnnotations[Annotation\OneToOne::class]):
                return $this->convertReflectionPropertyToOneToOneAssociationMetadata(
                    $reflectionProperty,
                    $propertyAnnotations,
                    $metadata
                );
            case isset($propertyAnnotations[Annotation\ManyToOne::class]):
                return $this->convertReflectionPropertyToManyToOneAssociationMetadata(
                    $reflectionProperty,
                    $propertyAnnotations,
                    $metadata
                );
            case isset($propertyAnnotations[Annotation\OneToMany::class]):
                return $this->convertReflectionPropertyToOneToManyAssociationMetadata(
                    $reflectionProperty,
                    $propertyAnnotations,
                    $metadata
                );
            case isset($propertyAnnotations[Annotation\ManyToMany::class]):
                return $this->convertReflectionPropertyToManyToManyAssociationMetadata(
                    $reflectionProperty,
                    $propertyAnnotations,
                    $metadata
                );
            case isset($propertyAnnotations[Annotation\Embedded::class]):
                return null;
            default:
                return new Mapping\TransientMetadata($reflectionProperty->getName());
        }
    }
246527
private function propertyToEmbedMany(array $properties, AbstractField $annotation)
    {
        $embeds = [];
        foreach ($properties as $property) {
            $embeds[] = $this->propertyToEmbedOne($property, $annotation);
        }

        return $embeds;
    }
1313489
public function parser(string $className, $objectAnnotation = null, string $propertyName = "", string $methodName = "", $propertyValue = null)
    {
        $beanName = $objectAnnotation->getName();
        $beanName = empty($beanName) ? $className : $beanName;
        $scope    = Scope::SINGLETON;

        DefinitionCollector::collect($className, $objectAnnotation, $propertyName, $methodName, $propertyValue);

        return [$beanName, $scope, ""];
    }
74890
private function convertReflectionPropertyToManyToOneAssociationMetadata(
        ReflectionProperty $reflectionProperty,
        array $propertyAnnotations,
        Mapping\ClassMetadata $classMetadata
    ) {
        $className      = $classMetadata->getClassName();
        $fieldName      = $reflectionProperty->getName();
        $manyToOneAnnot = $propertyAnnotations[Annotation\ManyToOne::class];

        if ($manyToOneAnnot->targetEntity === null) {
            throw Mapping\MappingException::missingTargetEntity($fieldName);
        }

        $assocMetadata = new Mapping\ManyToOneAssociationMetadata($fieldName);
        $targetEntity  = $manyToOneAnnot->targetEntity;

        $assocMetadata->setSourceEntity($className);
        $assocMetadata->setTargetEntity($targetEntity);
        $assocMetadata->setCascade($this->getCascade($className, $fieldName, $manyToOneAnnot->cascade));
        $assocMetadata->setFetchMode($this->getFetchMode($className, $manyToOneAnnot->fetch));

        if (! empty($manyToOneAnnot->inversedBy)) {
            $assocMetadata->setInversedBy($manyToOneAnnot->inversedBy);
        }

        // Check for Id
        if (isset($propertyAnnotations[Annotation\Id::class])) {
            $assocMetadata->setPrimaryKey(true);
        }

        // Check for Cache
        if (isset($propertyAnnotations[Annotation\Cache::class])) {
            $cacheAnnot    = $propertyAnnotations[Annotation\Cache::class];
            $cacheMetadata = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $classMetadata, $fieldName);

            $assocMetadata->setCache($cacheMetadata);
        }

        // Check for JoinColumn/JoinColumns annotations
        switch (true) {
            case isset($propertyAnnotations[Annotation\JoinColumn::class]):
                $joinColumnAnnot = $propertyAnnotations[Annotation\JoinColumn::class];
                $joinColumn      = $this->convertJoinColumnAnnotationToJoinColumnMetadata(
                    $reflectionProperty,
                    $joinColumnAnnot,
                    $classMetadata
                );

                $assocMetadata->addJoinColumn($joinColumn);

                break;

            case isset($propertyAnnotations[Annotation\JoinColumns::class]):
                $joinColumnsAnnot = $propertyAnnotations[Annotation\JoinColumns::class];

                foreach ($joinColumnsAnnot->value as $joinColumnAnnot) {
                    $joinColumn = $this->convertJoinColumnAnnotationToJoinColumnMetadata(
                        $reflectionProperty,
                        $joinColumnAnnot,
                        $classMetadata
                    );

                    $assocMetadata->addJoinColumn($joinColumn);
                }

                break;
        }

        return $assocMetadata;
    }
801913
public function collectAnnotationMetadata($classes, $metadataCollection)
    {
        // Load annotation and parse classes
        new Injectable();
        new InjectArgument(['var' => 'type']);
        new Service(['value' => '']);

        $reader = new AnnotationReader();
        $resolver = new AnnotationResolver(
            new AnnotationClassResolver($reader),
            new AnnotationPropertyResolver($reader),
            new AnnotationMethodResolver($reader)
        );

        $annotationCollector = new AnnotationMetadataCollector($resolver);

        // Rewrite collection by entity name
        return $annotationCollector->collect($classes, $metadataCollection);
    }
332783
private function getLabels($annotations)
    {
        $it = $this->getAnnotations()->filter($annotations)
            ->is(Label::class)
            ->onProperties();
        $labels = [];
        foreach ($it as $annotation) {
            $labels[$annotation->getPropertyName()] = $annotation->value;
        }
        return $labels;
    }
431228
public static function propertyAnnotations($class, $propertyName)
    {
        $class = is_object($class) ? get_class($class) : $class;
        if (!class_exists('ReflectionProperty')) {
            throw new \RuntimeException("Property type of " . $class . "::{$propertyName} cannot be resolved");
        }

        if ($annotations =& self::$propertiesType[$class][$propertyName]) {
            return $annotations;
        }

        if (!($refl =& self::$propertiesRefl[$class][$propertyName])) {
            $getter = self::getter($class, $propertyName);
            $refl = new \ReflectionMethod($class, $getter);
            self::$propertiesRefl[$class][$propertyName] = $refl;
        }

        // todo: smarter regexp
        if (!preg_match_all(
            '~\@([^\s@\(]+)[\t ]*(?:\(?([^\n@]+)\)?)?~i',
            $refl->getDocComment(),
            $annots,
            PREG_PATTERN_ORDER)
        ) {
            return null;
        }
        foreach ($annots[1] as $i => $annot) {
            $annotations[strtolower($annot)] = empty($annots[2][$i]) ? TRUE : rtrim($annots[2][$i], " \t\n\r)");
        }

        return $annotations;
    }
507414
private function convertReflectionPropertyToFieldMetadata(
        ReflectionProperty $reflProperty,
        array $propertyAnnotations,
        Mapping\ClassMetadata $metadata
    ) : Mapping\FieldMetadata {
        $className   = $metadata->getClassName();
        $fieldName   = $reflProperty->getName();
        $isVersioned = isset($propertyAnnotations[Annotation\Version::class]);
        $columnAnnot = $propertyAnnotations[Annotation\Column::class];

        if ($columnAnnot->type === null) {
            throw Mapping\MappingException::propertyTypeIsRequired($className, $fieldName);
        }

        $fieldMetadata = $this->convertColumnAnnotationToFieldMetadata($columnAnnot, $fieldName, $isVersioned);

        // Check for Id
        if (isset($propertyAnnotations[Annotation\Id::class])) {
            $fieldMetadata->setPrimaryKey(true);
        }

        // Check for GeneratedValue strategy
        if (isset($propertyAnnotations[Annotation\GeneratedValue::class])) {
            $generatedValueAnnot = $propertyAnnotations[Annotation\GeneratedValue::class];
            $strategy            = strtoupper($generatedValueAnnot->strategy);
            $idGeneratorType     = constant(sprintf('%s::%s', Mapping\GeneratorType::class, $strategy));

            if ($idGeneratorType !== Mapping\GeneratorType::NONE) {
                $idGeneratorDefinition = [];

                // Check for CustomGenerator/SequenceGenerator/TableGenerator definition
                switch (true) {
                    case isset($propertyAnnotations[Annotation\SequenceGenerator::class]):
                        $seqGeneratorAnnot = $propertyAnnotations[Annotation\SequenceGenerator::class];

                        $idGeneratorDefinition = [
                            'sequenceName' => $seqGeneratorAnnot->sequenceName,
                            'allocationSize' => $seqGeneratorAnnot->allocationSize,
                        ];

                        break;

                    case isset($propertyAnnotations[Annotation\CustomIdGenerator::class]):
                        $customGeneratorAnnot = $propertyAnnotations[Annotation\CustomIdGenerator::class];

                        $idGeneratorDefinition = [
                            'class' => $customGeneratorAnnot->class,
                            'arguments' => $customGeneratorAnnot->arguments,
                        ];

                        break;

                    /** @todo If it is not supported, why does this exist? */
                    case isset($propertyAnnotations['Doctrine\ORM\Mapping\TableGenerator']):
                        throw Mapping\MappingException::tableIdGeneratorNotImplemented($className);
                }

                $fieldMetadata->setValueGenerator(new Mapping\ValueGeneratorMetadata($idGeneratorType, $idGeneratorDefinition));
            }
        }

        return $fieldMetadata;
    }
201755
public static function parseByOneToMany($model, $propertyName, $annotation)
    {
        $className = BeanFactory::getObjectClass($model);

        $struct = new OneToMany($className, $propertyName, $annotation);
        $leftField = $struct->getLeftField();
        $rightField = $struct->getRightField();
        $rightModel = $struct->getRightModel();

        $rightModel::deleteBatch(function(IQuery $query) use($model, $leftField, $rightField){
            $query->where($rightField, '=', $model->$leftField);
        });
    }
274295
public static function init($model, $propertyName, $annotation, $forceInit = false)
    {
        $className = BeanFactory::getObjectClass($model);

        if(!$forceInit)
        {
            $autoSelect = AnnotationManager::getPropertyAnnotations($className, $propertyName, AutoSelect::class)[0] ?? null;
            if($autoSelect && !$autoSelect->status)
            {
                return;
            }
        }

        if(is_array($annotation))
        {
            $firstAnnotation = reset($annotation);
            if($firstAnnotation instanceof \Imi\Model\Annotation\Relation\PolymorphicToOne)
            {
                static::initByPolymorphicToOne($model, $propertyName, $annotation);
            }
        }
        else if($annotation instanceof \Imi\Model\Annotation\Relation\OneToOne)
        {
            static::initByOneToOne($model, $propertyName, $annotation);
        }
        else if($annotation instanceof \Imi\Model\Annotation\Relation\OneToMany)
        {
            static::initByOneToMany($model, $propertyName, $annotation);
        }
        else if($annotation instanceof \Imi\Model\Annotation\Relation\ManyToMany)
        {
            static::initByManyToMany($model, $propertyName, $annotation);
        }
        else if($annotation instanceof \Imi\Model\Annotation\Relation\PolymorphicOneToOne)
        {
            static::initByPolymorphicOneToOne($model, $propertyName, $annotation);
        }
        else if($annotation instanceof \Imi\Model\Annotation\Relation\PolymorphicOneToMany)
        {
            static::initByPolymorphicOneToMany($model, $propertyName, $annotation);
        }
        else if($annotation instanceof \Imi\Model\Annotation\Relation\PolymorphicManyToMany)
        {
            static::initByPolymorphicManyToMany($model, $propertyName, $annotation);
        }
        else if($annotation instanceof \Imi\Model\Annotation\Relation\PolymorphicToMany)
        {
            static::initByPolymorphicToMany($model, $propertyName, $annotation);
        }
    }
281520
public function formatMappingsWithRelations($mappings, $associationMappings)
    {
        $formattedMappings = $this->formatMappings($mappings);

        return $this->formatRelationMappings($formattedMappings, $associationMappings);
    }
953018
private function parsePropertyAnnotations(array $propertyAnnotations, string $className, string $propertyName, $propertyValue)
    {
        $isRef = false;
        $injectProperty = "";

        // 没有任何注解
        if (empty($propertyAnnotations) || !isset($propertyAnnotations[$propertyName])
            || !$this->isParseProperty($propertyAnnotations[$propertyName])
        ) {
            return [null, false];
        }

        // 属性注解解析
        foreach ($propertyAnnotations[$propertyName] as $propertyAnnotation) {
            $annotationClass = get_class($propertyAnnotation);
            if (!in_array($annotationClass, $this->getPropertyAnnotations())) {
                continue;
            }

            // 解析器
            $annotationParser = $this->getAnnotationParser($propertyAnnotation);
            if ($annotationParser === null) {
                $injectProperty = null;
                $isRef = false;
                continue;
            }
            list($injectProperty, $isRef) = $annotationParser->parser($className, $propertyAnnotation, $propertyName, "", $propertyValue);
        }

        return [$injectProperty, $isRef];
    }
651827
protected function indexAnnotationsByType(array $annotations)
    {
        $indexed = array();

        // if we are receiving annotations indexed by number, transform it to by class name
        if ($annotations && is_numeric(key($annotations))) {
            foreach ($annotations as $annotation) {
                $annotationType = get_class($annotation);

                if (isset($indexed[$annotationType]) && !is_array($indexed[$annotationType])) {
                    $indexed[$annotationType] = array($indexed[$annotationType], $annotation);
                } elseif (isset($indexed[$annotationType])) {
                    $indexed[$annotationType][] = $annotation;
                } else {
                    $indexed[$annotationType] = $annotation;
                }
            }
        }

        return $indexed;
    }
656563
public static function initRelations($model, $propertyName)
    {
        $className = BeanFactory::getObjectClass($model);
        $annotation = AnnotationManager::getPropertyAnnotations($className, $propertyName, RelationBase::class)[0] ?? null;
        if(null !== $annotation)
        {
            if($annotation instanceof \Imi\Model\Annotation\Relation\OneToOne)
            {
                $model->$propertyName = (ClassObject::parseSameLevelClassName($annotation->model, $className) . '::newInstance')();
            }
            else if($annotation instanceof \Imi\Model\Annotation\Relation\OneToMany)
            {
                $model->$propertyName = new ArrayList(ClassObject::parseSameLevelClassName($annotation->model, $className));
            }
            else if($annotation instanceof \Imi\Model\Annotation\Relation\ManyToMany)
            {
                $model->$propertyName = new ArrayList(ClassObject::parseSameLevelClassName($annotation->middle, $className));
            }
            else if($annotation instanceof \Imi\Model\Annotation\Relation\PolymorphicOneToOne)
            {
                $model->$propertyName = (ClassObject::parseSameLevelClassName($annotation->model, $className) . '::newInstance')();
            }
            else if($annotation instanceof \Imi\Model\Annotation\Relation\PolymorphicOneToMany)
            {
                $model->$propertyName = new ArrayList(ClassObject::parseSameLevelClassName($annotation->model, $className));
            }
            else if($annotation instanceof \Imi\Model\Annotation\Relation\PolymorphicManyToMany)
            {
                $model->$propertyName = new ArrayList(ClassObject::parseSameLevelClassName($annotation->middle, $className));
            }
            else
            {
                return;
            }
        }
    }
639368
public function loadAnnotations($annotations)
    {
        $moreSettings = parse_ini_string($annotations, true) ?: [];

        if (@$guardedFields = $moreSettings['blacklist']) {
            $this->setBlacklist(explode(',', $guardedFields));
        } // if

        if (@$moreSettings['casting'] && is_array($moreSettings['casting'])) {
            $this->setCastedFields($moreSettings['casting']);
        } // if

        if (@$modelName = $moreSettings['model']) {
            $this->setModelName($modelName);
        } // if

        $this->isPivotTable(@ (bool)$moreSettings['isPivot']);
        $this->withoutTimestamps(@ (bool)$moreSettings['withoutTimestamps']);

        return !@$moreSettings['ignore'];
    }