Get a Graphql Union configuration from given union annotation.

@param string    $className
@param GQL\Union $unionAnnotation
@param array     $classAnnotations
@param array     $methods

@return array
345769
private static function getGraphqlUnion(string $className, GQL\Union $unionAnnotation, array $classAnnotations, array $methods): array
    {
        $unionConfiguration = ['types' => $unionAnnotation->types];
        $unionConfiguration = self::getDescriptionConfiguration($classAnnotations) + $unionConfiguration;

        if ($unionAnnotation->resolveType) {
            $unionConfiguration['resolveType'] = self::formatExpression($unionAnnotation->resolveType);
        } else {
            if (isset($methods['resolveType'])) {
                $method = $methods['resolveType']['method'];
                if ($method->isStatic() && $method->isPublic()) {
                    $unionConfiguration['resolveType'] = self::formatExpression(\sprintf("@=call('%s::%s', [service('overblog_graphql.type_resolver'), value], true)", self::formatNamespaceForExpression($className), 'resolveType'));
                } else {
                    throw new InvalidArgumentException(\sprintf('The "resolveType()" method on class must be static and public. Or you must define a "resolveType" attribute on the @Union annotation.'));
                }
            } else {
                throw new InvalidArgumentException(\sprintf('The annotation @Union has no "resolveType" attribute and the related class has no "resolveType()" public static method. You need to define of them.'));
            }
        }

        return ['type' => 'union', 'config' => $unionConfiguration];
    }
95893
public static function getClassAnnotations($className)
    {
        if (!isset(self::$annotationCache[$className])) {
            $class = new \ReflectionClass($className);
            self::$annotationCache[$className] = self::parseAnnotations($class->getDocComment());
        }

        return self::$annotationCache[$className];
    }
537437
static function getAnnotation($name, $class, $method = NULL, $nullable = TRUE)
	{
		try {
			NULL === $method or $annotation = self::getMethodAnnotation($method, $name, $class);
			isset($annotation) or $annotation = self::getClassAnnotation($class, $name);
		} catch (ReflectionException $e) {
			$annotation = NULL;
		}

		return !$nullable && NULL === $annotation
			? self::annotationClass($name)
			: $annotation;
	}
1324764
public static function getReturnTypes($className, $methodName)
	{
		$methodName = Method::from($className, $methodName);
		list($types) = preg_split('~\s~', $methodName->getAnnotation('return'), 2);
		return explode('|', $types);
	}
829367
public static Set<Field> getFieldsAnnotatedBy(Class<?> classs, Class<? extends Annotation> ann) {
    Set<Field> set = new HashSet<>();
    for (Field field : getAllFields(classs)) {
      if (field.isAnnotationPresent(ann)) {
        set.add(field);
      }
    }
    return set;
  }
645964
private static function getFirstAnnotationMatching(array $annotations, $annotationClass)
    {
        if (\is_string($annotationClass)) {
            $annotationClass = [$annotationClass];
        }

        foreach ($annotations as $annotation) {
            foreach ($annotationClass as $class) {
                if ($annotation instanceof $class) {
                    return $annotation;
                }
            }
        }

        return false;
    }
1031348
private function getParamsFromAnnotationArray(array $annotations)
    {
        $params = array();
        foreach ($annotations as $annotation) {
            if ($annotation instanceof ParamInterface) {
                $params[$annotation->getName()] = $annotation;
            }
        }

        return $params;
    }
877512
public function parser(string $className, $objectAnnotation = null, string $propertyName = "", string $methodName = "", $propertyValue = null)
    {
        Collector::$methodAnnotations[$className][$methodName][] = get_class($objectAnnotation);
        return null;
    }
1278838
public static AnnotationAttributes getAnnotationAttributes(Annotation annotation, boolean classValuesAsString,
			boolean nestedAnnotationsAsMap) {

		AnnotationAttributes attrs = new AnnotationAttributes();
		Method[] methods = annotation.annotationType().getDeclaredMethods();
		for (Method method : methods) {
			if (method.getParameterTypes().length == 0 && method.getReturnType() != void.class) {
				try {
					Object value = method.invoke(annotation);
					attrs.put(method.getName(), adaptValue(value, classValuesAsString, nestedAnnotationsAsMap));
				}
				catch (Exception ex) {
					throw new IllegalStateException("Could not obtain annotation attribute values", ex);
				}
			}
		}
		return attrs;
	}
375867
public static function getPropertiesAnnotations($className, $annotationClassName = null)
    {
        if(null === $annotationClassName)
        {
            return static::$annotations[$className]['Properties'] ?? [];
        }
        $result = [];
        foreach(static::$annotations[$className]['Properties'] ?? [] as $propertyName => $annotations)
        {
            foreach($annotations as $annotation)
            {
                if($annotation instanceof $annotationClassName)
                {
                    $result[$propertyName][] = $annotation;
                }
            }
        }
        return $result;
    }
