/**
  * @param Mapping $mapping
  * @param string $className
  * @param string $propertyName
  *
  * @throws \Flowpack\ElasticSearch\Exception
  * @return void
  */
 protected function augmentMappingByProperty(Mapping $mapping, $className, $propertyName)
 {
     list($propertyType) = $this->reflectionService->getPropertyTagValues($className, $propertyName, 'var');
     if (($transformAnnotation = $this->reflectionService->getPropertyAnnotation($className, $propertyName, 'Flowpack\\ElasticSearch\\Annotations\\Transform')) !== NULL) {
         $mappingType = $this->transformerFactory->create($transformAnnotation->type)->getTargetMappingType();
     } elseif (\TYPO3\Flow\Utility\TypeHandling::isSimpleType($propertyType)) {
         $mappingType = $propertyType;
     } elseif ($propertyType === '\\DateTime') {
         $mappingType = 'date';
     } else {
         throw new \Flowpack\ElasticSearch\Exception('Mapping is only supported for simple types and DateTime objects; "' . $propertyType . '" given but without a Transform directive.');
     }
     $mapping->setPropertyByPath($propertyName, array('type' => $mappingType));
     $annotation = $this->reflectionService->getPropertyAnnotation($className, $propertyName, 'Flowpack\\ElasticSearch\\Annotations\\Mapping');
     if ($annotation instanceof MappingAnnotation) {
         $mapping->setPropertyByPath($propertyName, $this->processMappingAnnotation($annotation, $mapping->getPropertyByPath($propertyName)));
         if ($annotation->getFields()) {
             foreach ($annotation->getFields() as $multiFieldAnnotation) {
                 $multiFieldIndexName = trim($multiFieldAnnotation->index_name);
                 if ($multiFieldIndexName === '') {
                     throw new \Flowpack\ElasticSearch\Exception('Multi field require an unique index name "' . $className . '::' . $propertyName . '".');
                 }
                 if (isset($multiFields[$multiFieldIndexName])) {
                     throw new \Flowpack\ElasticSearch\Exception('Duplicate index name in the same multi field is not allowed "' . $className . '::' . $propertyName . '".');
                 }
                 $multiFieldAnnotation->type = $mappingType;
                 $multiFields[$multiFieldIndexName] = $this->processMappingAnnotation($multiFieldAnnotation);
             }
             $mapping->setPropertyByPath(array($propertyName, 'fields'), $multiFields);
         }
     }
 }
 /**
  * @param string $className
  * @return string
  */
 protected function createTableDefinitionQueryForClassName($className)
 {
     /** @var Table $table */
     $table = $this->reflectionService->getClassAnnotation($className, Table::class);
     $columns = $this->reflectionService->getPropertyNamesByAnnotation($className, Column::class);
     $columnDefinitions = [];
     foreach ($columns as $columnName) {
         /** @var Column $column */
         $column = $this->reflectionService->getPropertyAnnotation($className, $columnName, Column::class);
         $columnDefinitions[] = '`' . $columnName . '` ' . $column->definition;
     }
     if ($table->indexes !== NULL) {
         $columnDefinitions[] = $table->indexes;
     }
     return 'CREATE TABLE ' . $table->name . ' (' . implode(', ', $columnDefinitions) . ') ENGINE = InnoDB';
 }
 /**
  * @param string $model
  * @param object $context an arbitrary object which is available in all actions and nested functionality
  * @return array
  * @throws MissingModelTypeException
  * @throws \Exception
  */
 protected function getProperties($model, $context = null)
 {
     if ($model === NULL) {
         throw new MissingModelTypeException('The "model" property has not been specified as parameter to the ViewHelper ' . get_class($this) . '.', 1452715128);
     }
     $classSchema = $this->reflectionService->getClassSchema($model);
     if ($classSchema === NULL) {
         throw new MissingModelTypeException('No class schema could be resolved for model ' . $model . '.', 1452715183);
     }
     $classSchema->getProperties();
     $fields = [];
     foreach ($classSchema->getProperties() as $propertyName => $propertyDefinition) {
         if ($propertyName === 'Persistence_Object_Identifier') {
             continue;
         }
         /* @var $formFieldAnnotation FormField */
         $formFieldAnnotation = $this->reflectionService->getPropertyAnnotation($model, $propertyName, FormField::class);
         $fields[$propertyName] = [];
         if ($formFieldAnnotation) {
             $fields[$propertyName] = get_object_vars($formFieldAnnotation);
         }
         $this->addDefaultsToFields($fields, $propertyName);
     }
     foreach (get_class_methods($model) as $methodName) {
         if (substr($methodName, 0, 3) === 'get') {
             $methodAnnotation = $this->reflectionService->getMethodAnnotation($model, $methodName, FormField::class);
             if ($methodAnnotation) {
                 $propertyName = lcfirst(substr($methodName, 3));
                 $fields[$propertyName] = get_object_vars($methodAnnotation);
                 $this->addDefaultsToFields($fields, $propertyName);
             }
         }
     }
     $generatorAnnotation = $this->reflectionService->getClassAnnotation($model, FieldGenerator::class);
     if ($generatorAnnotation !== NULL) {
         $generator = $this->objectManager->get($generatorAnnotation->className);
         if (!$generator instanceof FieldGeneratorInterface) {
             throw new \Exception('TODO: generator must implement FieldGeneratorInterface, ' . get_class($generator) . ' given.');
         }
         foreach ($generator->generate($context) as $propertyName => $annotation) {
             $fields[$propertyName] = get_object_vars($annotation);
             $this->addDefaultsToFields($fields, $propertyName);
         }
     }
     return (new PositionalArraySorter($fields))->toArray();
 }
 /**
  * Returns a multidimensional array with the indexable, probably transformed values of an object
  *
  * @param $object
  *
  * @return array
  */
 protected function getIndexablePropertiesAndValuesFromObject($object)
 {
     $className = $this->reflectionService->getClassNameByObject($object);
     $data = array();
     foreach ($this->indexInformer->getClassProperties($className) as $propertyName) {
         $value = \TYPO3\Flow\Reflection\ObjectAccess::getProperty($object, $propertyName);
         if (($transformAnnotation = $this->reflectionService->getPropertyAnnotation($className, $propertyName, 'Flowpack\\ElasticSearch\\Annotations\\Transform')) !== null) {
             $value = $this->transformerFactory->create($transformAnnotation->type)->transformByAnnotation($value, $transformAnnotation);
         }
         $data[$propertyName] = $value;
     }
     return $data;
 }
 /**
  * Builds code which injects an object which was specified by its object name
  *
  * @param Configuration $objectConfiguration Configuration of the object to inject into
  * @param $propertyName
  * @param $propertyObjectName
  * @param string $propertyName Name of the property to inject
  * @param string $propertyObjectName Object name of the object to inject
  * @return array PHP code
  * @throws \TYPO3\Flow\Object\Exception\UnknownObjectException
  */
 public function buildPropertyInjectionCodeByString(Configuration $objectConfiguration, $propertyName, $propertyObjectName)
 {
     $className = $objectConfiguration->getClassName();
     $injectAnnotation = $this->reflectionService->getPropertyAnnotation($className, $propertyName, 'TYPO3\\Flow\\Annotations\\Inject');
     if (strpos($propertyObjectName, '.') !== FALSE) {
         $settingPath = explode('.', $propertyObjectName);
         $settings = Arrays::getValueByPath($this->configurationManager->getConfiguration(ConfigurationManager::CONFIGURATION_TYPE_SETTINGS), array_shift($settingPath));
         $propertyObjectName = Arrays::getValueByPath($settings, $settingPath);
     }
     if ($injectAnnotation !== NULL && $injectAnnotation->setting !== NULL) {
         if ($injectAnnotation->package === NULL) {
             $settingPath = $objectConfiguration->getPackageKey();
         } else {
             $settingPath = $injectAnnotation->package;
         }
         $settingPath .= '.' . $injectAnnotation->setting;
         return array('$this->' . $propertyName . ' = \\TYPO3\\Flow\\Core\\Bootstrap::$staticObjectManager->get(\'TYPO3\\Flow\\Configuration\\ConfigurationManager\')->getConfiguration(\\TYPO3\\Flow\\Configuration\\ConfigurationManager::CONFIGURATION_TYPE_SETTINGS, \'' . $settingPath . '\');');
     }
     if (!isset($this->objectConfigurations[$propertyObjectName])) {
         $configurationSource = $objectConfiguration->getConfigurationSourceHint();
         if (!isset($propertyObjectName[0])) {
             throw new \TYPO3\Flow\Object\Exception\UnknownObjectException('Malformed DocComent block for a property in class "' . $className . '".', 1360171313);
         }
         if ($propertyObjectName[0] === '\\') {
             throw new \TYPO3\Flow\Object\Exception\UnknownObjectException('The object name "' . $propertyObjectName . '" which was specified as a property in the object configuration of object "' . $objectConfiguration->getObjectName() . '" (' . $configurationSource . ') starts with a leading backslash.', 1277827579);
         } else {
             throw new \TYPO3\Flow\Object\Exception\UnknownObjectException('The object "' . $propertyObjectName . '" which was specified as a property in the object configuration of object "' . $objectConfiguration->getObjectName() . '" (' . $configurationSource . ') does not exist. Check for spelling mistakes and if that dependency is correctly configured.', 1265213849);
         }
     }
     $propertyClassName = $this->objectConfigurations[$propertyObjectName]->getClassName();
     if ($this->objectConfigurations[$propertyObjectName]->getScope() === Configuration::SCOPE_PROTOTYPE) {
         $preparedSetterArgument = 'new \\' . $propertyClassName . '(' . $this->buildMethodParametersCode($this->objectConfigurations[$propertyObjectName]->getArguments()) . ')';
     } else {
         $preparedSetterArgument = '\\TYPO3\\Flow\\Core\\Bootstrap::$staticObjectManager->get(\'' . $propertyObjectName . '\')';
     }
     $result = $this->buildSetterInjectionCode($className, $propertyName, $preparedSetterArgument);
     if ($result !== NULL) {
         return $result;
     }
     if ($injectAnnotation->lazy === TRUE && $this->objectConfigurations[$propertyObjectName]->getScope() !== Configuration::SCOPE_PROTOTYPE) {
         return $this->buildLazyPropertyInjectionCode($propertyObjectName, $propertyClassName, $propertyName, $preparedSetterArgument);
     } else {
         return array('$this->' . $propertyName . ' = ' . $preparedSetterArgument . ';');
     }
 }
 /**
  * Creates and returns an aspect from the annotations found in a class which
  * is tagged as an aspect. The object acting as an advice will already be
  * fetched (and therefore instantiated if necessary).
  *
  * @param  string $aspectClassName Name of the class which forms the aspect, contains advices etc.
  * @return mixed The aspect container containing one or more advisors or FALSE if no container could be built
  * @throws \TYPO3\Flow\Aop\Exception
  */
 protected function buildAspectContainer($aspectClassName)
 {
     $aspectContainer = new \TYPO3\Flow\Aop\AspectContainer($aspectClassName);
     $methodNames = get_class_methods($aspectClassName);
     foreach ($methodNames as $methodName) {
         foreach ($this->reflectionService->getMethodAnnotations($aspectClassName, $methodName) as $annotation) {
             $annotationClass = get_class($annotation);
             switch ($annotationClass) {
                 case \TYPO3\Flow\Annotations\Around::class:
                     $pointcutFilterComposite = $this->pointcutExpressionParser->parse($annotation->pointcutExpression, $this->renderSourceHint($aspectClassName, $methodName, $annotationClass));
                     $advice = new \TYPO3\Flow\Aop\Advice\AroundAdvice($aspectClassName, $methodName);
                     $pointcut = new \TYPO3\Flow\Aop\Pointcut\Pointcut($annotation->pointcutExpression, $pointcutFilterComposite, $aspectClassName);
                     $advisor = new \TYPO3\Flow\Aop\Advisor($advice, $pointcut);
                     $aspectContainer->addAdvisor($advisor);
                     break;
                 case \TYPO3\Flow\Annotations\Before::class:
                     $pointcutFilterComposite = $this->pointcutExpressionParser->parse($annotation->pointcutExpression, $this->renderSourceHint($aspectClassName, $methodName, $annotationClass));
                     $advice = new \TYPO3\Flow\Aop\Advice\BeforeAdvice($aspectClassName, $methodName);
                     $pointcut = new \TYPO3\Flow\Aop\Pointcut\Pointcut($annotation->pointcutExpression, $pointcutFilterComposite, $aspectClassName);
                     $advisor = new \TYPO3\Flow\Aop\Advisor($advice, $pointcut);
                     $aspectContainer->addAdvisor($advisor);
                     break;
                 case \TYPO3\Flow\Annotations\AfterReturning::class:
                     $pointcutFilterComposite = $this->pointcutExpressionParser->parse($annotation->pointcutExpression, $this->renderSourceHint($aspectClassName, $methodName, $annotationClass));
                     $advice = new \TYPO3\Flow\Aop\Advice\AfterReturningAdvice($aspectClassName, $methodName);
                     $pointcut = new \TYPO3\Flow\Aop\Pointcut\Pointcut($annotation->pointcutExpression, $pointcutFilterComposite, $aspectClassName);
                     $advisor = new \TYPO3\Flow\Aop\Advisor($advice, $pointcut);
                     $aspectContainer->addAdvisor($advisor);
                     break;
                 case \TYPO3\Flow\Annotations\AfterThrowing::class:
                     $pointcutFilterComposite = $this->pointcutExpressionParser->parse($annotation->pointcutExpression, $this->renderSourceHint($aspectClassName, $methodName, $annotationClass));
                     $advice = new \TYPO3\Flow\Aop\Advice\AfterThrowingAdvice($aspectClassName, $methodName);
                     $pointcut = new \TYPO3\Flow\Aop\Pointcut\Pointcut($annotation->pointcutExpression, $pointcutFilterComposite, $aspectClassName);
                     $advisor = new \TYPO3\Flow\Aop\Advisor($advice, $pointcut);
                     $aspectContainer->addAdvisor($advisor);
                     break;
                 case \TYPO3\Flow\Annotations\After::class:
                     $pointcutFilterComposite = $this->pointcutExpressionParser->parse($annotation->pointcutExpression, $this->renderSourceHint($aspectClassName, $methodName, $annotationClass));
                     $advice = new \TYPO3\Flow\Aop\Advice\AfterAdvice($aspectClassName, $methodName);
                     $pointcut = new \TYPO3\Flow\Aop\Pointcut\Pointcut($annotation->pointcutExpression, $pointcutFilterComposite, $aspectClassName);
                     $advisor = new \TYPO3\Flow\Aop\Advisor($advice, $pointcut);
                     $aspectContainer->addAdvisor($advisor);
                     break;
                 case \TYPO3\Flow\Annotations\Pointcut::class:
                     $pointcutFilterComposite = $this->pointcutExpressionParser->parse($annotation->expression, $this->renderSourceHint($aspectClassName, $methodName, $annotationClass));
                     $pointcut = new \TYPO3\Flow\Aop\Pointcut\Pointcut($annotation->expression, $pointcutFilterComposite, $aspectClassName, $methodName);
                     $aspectContainer->addPointcut($pointcut);
                     break;
             }
         }
     }
     $introduceAnnotation = $this->reflectionService->getClassAnnotation($aspectClassName, \TYPO3\Flow\Annotations\Introduce::class);
     if ($introduceAnnotation !== null) {
         if ($introduceAnnotation->interfaceName === null && $introduceAnnotation->traitName === null) {
             throw new \TYPO3\Flow\Aop\Exception('The introduction in class "' . $aspectClassName . '" does neither contain an interface name nor a trait name, at least one is required.', 1172694761);
         }
         $pointcutFilterComposite = $this->pointcutExpressionParser->parse($introduceAnnotation->pointcutExpression, $this->renderSourceHint($aspectClassName, $introduceAnnotation->interfaceName, \TYPO3\Flow\Annotations\Introduce::class));
         $pointcut = new \TYPO3\Flow\Aop\Pointcut\Pointcut($introduceAnnotation->pointcutExpression, $pointcutFilterComposite, $aspectClassName);
         if ($introduceAnnotation->interfaceName !== null) {
             $introduction = new \TYPO3\Flow\Aop\InterfaceIntroduction($aspectClassName, $introduceAnnotation->interfaceName, $pointcut);
             $aspectContainer->addInterfaceIntroduction($introduction);
         }
         if ($introduceAnnotation->traitName !== null) {
             $introduction = new \TYPO3\Flow\Aop\TraitIntroduction($aspectClassName, $introduceAnnotation->traitName, $pointcut);
             $aspectContainer->addTraitIntroduction($introduction);
         }
     }
     foreach ($this->reflectionService->getClassPropertyNames($aspectClassName) as $propertyName) {
         $introduceAnnotation = $this->reflectionService->getPropertyAnnotation($aspectClassName, $propertyName, \TYPO3\Flow\Annotations\Introduce::class);
         if ($introduceAnnotation !== null) {
             $pointcutFilterComposite = $this->pointcutExpressionParser->parse($introduceAnnotation->pointcutExpression, $this->renderSourceHint($aspectClassName, $propertyName, \TYPO3\Flow\Annotations\Introduce::class));
             $pointcut = new \TYPO3\Flow\Aop\Pointcut\Pointcut($introduceAnnotation->pointcutExpression, $pointcutFilterComposite, $aspectClassName);
             $introduction = new PropertyIntroduction($aspectClassName, $propertyName, $pointcut);
             $aspectContainer->addPropertyIntroduction($introduction);
         }
     }
     if (count($aspectContainer->getAdvisors()) < 1 && count($aspectContainer->getPointcuts()) < 1 && count($aspectContainer->getInterfaceIntroductions()) < 1 && count($aspectContainer->getTraitIntroductions()) < 1 && count($aspectContainer->getPropertyIntroductions()) < 1) {
         throw new \TYPO3\Flow\Aop\Exception('The class "' . $aspectClassName . '" is tagged to be an aspect but doesn\'t contain advices nor pointcut or introduction declarations.', 1169124534);
     }
     return $aspectContainer;
 }
 /**
  * This function tries to find yet unmatched dependencies which need to be injected via "inject*" setter methods.
  *
  * @param array &$objectConfigurations
  * @return void
  * @throws \TYPO3\Flow\Object\Exception if an injected property is private
  */
 protected function autowireProperties(array &$objectConfigurations)
 {
     /** @var Configuration $objectConfiguration */
     foreach ($objectConfigurations as $objectConfiguration) {
         $className = $objectConfiguration->getClassName();
         $properties = $objectConfiguration->getProperties();
         if ($className === '') {
             continue;
         }
         $classMethodNames = get_class_methods($className);
         if (!is_array($classMethodNames)) {
             if (!class_exists($className)) {
                 throw new \TYPO3\Flow\Object\Exception\UnknownClassException(sprintf('The class "%s" defined in the object configuration for object "%s", defined in package: %s, does not exist.', $className, $objectConfiguration->getObjectName(), $objectConfiguration->getPackageKey()), 1352371371);
             } else {
                 throw new \TYPO3\Flow\Object\Exception\UnknownClassException(sprintf('Could not autowire properties of class "%s" because names of methods contained in that class could not be retrieved using get_class_methods().', $className), 1352386418);
             }
         }
         foreach ($classMethodNames as $methodName) {
             if (isset($methodName[6]) && strpos($methodName, 'inject') === 0 && $methodName[6] === strtoupper($methodName[6])) {
                 $propertyName = lcfirst(substr($methodName, 6));
                 $autowiringAnnotation = $this->reflectionService->getMethodAnnotation($className, $methodName, \TYPO3\Flow\Annotations\Autowiring::class);
                 if ($autowiringAnnotation !== NULL && $autowiringAnnotation->enabled === FALSE) {
                     continue;
                 }
                 if ($methodName === 'injectSettings') {
                     $packageKey = $objectConfiguration->getPackageKey();
                     if ($packageKey !== NULL) {
                         $properties[$propertyName] = new ConfigurationProperty($propertyName, array('type' => ConfigurationManager::CONFIGURATION_TYPE_SETTINGS, 'path' => $packageKey), ConfigurationProperty::PROPERTY_TYPES_CONFIGURATION);
                     }
                 } else {
                     if (array_key_exists($propertyName, $properties)) {
                         continue;
                     }
                     $methodParameters = $this->reflectionService->getMethodParameters($className, $methodName);
                     if (count($methodParameters) !== 1) {
                         $this->systemLogger->log(sprintf('Could not autowire property %s because %s() expects %s instead of exactly 1 parameter.', $className . '::' . $propertyName, $methodName, count($methodParameters) ?: 'none'), LOG_DEBUG);
                         continue;
                     }
                     $methodParameter = array_pop($methodParameters);
                     if ($methodParameter['class'] === NULL) {
                         $this->systemLogger->log(sprintf('Could not autowire property %s because the method parameter in %s() contained no class type hint.', $className . '::' . $propertyName, $methodName), LOG_DEBUG);
                         continue;
                     }
                     $properties[$propertyName] = new ConfigurationProperty($propertyName, $methodParameter['class'], ConfigurationProperty::PROPERTY_TYPES_OBJECT);
                 }
             }
         }
         foreach ($this->reflectionService->getPropertyNamesByAnnotation($className, \TYPO3\Flow\Annotations\Inject::class) as $propertyName) {
             if ($this->reflectionService->isPropertyPrivate($className, $propertyName)) {
                 throw new \TYPO3\Flow\Object\Exception(sprintf('The property "%%s" in class "%s" must not be private when annotated for injection.', $propertyName, $className), 1328109641);
             }
             if (!array_key_exists($propertyName, $properties)) {
                 /** @var Inject $injectAnnotation */
                 $injectAnnotation = $this->reflectionService->getPropertyAnnotation($className, $propertyName, \TYPO3\Flow\Annotations\Inject::class);
                 // TODO: Should be removed with 3.2. Inject settings by Inject-Annotation is deprecated since 3.0. Injecting settings by annotation should be done using the InjectConfiguration annotation
                 if ($injectAnnotation->setting !== NULL) {
                     $packageKey = $injectAnnotation->package !== NULL ? $injectAnnotation->package : $objectConfiguration->getPackageKey();
                     $configurationPath = rtrim($packageKey . '.' . $injectAnnotation->setting, '.');
                     $properties[$propertyName] = new ConfigurationProperty($propertyName, array('type' => ConfigurationManager::CONFIGURATION_TYPE_SETTINGS, 'path' => $configurationPath), ConfigurationProperty::PROPERTY_TYPES_CONFIGURATION);
                 } else {
                     $objectName = trim(implode('', $this->reflectionService->getPropertyTagValues($className, $propertyName, 'var')), ' \\');
                     $configurationProperty = new ConfigurationProperty($propertyName, $objectName, ConfigurationProperty::PROPERTY_TYPES_OBJECT, NULL, $injectAnnotation->lazy);
                     $properties[$propertyName] = $configurationProperty;
                 }
             }
         }
         foreach ($this->reflectionService->getPropertyNamesByAnnotation($className, \TYPO3\Flow\Annotations\InjectConfiguration::class) as $propertyName) {
             if ($this->reflectionService->isPropertyPrivate($className, $propertyName)) {
                 throw new \TYPO3\Flow\Object\Exception(sprintf('The property "%s" in class "%s" must not be private when annotated for configuration injection.', $propertyName, $className), 1416765599);
             }
             if (array_key_exists($propertyName, $properties)) {
                 continue;
             }
             /** @var InjectConfiguration $injectConfigurationAnnotation */
             $injectConfigurationAnnotation = $this->reflectionService->getPropertyAnnotation($className, $propertyName, \TYPO3\Flow\Annotations\InjectConfiguration::class);
             if ($injectConfigurationAnnotation->type === ConfigurationManager::CONFIGURATION_TYPE_SETTINGS) {
                 $packageKey = $injectConfigurationAnnotation->package !== NULL ? $injectConfigurationAnnotation->package : $objectConfiguration->getPackageKey();
                 $configurationPath = rtrim($packageKey . '.' . $injectConfigurationAnnotation->path, '.');
             } else {
                 if ($injectConfigurationAnnotation->package !== NULL) {
                     throw new \TYPO3\Flow\Object\Exception(sprintf('The InjectConfiguration annotation for property "%s" in class "%s" specifies a "package" key for configuration type "%s", but this is only supported for injection of "Settings".', $propertyName, $className, $injectConfigurationAnnotation->type), 1420811958);
                 }
                 $configurationPath = $injectConfigurationAnnotation->path;
             }
             $properties[$propertyName] = new ConfigurationProperty($propertyName, array('type' => $injectConfigurationAnnotation->type, 'path' => $configurationPath), ConfigurationProperty::PROPERTY_TYPES_CONFIGURATION);
         }
         $objectConfiguration->setProperties($properties);
     }
 }