Parses a dynamic attribute
@param string$propertyName
@param \ReflectionProperty $reflectionProperty
@param \Omocha\AnnotationBag $propertyAnnotations
519583
protected function parseDynamicAttribute($propertyName, \ReflectionProperty $reflectionProperty, AnnotationBag $propertyAnnotations) {
		if ($propertyAnnotations->has('Query'))
			$attribute = new Query($propertyName, $reflectionProperty, $propertyAnnotations);
		elseif ($propertyAnnotations->has('Statement'))
			$attribute = new Statement($propertyName, $reflectionProperty, $propertyAnnotations);
		elseif ($propertyAnnotations->has('Procedure'))
			$attribute = new Procedure($propertyName, $reflectionProperty, $propertyAnnotations);
		elseif ($propertyAnnotations->has('Eval'))
			$attribute = new Macro($propertyName, $reflectionProperty, $propertyAnnotations);
		
		if ($attribute->isCacheable())
			$this->attributes[$propertyName] = $attribute;
		else
			$this->dynamicAttributes[$propertyName] = $attribute;
	}
830758
public static function collect(string $className, $objectAnnotation = null, string $propertyName = '', string $methodName = '', $propertyValue = null)
    {
        // collect service
        if ($objectAnnotation instanceof Service) {

            $rc = new \ReflectionClass($className);
            $interfaces = $rc->getInterfaceNames();
            $methods = $rc->getMethods(\ReflectionMethod::IS_PUBLIC);
            $version = $objectAnnotation->getVersion();

            foreach ($interfaces as $interfaceClass){
                foreach ($methods as $method){
                    $methodName = $method->getName();
                    self::$serviceMapping[$interfaceClass][$version][$methodName] = [$className, $methodName];
                }
            }

            return;
        }
    }
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, ""];
    }
271331
public static function parseByManyToMany($model, $propertyName, $annotation)
    {
        $className = BeanFactory::getObjectClass($model);

        $struct = new ManyToMany($className, $propertyName, $annotation);
        $middleModel = $struct->getMiddleModel();
        $middleLeftField = $struct->getMiddleLeftField();
        $leftField = $struct->getLeftField();

        $middleModel::deleteBatch(function(IQuery $query) use($model, $leftField, $middleLeftField){
            $query->where($middleLeftField, '=', $model->$leftField);
        });
    }
59330
protected static function readPropertyAnnotations(\ReflectionProperty &$property, AnnotationReader &$reader)
	{
		$dataMap = [];
		$strategyMap = [];
		$validatorsMap = [];
		foreach ($reader->getPropertyAnnotations($property) as $annotation)
		{
			switch (true)
			{
				case ($annotation instanceof DT\Annotation\Data):
					$dataMap[$annotation->subset] = $annotation;
					break;
				case ($annotation instanceof DT\Annotation\Strategy):
					$strategyMap[$annotation->subset] = $annotation;
					break;
				case ($annotation instanceof DT\Annotation\Validator):
					$validatorsMap[$annotation->subset][] = $annotation;
					break;
			}
		}
		foreach ($dataMap as $subset => $data)
		{
			$strategy = isset($strategyMap[$subset])? $strategyMap[$subset] : null;
			$validators = isset($validatorsMap[$subset])? $validatorsMap[$subset] : [];
			yield $subset => [$data, $strategy, $validators];
		}
	}
1216465
public static function parseByPolymorphicManyToMany($model, $propertyName, $annotation)
    {
        $className = BeanFactory::getObjectClass($model);

        $struct = new PolymorphicManyToMany($className, $propertyName, $annotation);
        $middleModel = $struct->getMiddleModel();
        $middleLeftField = $struct->getMiddleLeftField();
        $leftField = $struct->getLeftField();

        foreach($model->$propertyName as $index => $row)
        {
            if(!$row instanceof $middleModel)
            {
                $row = $middleModel::newInstance($row);
                $model->$propertyName[$index] = $row;
            }
            $row[$middleLeftField] = $model->$leftField;
            $row[$annotation->type] = $annotation->typeValue;
            $row->insert();
        }
    }
536769
public static function collect(
        string $className,
        $objectAnnotation = null,
        string $propertyName = '',
        string $methodName = '',
        $propertyValue = null
    ) {
        if ($objectAnnotation instanceof Command) {
            self::collectCommand($className, $objectAnnotation);
        } elseif ($objectAnnotation instanceof Mapping) {
            self::collectMapping($className, $objectAnnotation, $methodName);
        } elseif ($objectAnnotation === null && isset(self::$commandMapping[$className])) {
            self::collectWithoutAnnotation($className, $methodName);
        }
    }
