/**
  * @param string $className
  * @return array
  */
 public function findModelProperties($className)
 {
     $modelDefinition = array();
     foreach ($this->reflectionService->getClassPropertyNames($className) as $propertyName) {
         if (is_array($this->ignoredProperties) && in_array($propertyName, $this->ignoredProperties)) {
             continue;
         }
         $propertyType = $this->reflectionService->getPropertyTagValues($className, $propertyName, 'var');
         $type = \TYPO3\Flow\Utility\TypeHandling::parseType($propertyType[0]);
         //			if (class_exists($type['type']) && !isset($classNames[$type['type']])) {
         //				$this->readClass($type['type'], $classNames);
         //			}
         //			if (class_exists($type['elementType']) && !isset($classNames[$type['elementType']])) {
         //				if ($this->reflectionService->isClassAbstract($type['elementType'])) {
         //					$implementations = $this->reflectionService->getAllSubClassNamesForClass($type['elementType']);
         //					foreach ($implementations as $implementationClassName) {
         //						if (isset($classNames[$implementationClassName])) {
         //							continue;
         //						}
         //						$this->readClass($implementationClassName, $classNames);
         //					}
         //				} else {
         //					$this->readClass($type['elementType'], $classNames);
         //				}
         //			}
         // TODO: Add lookup for relations and add them to the modelImplementations
         $modelDefinition[$propertyName] = array('type' => \TYPO3\Flow\Utility\TypeHandling::isCollectionType($type['type']) ? $type['elementType'] : $type['type']);
     }
     return $modelDefinition;
 }
 /**
  * Convert raw property values to the correct type according to a node type configuration
  *
  * @param NodeType $nodeType
  * @param string $propertyName
  * @param string $rawValue
  * @param Context $context
  * @return mixed
  */
 public function convert(NodeType $nodeType, $propertyName, $rawValue, Context $context)
 {
     $propertyType = $nodeType->getPropertyType($propertyName);
     switch ($propertyType) {
         case 'string':
             return $rawValue;
         case 'reference':
             return $this->convertReference($rawValue, $context);
         case 'references':
             return $this->convertReferences($rawValue, $context);
         case 'DateTime':
             return $this->convertDateTime($rawValue);
         case 'integer':
             return $this->convertInteger($rawValue);
         case 'boolean':
             return $this->convertBoolean($rawValue);
         case 'array':
             return $this->convertArray($rawValue);
         default:
             $innerType = $propertyType;
             if ($propertyType !== null) {
                 try {
                     $parsedType = \TYPO3\Flow\Utility\TypeHandling::parseType($propertyType);
                     $innerType = $parsedType['elementType'] ?: $parsedType['type'];
                 } catch (\TYPO3\Flow\Utility\Exception\InvalidTypeException $exception) {
                 }
             }
             if (is_string($rawValue) && $this->objectManager->isRegistered($innerType) && $rawValue !== '') {
                 return $this->propertyMapper->convert(json_decode($rawValue, true), $propertyType, $configuration);
             }
     }
 }
 /**
  * Convert raw property values to the correct type according to a node type configuration
  *
  * @param NodeType $nodeType
  * @param string $propertyName
  * @param string $rawValue
  * @param Context $context
  * @return mixed
  */
 public function convert(NodeType $nodeType, $propertyName, $rawValue, Context $context)
 {
     $propertyType = $nodeType->getPropertyType($propertyName);
     switch ($propertyType) {
         case 'string':
             return $rawValue;
         case 'reference':
             return $this->convertReference($rawValue, $context);
         case 'references':
             return $this->convertReferences($rawValue, $context);
         case 'DateTime':
             return $this->convertDateTime($rawValue);
         case 'integer':
             return $this->convertInteger($rawValue);
         case 'boolean':
             return $this->convertBoolean($rawValue);
         case 'array':
             return $this->convertArray($rawValue);
         default:
             $innerType = $propertyType;
             if ($propertyType !== null) {
                 try {
                     $parsedType = \TYPO3\Flow\Utility\TypeHandling::parseType($propertyType);
                     $innerType = $parsedType['elementType'] ?: $parsedType['type'];
                 } catch (\TYPO3\Flow\Utility\Exception\InvalidTypeException $exception) {
                 }
             }
             if ((is_string($rawValue) || is_array($rawValue)) && $this->objectManager->isRegistered($innerType) && $rawValue !== '') {
                 $propertyMappingConfiguration = new MvcPropertyMappingConfiguration();
                 $propertyMappingConfiguration->allowOverrideTargetType();
                 $propertyMappingConfiguration->allowAllProperties();
                 $propertyMappingConfiguration->skipUnknownProperties();
                 $propertyMappingConfiguration->setTypeConverterOption('TYPO3\\Flow\\Property\\TypeConverter\\PersistentObjectConverter', \TYPO3\Flow\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_MODIFICATION_ALLOWED, true);
                 $propertyMappingConfiguration->setTypeConverterOption('TYPO3\\Flow\\Property\\TypeConverter\\PersistentObjectConverter', \TYPO3\Flow\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED, true);
                 return $this->propertyMapper->convert($rawValue, $propertyType, $propertyMappingConfiguration);
             } else {
                 return $rawValue;
             }
     }
 }
 /**
  * @param ClassSchema $classSchema
  * @param string $propertyName
  * @return boolean
  * @throws InvalidPropertyTypeException
  * @throws \InvalidArgumentException
  */
 protected function evaluateClassPropertyAnnotationsForSchema(ClassSchema $classSchema, $propertyName)
 {
     $skipArtificialIdentity = false;
     $className = $classSchema->getClassName();
     if ($this->isPropertyAnnotatedWith($className, $propertyName, Flow\Transient::class)) {
         return false;
     }
     if ($this->isPropertyAnnotatedWith($className, $propertyName, Flow\Inject::class)) {
         return false;
     }
     if (!$this->isPropertyTaggedWith($className, $propertyName, 'var')) {
         return false;
     }
     $varTagValues = $this->getPropertyTagValues($className, $propertyName, 'var');
     if (count($varTagValues) > 1) {
         throw new InvalidPropertyTypeException('More than one @var annotation given for "' . $className . '::$' . $propertyName . '"', 1367334366);
     }
     $declaredType = strtok(trim(current($varTagValues), " \n\t"), " \n\t");
     try {
         TypeHandling::parseType($declaredType);
     } catch (InvalidTypeException $exception) {
         throw new \InvalidArgumentException(sprintf($exception->getMessage(), 'class "' . $className . '" for property "' . $propertyName . '"'), 1315564475);
     }
     if ($this->isPropertyAnnotatedWith($className, $propertyName, ORM\Id::class)) {
         $skipArtificialIdentity = true;
     }
     $classSchema->addProperty($propertyName, $declaredType, $this->isPropertyAnnotatedWith($className, $propertyName, Flow\Lazy::class), $this->isPropertyAnnotatedWith($className, $propertyName, Flow\Transient::class));
     if ($this->isPropertyAnnotatedWith($className, $propertyName, Flow\Identity::class)) {
         $classSchema->markAsIdentityProperty($propertyName);
     }
     return $skipArtificialIdentity;
 }
 private function buildNodeProperty(NodeInterface $node, $propertyName, $dataType)
 {
     if (substr($propertyName, 0, 1) === '_') {
         $propertyValue = ObjectAccess::getProperty($node, substr($propertyName, 1));
     } else {
         $propertyValue = $node->getProperty($propertyName);
     }
     // Enforce an integer value for integer properties as otherwise javascript will give NaN and VIE converts it to an array containing 16 times 'NaN'
     if ($dataType === 'integer') {
         $propertyValue = (int) $propertyValue;
     }
     // Serialize boolean values to String
     if ($dataType === 'boolean') {
         return $propertyValue ? 'true' : 'false';
     }
     // Serialize array values to String
     if ($dataType === 'array') {
         return $propertyValue;
     }
     // Serialize date values to String
     if ($dataType === 'DateTime') {
         if (!$propertyValue instanceof \DateTimeInterface) {
             return '';
         }
         $value = clone $propertyValue;
         return $value->setTimezone(new \DateTimeZone('UTC'))->format(\DateTime::W3C);
     }
     // Serialize node references to node identifiers
     if ($dataType === 'references') {
         $nodeIdentifiers = array();
         if (is_array($propertyValue)) {
             /** @var $subNode NodeInterface */
             foreach ($propertyValue as $subNode) {
                 $nodeIdentifiers[] = $subNode->getIdentifier();
             }
         }
         return $nodeIdentifiers;
     }
     // Serialize node reference to node identifier
     if ($dataType === 'reference') {
         if ($propertyValue instanceof NodeInterface) {
             return $propertyValue->getIdentifier();
         } else {
             return '';
         }
     }
     if ($propertyValue instanceof \TYPO3\Media\Domain\Model\ImageInterface) {
         $propertyMappingConfiguration = new \TYPO3\Flow\Property\PropertyMappingConfiguration();
         return $this->entityToIdentityConverter->convertFrom($propertyValue, 'array', array(), $propertyMappingConfiguration);
     }
     // Serialize an Asset to JSON (the NodeConverter expects JSON for object type properties)
     if ($dataType === ltrim('TYPO3\\Media\\Domain\\Model\\Asset', '\\') && $propertyValue !== null) {
         if ($propertyValue instanceof \TYPO3\Media\Domain\Model\Asset) {
             return $this->persistenceManager->getIdentifierByObject($propertyValue);
         }
     }
     // Serialize an array of Assets to JSON
     if (is_array($propertyValue)) {
         $parsedType = \TYPO3\Flow\Utility\TypeHandling::parseType($dataType);
         if ($parsedType['elementType'] === ltrim('TYPO3\\Media\\Domain\\Model\\Asset', '\\')) {
             $convertedValues = array();
             foreach ($propertyValue as $singlePropertyValue) {
                 if ($singlePropertyValue instanceof \TYPO3\Media\Domain\Model\Asset) {
                     $convertedValues[] = $this->persistenceManager->getIdentifierByObject($singlePropertyValue);
                 }
             }
             return $convertedValues;
         }
     }
     return $propertyValue === null ? '' : $propertyValue;
 }
 /**
  * Iterates through the given $properties setting them on the specified $node using the appropriate TypeConverters.
  *
  * @param object $nodeLike
  * @param NodeType $nodeType
  * @param array $properties
  * @param TYPO3CRContext $context
  * @param PropertyMappingConfigurationInterface $configuration
  * @return void
  * @throws TypeConverterException
  */
 protected function setNodeProperties($nodeLike, NodeType $nodeType, array $properties, TYPO3CRContext $context, PropertyMappingConfigurationInterface $configuration = null)
 {
     $nodeTypeProperties = $nodeType->getProperties();
     unset($properties['_lastPublicationDateTime']);
     foreach ($properties as $nodePropertyName => $nodePropertyValue) {
         if (substr($nodePropertyName, 0, 2) === '__') {
             continue;
         }
         $nodePropertyType = isset($nodeTypeProperties[$nodePropertyName]['type']) ? $nodeTypeProperties[$nodePropertyName]['type'] : null;
         switch ($nodePropertyType) {
             case 'reference':
                 $nodePropertyValue = $context->getNodeByIdentifier($nodePropertyValue);
                 break;
             case 'references':
                 $nodeIdentifiers = json_decode($nodePropertyValue);
                 $nodePropertyValue = array();
                 if (is_array($nodeIdentifiers)) {
                     foreach ($nodeIdentifiers as $nodeIdentifier) {
                         $referencedNode = $context->getNodeByIdentifier($nodeIdentifier);
                         if ($referencedNode !== null) {
                             $nodePropertyValue[] = $referencedNode;
                         }
                     }
                 } elseif ($nodeIdentifiers !== null) {
                     throw new TypeConverterException(sprintf('node type "%s" expects an array of identifiers for its property "%s"', $nodeType->getName(), $nodePropertyName), 1383587419);
                 }
                 break;
             case 'DateTime':
                 if ($nodePropertyValue !== '' && ($nodePropertyValue = \DateTime::createFromFormat(\DateTime::W3C, $nodePropertyValue)) !== false) {
                     $nodePropertyValue->setTimezone(new \DateTimeZone(date_default_timezone_get()));
                 } else {
                     $nodePropertyValue = null;
                 }
                 break;
             case 'integer':
                 $nodePropertyValue = intval($nodePropertyValue);
                 break;
             case 'boolean':
                 if (is_string($nodePropertyValue)) {
                     $nodePropertyValue = $nodePropertyValue === 'true' ? true : false;
                 }
                 break;
             case 'array':
                 $nodePropertyValue = json_decode($nodePropertyValue, true);
                 break;
         }
         if (substr($nodePropertyName, 0, 1) === '_') {
             $nodePropertyName = substr($nodePropertyName, 1);
             ObjectAccess::setProperty($nodeLike, $nodePropertyName, $nodePropertyValue);
             continue;
         }
         if (!isset($nodeTypeProperties[$nodePropertyName])) {
             if ($configuration !== null && $configuration->shouldSkipUnknownProperties()) {
                 continue;
             } else {
                 throw new TypeConverterException(sprintf('Node type "%s" does not have a property "%s" according to the schema', $nodeType->getName(), $nodePropertyName), 1359552744);
             }
         }
         $innerType = $nodePropertyType;
         if ($nodePropertyType !== null) {
             try {
                 $parsedType = TypeHandling::parseType($nodePropertyType);
                 $innerType = $parsedType['elementType'] ?: $parsedType['type'];
             } catch (InvalidTypeException $exception) {
             }
         }
         if (is_string($nodePropertyValue) && $this->objectManager->isRegistered($innerType) && $nodePropertyValue !== '') {
             $nodePropertyValue = $this->propertyMapper->convert(json_decode($nodePropertyValue, true), $nodePropertyType, $configuration);
         }
         $nodeLike->setProperty($nodePropertyName, $nodePropertyValue);
     }
 }
 /**
  * The type of a property is determined by the reflection service.
  *
  * @param string $targetType
  * @param string $propertyName
  * @param \TYPO3\Flow\Property\PropertyMappingConfigurationInterface $configuration
  * @return string
  * @throws \TYPO3\Flow\Property\Exception\InvalidTargetException
  */
 public function getTypeOfChildProperty($targetType, $propertyName, \TYPO3\Flow\Property\PropertyMappingConfigurationInterface $configuration)
 {
     $configuredTargetType = $configuration->getConfigurationFor($propertyName)->getConfigurationValue('TYPO3\\Flow\\Property\\TypeConverter\\PersistentObjectConverter', self::CONFIGURATION_TARGET_TYPE);
     if ($configuredTargetType !== NULL) {
         return $configuredTargetType;
     }
     $varAnnotations = $this->reflectionService->getPropertyTagValues($targetType, $propertyName, 'var');
     if ($varAnnotations === array()) {
         // No @var annotation found for property, use string as default
         // TODO: Read this from some kind of configuration
         return 'string';
     }
     $propertyInformation = \TYPO3\Flow\Utility\TypeHandling::parseType($varAnnotations[0]);
     return $propertyInformation['type'] . ($propertyInformation['elementType'] !== NULL ? '<' . $propertyInformation['elementType'] . '>' : '');
 }
 /**
  * Builds a base validator conjunction for the given data type.
  *
  * The base validation rules are those which were declared directly in a class (typically
  * a model) through some validate annotations on properties.
  *
  * If a property holds a class for which a base validator exists, that property will be
  * checked as well, regardless of a validate annotation
  *
  * Additionally, if a custom validator was defined for the class in question, it will be added
  * to the end of the conjunction. A custom validator is found if it follows the naming convention
  * "Replace '\Model\' by '\Validator\' and append 'Validator'".
  *
  * Example: $targetClassName is TYPO3\Foo\Domain\Model\Quux, then the validator will be found if it has the
  * name TYPO3\Foo\Domain\Validator\QuuxValidator
  *
  * @param string $indexKey The key to use as index in $this->baseValidatorConjunctions; calculated from target class name and validation groups
  * @param string $targetClassName The data type to build the validation conjunction for. Needs to be the fully qualified class name.
  * @param array $validationGroups The validation groups to build the validator for
  * @return void
  * @throws \TYPO3\Flow\Validation\Exception\NoSuchValidatorException
  * @throws \InvalidArgumentException
  */
 protected function buildBaseValidatorConjunction($indexKey, $targetClassName, array $validationGroups)
 {
     $conjunctionValidator = new ConjunctionValidator();
     $this->baseValidatorConjunctions[$indexKey] = $conjunctionValidator;
     if (!TypeHandling::isSimpleType($targetClassName) && class_exists($targetClassName)) {
         // Model based validator
         $objectValidator = new GenericObjectValidator(array());
         foreach ($this->reflectionService->getClassPropertyNames($targetClassName) as $classPropertyName) {
             $classPropertyTagsValues = $this->reflectionService->getPropertyTagsValues($targetClassName, $classPropertyName);
             if (!isset($classPropertyTagsValues['var'])) {
                 throw new \InvalidArgumentException(sprintf('There is no @var annotation for property "%s" in class "%s".', $classPropertyName, $targetClassName), 1363778104);
             }
             try {
                 $parsedType = TypeHandling::parseType(trim(implode('', $classPropertyTagsValues['var']), ' \\'));
             } catch (\TYPO3\Flow\Utility\Exception\InvalidTypeException $exception) {
                 throw new \InvalidArgumentException(sprintf(' @var annotation of ' . $exception->getMessage(), 'class "' . $targetClassName . '", property "' . $classPropertyName . '"'), 1315564744, $exception);
             }
             if ($this->reflectionService->isPropertyAnnotatedWith($targetClassName, $classPropertyName, \TYPO3\Flow\Annotations\IgnoreValidation::class)) {
                 continue;
             }
             $propertyTargetClassName = $parsedType['type'];
             if (TypeHandling::isCollectionType($propertyTargetClassName) === TRUE) {
                 $collectionValidator = $this->createValidator(\TYPO3\Flow\Validation\Validator\CollectionValidator::class, array('elementType' => $parsedType['elementType'], 'validationGroups' => $validationGroups));
                 $objectValidator->addPropertyValidator($classPropertyName, $collectionValidator);
             } elseif (!TypeHandling::isSimpleType($propertyTargetClassName) && $this->objectManager->isRegistered($propertyTargetClassName) && $this->objectManager->getScope($propertyTargetClassName) === \TYPO3\Flow\Object\Configuration\Configuration::SCOPE_PROTOTYPE) {
                 $validatorForProperty = $this->getBaseValidatorConjunction($propertyTargetClassName, $validationGroups);
                 if (count($validatorForProperty) > 0) {
                     $objectValidator->addPropertyValidator($classPropertyName, $validatorForProperty);
                 }
             }
             $validateAnnotations = $this->reflectionService->getPropertyAnnotations($targetClassName, $classPropertyName, \TYPO3\Flow\Annotations\Validate::class);
             foreach ($validateAnnotations as $validateAnnotation) {
                 if (count(array_intersect($validateAnnotation->validationGroups, $validationGroups)) === 0) {
                     // In this case, the validation groups for the property do not match current validation context
                     continue;
                 }
                 $newValidator = $this->createValidator($validateAnnotation->type, $validateAnnotation->options);
                 if ($newValidator === NULL) {
                     throw new Exception\NoSuchValidatorException('Invalid validate annotation in ' . $targetClassName . '::' . $classPropertyName . ': Could not resolve class name for  validator "' . $validateAnnotation->type . '".', 1241098027);
                 }
                 $objectValidator->addPropertyValidator($classPropertyName, $newValidator);
             }
         }
         if (count($objectValidator->getPropertyValidators()) > 0) {
             $conjunctionValidator->addValidator($objectValidator);
         }
     }
     $this->addCustomValidators($targetClassName, $conjunctionValidator);
 }
 /**
  * The type of a property is determined by the reflection service.
  *
  * @param string $targetType
  * @param string $propertyName
  * @param PropertyMappingConfigurationInterface $configuration
  * @return string
  * @throws InvalidTargetException
  */
 public function getTypeOfChildProperty($targetType, $propertyName, PropertyMappingConfigurationInterface $configuration)
 {
     $configuredTargetType = $configuration->getConfigurationFor($propertyName)->getConfigurationValue(ObjectConverter::class, self::CONFIGURATION_TARGET_TYPE);
     if ($configuredTargetType !== null) {
         return $configuredTargetType;
     }
     $methodParameters = $this->reflectionService->getMethodParameters($targetType, '__construct');
     if (isset($methodParameters[$propertyName]) && isset($methodParameters[$propertyName]['type'])) {
         return $methodParameters[$propertyName]['type'];
     } elseif ($this->reflectionService->hasMethod($targetType, ObjectAccess::buildSetterMethodName($propertyName))) {
         $methodParameters = $this->reflectionService->getMethodParameters($targetType, ObjectAccess::buildSetterMethodName($propertyName));
         $methodParameter = current($methodParameters);
         if (!isset($methodParameter['type'])) {
             throw new InvalidTargetException('Setter for property "' . $propertyName . '" had no type hint or documentation in target object of type "' . $targetType . '".', 1303379158);
         } else {
             return $methodParameter['type'];
         }
     } else {
         $targetPropertyNames = $this->reflectionService->getClassPropertyNames($targetType);
         if (in_array($propertyName, $targetPropertyNames)) {
             $varTagValues = $this->reflectionService->getPropertyTagValues($targetType, $propertyName, 'var');
             if (count($varTagValues) > 0) {
                 // This ensures that FQCNs are returned without leading backslashes. Otherwise, something like @var \DateTime
                 // would not find a property mapper. It is needed because the ObjectConverter doesn't use class schemata,
                 // but reads the annotations directly.
                 $declaredType = strtok(trim(current($varTagValues), " \n\t"), " \n\t");
                 try {
                     $parsedType = TypeHandling::parseType($declaredType);
                 } catch (InvalidTypeException $exception) {
                     throw new \InvalidArgumentException(sprintf($exception->getMessage(), 'class "' . $targetType . '" for property "' . $propertyName . '"'), 1467699674);
                 }
                 return $parsedType['type'] . ($parsedType['elementType'] !== null ? '<' . $parsedType['elementType'] . '>' : '');
             } else {
                 throw new InvalidTargetException(sprintf('Public property "%s" had no proper type annotation (i.e. "@var") in target object of type "%s".', $propertyName, $targetType), 1406821818);
             }
         }
     }
     throw new InvalidTargetException(sprintf('Property "%s" has neither a setter or constructor argument, nor is public, in target object of type "%s".', $propertyName, $targetType), 1303379126);
 }
 /**
  * Adds (defines) a specific property and its type.
  *
  * @param string $name Name of the property
  * @param string $type Type of the property
  * @param boolean $lazy Whether the property should be lazy-loaded when reconstituting
  * @param boolean $transient Whether the property should not be considered for persistence
  * @return void
  * @throws \InvalidArgumentException
  */
 public function addProperty($name, $type, $lazy = false, $transient = false)
 {
     try {
         $type = TypeHandling::parseType($type);
     } catch (InvalidTypeException $exception) {
         throw new \InvalidArgumentException(sprintf($exception->getMessage(), 'class "' . $name . '"'), 1315564474);
     }
     $this->properties[$name] = ['type' => $type['type'], 'elementType' => $type['elementType'], 'lazy' => $lazy, 'transient' => $transient];
 }
 /**
  * Adds properties of the class at hand to the class schema.
  *
  * Only non-transient properties annotated with a var annotation will be added.
  * Invalid annotations will cause an exception to be thrown. Properties pointing
  * to existing classes will only be added if the target type is annotated as
  * entity or valueobject.
  *
  * @param \TYPO3\Flow\Reflection\ClassSchema $classSchema
  * @return void
  * @throws \InvalidArgumentException
  * @throws \TYPO3\Flow\Reflection\Exception\InvalidPropertyTypeException
  */
 protected function addPropertiesToClassSchema(ClassSchema $classSchema)
 {
     // those are added as property even if not tagged with entity/valueobject
     $propertyTypeWhiteList = array('DateTime', 'SplObjectStorage', 'Doctrine\\Common\\Collections\\Collection', 'Doctrine\\Common\\Collections\\ArrayCollection');
     $className = $classSchema->getClassName();
     $needsArtificialIdentity = TRUE;
     foreach ($this->getClassPropertyNames($className) as $propertyName) {
         if ($this->isPropertyTaggedWith($className, $propertyName, 'var') && !$this->isPropertyAnnotatedWith($className, $propertyName, 'TYPO3\\Flow\\Annotations\\Transient')) {
             $varTagValues = $this->getPropertyTagValues($className, $propertyName, 'var');
             if (count($varTagValues) > 1) {
                 throw new InvalidPropertyTypeException('More than one @var annotation given for "' . $className . '::$' . $propertyName . '"', 1367334366);
             } else {
                 $declaredType = strtok(trim(current($varTagValues), " \n\t"), " \n\t");
             }
             if ($this->isPropertyAnnotatedWith($className, $propertyName, 'Doctrine\\ORM\\Mapping\\Id')) {
                 $needsArtificialIdentity = FALSE;
             }
             try {
                 $parsedType = TypeHandling::parseType($declaredType);
             } catch (InvalidTypeException $exception) {
                 throw new \InvalidArgumentException(sprintf($exception->getMessage(), 'class "' . $className . '" for property "' . $propertyName . '"'), 1315564475);
             }
             if (!in_array($parsedType['type'], $propertyTypeWhiteList) && (class_exists($parsedType['type']) || interface_exists($parsedType['type'])) && !($this->isClassAnnotatedWith($parsedType['type'], 'TYPO3\\Flow\\Annotations\\Entity') || $this->isClassAnnotatedWith($parsedType['type'], 'Doctrine\\ORM\\Mapping\\Entity') || $this->isClassAnnotatedWith($parsedType['type'], 'TYPO3\\Flow\\Annotations\\ValueObject'))) {
                 continue;
             }
             $classSchema->addProperty($propertyName, $declaredType, $this->isPropertyAnnotatedWith($className, $propertyName, 'TYPO3\\Flow\\Annotations\\Lazy'));
             if ($this->isPropertyAnnotatedWith($className, $propertyName, 'TYPO3\\Flow\\Annotations\\Identity')) {
                 $classSchema->markAsIdentityProperty($propertyName);
             }
         }
     }
     if ($needsArtificialIdentity === TRUE) {
         $classSchema->addProperty('Persistence_Object_Identifier', 'string');
     }
 }
 /**
  * Adds properties of the class at hand to the class schema.
  *
  * Properties will be added if they have a var annotation && (!transient-annotation && !inject-annotation)
  *
  * Invalid annotations will cause an exception to be thrown.
  *
  * @param \TYPO3\Flow\Reflection\ClassSchema $classSchema
  * @return void
  * @throws Exception\InvalidPropertyTypeException
  */
 protected function addPropertiesToClassSchema(ClassSchema $classSchema)
 {
     $className = $classSchema->getClassName();
     $needsArtificialIdentity = true;
     foreach ($this->getClassPropertyNames($className) as $propertyName) {
         if ($this->isPropertyAnnotatedWith($className, $propertyName, \TYPO3\Flow\Annotations\Transient::class)) {
             continue;
         }
         if ($this->isPropertyAnnotatedWith($className, $propertyName, \TYPO3\Flow\Annotations\Inject::class)) {
             continue;
         }
         if ($this->isPropertyTaggedWith($className, $propertyName, 'var')) {
             $varTagValues = $this->getPropertyTagValues($className, $propertyName, 'var');
             if (count($varTagValues) > 1) {
                 throw new InvalidPropertyTypeException('More than one @var annotation given for "' . $className . '::$' . $propertyName . '"', 1367334366);
             } else {
                 $declaredType = strtok(trim(current($varTagValues), " \n\t"), " \n\t");
             }
             try {
                 TypeHandling::parseType($declaredType);
             } catch (InvalidTypeException $exception) {
                 throw new \InvalidArgumentException(sprintf($exception->getMessage(), 'class "' . $className . '" for property "' . $propertyName . '"'), 1315564475);
             }
             if ($this->isPropertyAnnotatedWith($className, $propertyName, 'Doctrine\\ORM\\Mapping\\Id')) {
                 $needsArtificialIdentity = false;
             }
             $classSchema->addProperty($propertyName, $declaredType, $this->isPropertyAnnotatedWith($className, $propertyName, \TYPO3\Flow\Annotations\Lazy::class), $this->isPropertyAnnotatedWith($className, $propertyName, \TYPO3\Flow\Annotations\Transient::class));
             if ($this->isPropertyAnnotatedWith($className, $propertyName, \TYPO3\Flow\Annotations\Identity::class)) {
                 $classSchema->markAsIdentityProperty($propertyName);
             }
         }
         if ($needsArtificialIdentity === true) {
             $classSchema->addProperty('Persistence_Object_Identifier', 'string');
         }
     }
 }
 /**
  * Traverses the given object structure in order to transform it into an
  * array structure.
  *
  * @param object $object Object to traverse
  * @param array $configuration Configuration for transforming the given object
  * @param boolean $onlyIdentifier
  * @param boolean $enableSideLoading
  * @return array Object structure as an array
  * @todo implement sideloading
  */
 protected function transformObject($object, array $configuration, $onlyIdentifier = TRUE, $enableSideLoading = TRUE)
 {
     if ($object instanceof \DateTime) {
         return $object->format('Y-m-d\\TH:i:s');
     } elseif ($onlyIdentifier === TRUE) {
         return $this->persistenceManager->getIdentifierByObject($object);
     } else {
         $transformedObject = array('id' => $this->persistenceManager->getIdentifierByObject($object));
         $objectName = $this->objectManager->getObjectNameByClassName($this->reflectionService->getClassNameByObject($object));
         foreach (ObjectAccess::getGettablePropertyNames($object) as $propertyName) {
             if ($this->isPropertyIgnored($objectName, $propertyName)) {
                 continue;
             }
             $propertyValue = ObjectAccess::getProperty($object, $propertyName);
             if (is_array($propertyValue) || $propertyValue instanceof \ArrayAccess) {
                 $propertyValue = $this->transformValue($propertyValue, array(), TRUE, $enableSideLoading);
                 if ($this->isRelation($objectName, $propertyName)) {
                     $this->addLink($transformedObject, $propertyName, $propertyValue);
                     if ($enableSideLoading === TRUE && !$this->reflectionService->isPropertyAnnotatedWith($objectName, $propertyName, 'TYPO3\\Flow\\Annotations\\Lazy')) {
                         $propertyTags = $this->reflectionService->getPropertyTagValues($objectName, $propertyName, 'var');
                         $propertyObjectType = TypeHandling::parseType($propertyTags[0]);
                         $this->sideLoad($propertyObjectType['elementType'], $propertyValue);
                     }
                     $propertyValue = NULL;
                 } elseif (empty($propertyValue)) {
                     $propertyValue = NULL;
                 }
             } elseif ($this->isSimpleValue($propertyValue)) {
                 if (is_object($propertyValue)) {
                     $propertyValue = $this->transformObject($propertyValue, $configuration, FALSE, $enableSideLoading);
                 }
             } elseif (is_object($propertyValue)) {
                 $propertyObjectName = $this->objectManager->getObjectNameByClassName($this->reflectionService->getClassNameByObject($propertyValue));
                 if (!$this->isObjectIgnored($propertyObjectName)) {
                     $propertyValue = $this->transformObject($propertyValue, $configuration, TRUE, $enableSideLoading);
                     if ($this->isRelation($objectName, $propertyName)) {
                         $this->addLink($transformedObject, $propertyName, $propertyValue);
                         if ($enableSideLoading === TRUE && !$this->reflectionService->isPropertyAnnotatedWith($objectName, $propertyName, 'TYPO3\\Flow\\Annotations\\Lazy')) {
                             $this->sideLoad($propertyObjectName, $propertyValue);
                         }
                         $propertyValue = NULL;
                     } elseif (empty($propertyValue)) {
                         $propertyValue = NULL;
                     }
                 }
             }
             if ($propertyValue !== NULL) {
                 if (is_object($propertyValue)) {
                     $propertyObjectName = $this->objectManager->getObjectNameByClassName($this->reflectionService->getClassNameByObject($propertyValue));
                     if (!$this->isObjectIgnored($propertyObjectName)) {
                         $transformedObject[$propertyName] = $propertyValue;
                     }
                 } else {
                     $transformedObject[$propertyName] = $propertyValue;
                 }
             }
         }
         return $transformedObject;
     }
 }
 /**
  * Return the type of a given sub-property inside the $targetType
  *
  * @param string $targetType
  * @param string $propertyName
  * @param PropertyMappingConfigurationInterface $configuration
  * @return string
  */
 public function getTypeOfChildProperty($targetType, $propertyName, PropertyMappingConfigurationInterface $configuration)
 {
     $parsedTargetType = TypeHandling::parseType($targetType);
     return $parsedTargetType['elementType'];
 }
 /**
  * Adds (defines) a specific property and its type.
  *
  * @param string $name Name of the property
  * @param string $type Type of the property
  * @param boolean $lazy Whether the property should be lazy-loaded when reconstituting
  * @return void
  * @throws \InvalidArgumentException
  */
 public function addProperty($name, $type, $lazy = FALSE)
 {
     try {
         $type = \TYPO3\Flow\Utility\TypeHandling::parseType($type);
     } catch (\TYPO3\Flow\Utility\Exception\InvalidTypeException $exception) {
         throw new \InvalidArgumentException(sprintf($exception->getMessage(), 'class "' . $name . '"'), 1315564474);
     }
     $this->properties[$name] = array('type' => $type['type'], 'elementType' => $type['elementType'], 'lazy' => $lazy);
 }
 /**
  * @test
  * @dataProvider types
  */
 public function parseTypeReturnsArrayWithInformation($type, $expectedResult)
 {
     $this->assertEquals($expectedResult, TypeHandling::parseType($type), 'Failed for ' . $type);
 }
 /**
  * Create a property mapping configuration for the given dataType to convert a Node property value from the given dataType to a simple type.
  *
  * @param string $dataType
  * @return PropertyMappingConfigurationInterface
  */
 protected function createConfiguration($dataType)
 {
     if (!isset($this->generatedPropertyMappingConfigurations[$dataType])) {
         $propertyMappingConfiguration = new PropertyMappingConfiguration();
         $propertyMappingConfiguration->allowAllProperties();
         $parsedType = ['elementType' => null, 'type' => $dataType];
         // Special handling for "reference(s)", should be deprecated and normlized to array<NodeInterface>
         if ($dataType !== 'references' && $dataType !== 'reference') {
             $parsedType = TypeHandling::parseType($dataType);
         }
         if ($this->setTypeConverterForType($propertyMappingConfiguration, $dataType) === false) {
             $this->setTypeConverterForType($propertyMappingConfiguration, $parsedType['type']);
         }
         $elementConfiguration = $propertyMappingConfiguration->forProperty('*');
         $this->setTypeConverterForType($elementConfiguration, $parsedType['elementType']);
         $this->generatedPropertyMappingConfigurations[$dataType] = $propertyMappingConfiguration;
     }
     return $this->generatedPropertyMappingConfigurations[$dataType];
 }