984383
static function getAnnotations($class, $method = NULL)
	{
		$result = [];
		foreach (Arr::merge(self::getClassAnnotations($class), NULL !== $method
			? self::getMethodAnnotations($method, $class) : []) as $obj) {
			$result[get_class($obj)] = $obj;
		};

		return $result;
	}
666412
private function parseProperties(array $propertyAnnotations, array $properties, string $className)
    {
        $propertyInjections = [];

        /* @var \ReflectionProperty $property */
        foreach ($properties as $property) {
            if ($property->isStatic()) {
                continue;
            }
            $propertyName = $property->getName();
            if (!isset($propertyAnnotations[$propertyName]) || !$this->isParseProperty($propertyAnnotations[$propertyName])) {
                continue;
            }

            $object = new $className();
            $property->setAccessible(true);
            $propertyValue = $property->getValue($object);

            list($injectProperty, $isRef) = $this->parsePropertyAnnotations($propertyAnnotations, $className, $propertyName, $propertyValue);
            if ($injectProperty == null) {
                continue;
            }

            $propertyInjection = new PropertyInjection($propertyName, $injectProperty, (bool)$isRef);
            $propertyInjections[$propertyName] = $propertyInjection;
        }

        return $propertyInjections;
    }
749723
private static function getGraphqlFieldsFromProviders(string $namespace, string $className, string $annotationName, string $targetType, bool $isRoot = false)
    {
        $fields = [];
        foreach (self::$providers as $className => $configuration) {
            $providerMethods = $configuration['methods'];
            $providerAnnotation = $configuration['annotation'];

            $filteredMethods = [];
            foreach ($providerMethods as $methodName => $config) {
                $annotations = $config['annotations'];

                $annotation = self::getFirstAnnotationMatching($annotations, \sprintf('Overblog\\GraphQLBundle\\Annotation\\%s', $annotationName));
                if (!$annotation) {
                    continue;
                }

                $annotationTarget = 'Query' === $annotationName ? $annotation->targetType : null;
                if (!$annotationTarget && $isRoot) {
                    $annotationTarget = $targetType;
                }

                if ($annotationTarget !== $targetType) {
                    continue;
                }

                $filteredMethods[$methodName] = $config;
            }

            $currentValue = \sprintf("service('%s')", self::formatNamespaceForExpression($className));
            $providerFields = self::getGraphqlFieldsFromAnnotations($namespace, $filteredMethods, false, true, $currentValue, $annotationName);
            foreach ($providerFields as $fieldName => $fieldConfig) {
                if ($providerAnnotation->prefix) {
                    $fieldName = \sprintf('%s%s', $providerAnnotation->prefix, $fieldName);
                }
                $fields[$fieldName] = $fieldConfig;
            }
        }

        return $fields;
    }
329239
public function getMethod($classname, $method)
    {
        return $this->hasMethod($classname, $method)
            ? $this->methods[$classname . '::' . $method]
            : [];
    }
671772
private function parseMethods(array $methodAnnotations, string $className, array $publicMethods)
    {
        // 循环解析
        foreach ($publicMethods as $method) {
            /* @var \ReflectionMethod $method*/
            if ($method->isStatic()) {
                continue;
            }

            /* @var \ReflectionClass $declaredClass*/
            $declaredClass = $method->getDeclaringClass();
            $declaredName = $declaredClass->getName();

            // 不是当前类方法
            if ($declaredName != $className) {
                continue;
            }
            $this->parseMethodAnnotations($className, $method, $methodAnnotations);
        }
    }
1322238
private function convertClassAnnotationsToClassMetadata(
        array $classAnnotations,
        ReflectionClass $reflectionClass,
        Mapping\ClassMetadata $metadata,
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
    ) : Mapping\ClassMetadata {
        switch (true) {
            case isset($classAnnotations[Annotation\Entity::class]):
                return $this->convertClassAnnotationsToEntityClassMetadata(
                    $classAnnotations,
                    $reflectionClass,
                    $metadata,
                    $metadataBuildingContext
                );

                break;

            case isset($classAnnotations[Annotation\MappedSuperclass::class]):
                return $this->convertClassAnnotationsToMappedSuperClassMetadata(
                    $classAnnotations,
                    $reflectionClass,
                    $metadata
                );
            case isset($classAnnotations[Annotation\Embeddable::class]):
                return $this->convertClassAnnotationsToEmbeddableClassMetadata(
                    $classAnnotations,
                    $reflectionClass,
                    $metadata
                );
            default:
                throw Mapping\MappingException::classIsNotAValidEntityOrMappedSuperClass($reflectionClass->getName());
        }
    }
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);
    }