44613
protected static function createProperty(\ReflectionProperty $reflectionProperty)
    {
        $property = new Property();
        $property->setName($reflectionProperty->getName());
        
        $annotationReader = new AnnotationReader();
        $annotations = $annotationReader->getPropertyAnnotations($reflectionProperty);
        foreach ($annotations as $annotation) {
            $annotationClassName = get_class($annotation);
            if ($annotationClassName == 'Doctrine\ORM\Mapping\Column' || $annotationClassName == 'Doctrine\ORM\Mapping\Id') {
                $property->setAnnotation($annotation);
                $property->setType(Property::PROPERTY_TYPE_COLUMN);
            } elseif ($annotationClassName == 'Doctrine\ORM\Mapping\ManyToOne' || $annotationClassName == 'Doctrine\ORM\Mapping\OneToOne') {
                $property->setAnnotation($annotation);
                $property->setType(Property::PROPERTY_TYPE_REF_ONE);
                $property->setTargetEntity($annotation->targetEntity);
            } elseif ($annotationClassName == 'Doctrine\ORM\Mapping\ManyToMany' || $annotationClassName == 'Doctrine\ORM\Mapping\OneToMany') {
                $property->setAnnotation($annotation);
                $property->setType(Property::PROPERTY_TYPE_REF_MANY);
                $property->setTargetEntity($annotation->targetEntity);
            }
        }
        
        if ($property->getType() == -1) {
            throw new \Exception(
                'Entity "' . $reflectionProperty->getDeclaringClass()->getName()
                . '": defining annotation is missing at property "' . $property->getName() . '"!'
            );
        }
        
        return $property;
    }
1037741
public function updateProperty(\ReflectionProperty $reflectionProperty, Schema $property)
    {
        $annotations = $this->annotationsReader->getPropertyAnnotations($reflectionProperty);

        foreach ($annotations as $annotation) {
            if ($annotation instanceof Assert\NotBlank || $annotation instanceof Assert\NotNull) {
                // The field is required
                if (null === $this->schema) {
                    continue;
                }

                $propertyName = $this->getSchemaPropertyName($property);
                if (null === $propertyName) {
                    continue;
                }

                $existingRequiredFields = $this->schema->getRequired() ?? [];
                $existingRequiredFields[] = $propertyName;

                $this->schema->setRequired(array_values(array_unique($existingRequiredFields)));
            } elseif ($annotation instanceof Assert\Length) {
                $property->setMinLength($annotation->min);
                $property->setMaxLength($annotation->max);
            } elseif ($annotation instanceof Assert\Regex) {
                $this->appendPattern($property, $annotation->getHtmlPattern());
            } elseif ($annotation instanceof Assert\Count) {
                $property->setMinItems($annotation->min);
                $property->setMaxItems($annotation->max);
            } elseif ($annotation instanceof Assert\Choice) {
                $property->setEnum($annotation->callback ? call_user_func(is_array($annotation->callback) ? $annotation->callback : [$reflectionProperty->class, $annotation->callback]) : $annotation->choices);
            } elseif ($annotation instanceof Assert\Expression) {
                $this->appendPattern($property, $annotation->message);
            } elseif ($annotation instanceof Assert\Range) {
                $property->setMinimum($annotation->min);
                $property->setMaximum($annotation->max);
            } elseif ($annotation instanceof Assert\LessThan) {
                $property->setExclusiveMaximum($annotation->value);
            } elseif ($annotation instanceof Assert\LessThanOrEqual) {
                $property->setMaximum($annotation->value);
            }
        }
    }
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;
    }
813250
public static function initByOneToMany($model, $propertyName, $annotation)
    {
        $className = BeanFactory::getObjectClass($model);

        if(class_exists($annotation->model))
        {
            $modelClass = $annotation->model;
        }
        else
        {
            $modelClass = Imi::getClassNamespace($className) . '\\' . $annotation->model;
        }

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

        $model->$propertyName = new ArrayList($modelClass);
        if(null !== $model->$leftField)
        {
            $list = $modelClass::query()->where($rightField, '=', $model->$leftField)->select()->getArray();
            if(null !== $list)
            {
                $model->$propertyName->append(...$list);
            }
        }

    }
434234
protected function getProperty(\ReflectionClass $reflection, $propertyName): ReflectionProperty
    {
        $property = $reflection->getProperty($propertyName);
        $property->setAccessible(true);
        return $property;
    }
1346955
private function convertReflectionPropertyToOneToManyAssociationMetadata(
        ReflectionProperty $reflectionProperty,
        array $propertyAnnotations,
        Mapping\ClassMetadata $classMetadata
    ) {
        $className      = $classMetadata->getClassName();
        $fieldName      = $reflectionProperty->getName();
        $oneToManyAnnot = $propertyAnnotations[Annotation\OneToMany::class];

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

        $assocMetadata = new Mapping\OneToManyAssociationMetadata($fieldName);
        $targetEntity  = $oneToManyAnnot->targetEntity;

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

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

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

        // 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;
    }
738980
private function convertReflectionPropertyAnnotationsToProperty(
        ReflectionProperty $reflectionProperty,
        array $propertyAnnotations,
        Mapping\ClassMetadata $classMetadata
    ) {
        // Field can only be annotated with one of:
        // @Column, @OneToOne, @OneToMany, @ManyToOne, @ManyToMany, @Embedded
        switch (true) {
            case isset($propertyAnnotations[Annotation\Column::class]):
                return $this->convertReflectionPropertyToFieldMetadata(
                    $reflectionProperty,
                    $propertyAnnotations,
                    $classMetadata
                );
            case isset($propertyAnnotations[Annotation\OneToOne::class]):
                return $this->convertReflectionPropertyToOneToOneAssociationMetadata(
                    $reflectionProperty,
                    $propertyAnnotations,
                    $classMetadata
                );
            case isset($propertyAnnotations[Annotation\ManyToOne::class]):
                return $this->convertReflectionPropertyToManyToOneAssociationMetadata(
                    $reflectionProperty,
                    $propertyAnnotations,
                    $classMetadata
                );
            case isset($propertyAnnotations[Annotation\OneToMany::class]):
                return $this->convertReflectionPropertyToOneToManyAssociationMetadata(
                    $reflectionProperty,
                    $propertyAnnotations,
                    $classMetadata
                );
            case isset($propertyAnnotations[Annotation\ManyToMany::class]):
                return $this->convertReflectionPropertyToManyToManyAssociationMetadata(
                    $reflectionProperty,
                    $propertyAnnotations,
                    $classMetadata
                );
            case isset($propertyAnnotations[Annotation\Embedded::class]):
                // @todo guilhermeblanco Implement later... =)
                break;

            default:
                return new Mapping\TransientMetadata($reflectionProperty->getName());
        }
    }
961221
public static function initByManyToMany($model, $propertyName, $annotation)
    {
        $className = BeanFactory::getObjectClass($model);

        if(class_exists($annotation->model))
        {
            $modelClass = $annotation->model;
        }
        else
        {
            $modelClass = Imi::getClassNamespace($className) . '\\' . $annotation->model;
        }

        $struct = new ManyToMany($className, $propertyName, $annotation);
        $leftField = $struct->getLeftField();
        $rightField = $struct->getRightField();
        $middleTable = ModelManager::getTable($struct->getMiddleModel());
        $rightTable = ModelManager::getTable($struct->getRightModel());

        static::parseManyToManyQueryFields($struct->getMiddleModel(), $struct->getRightModel(), $middleFields, $rightFields);
        $fields = static::mergeManyToManyFields($middleTable, $middleFields, $rightTable, $rightFields);
        
        $model->$propertyName = new ArrayList($struct->getMiddleModel());
        $model->{$annotation->rightMany} = new ArrayList($struct->getRightModel());
        
        if(null !== $model->$leftField)
        {
            $list = Db::query(ModelManager::getDbPoolName($className))
                        ->table($rightTable)
                        ->field(...$fields)
                        ->join($middleTable, $middleTable . '.' . $struct->getMiddleRightField(), '=', $rightTable . '.' . $rightField)
                        ->where($middleTable . '.' . $struct->getMiddleLeftField(), '=', $model->$leftField)
                        ->select()
                        ->getArray();
            if(null !== $list)
            {
                // 关联数据
                static::appendMany($model->$propertyName, $list, $middleFields, $struct->getMiddleModel());

                // 右侧表数据
                static::appendMany($model->{$annotation->rightMany}, $list, $rightFields, $struct->getRightModel());

            }
        }
    }
963001
public static function initByOneToOne($model, $propertyName, $annotation)
    {
        $className = BeanFactory::getObjectClass($model);

        if(class_exists($annotation->model))
        {
            $modelClass = $annotation->model;
        }
        else
        {
            $modelClass = Imi::getClassNamespace($className) . '\\' . $annotation->model;
        }

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

        if(null === $model->$leftField)
        {
            $rightModel = $modelClass::newInstance();
        }
        else
        {
            $rightModel = $modelClass::query()->where($rightField, '=', $model->$leftField)->select()->get();
            if(null === $rightModel)
            {
                $rightModel = $modelClass::newInstance();
            }
        }

        $model->$propertyName = $rightModel;
    }
549978
private function getPropertyAnnotationData(\ReflectionProperty $property)
    {
        $result = $this->reader->getPropertyAnnotation($property, self::PROPERTY_ANNOTATION);

        if ($result !== null && $result->name === null) {
            $result->name = Caser::snake($property->getName());
        }

        return $result;
    }