Throw an exception if the given type does not inherit expected type

@param string $classWithAnnotation
@param string $annotation
@param string $expectedClassName
@param string $actualClassName
543733
final protected function throwIfInvalidAnnotation(string $classWithAnnotation, string $annotation, string $expectedClassName, string $actualClassName): void
    {
        if (!is_a($actualClassName, $expectedClassName, true)) {
            throw new Exception('On class `' . $classWithAnnotation . '` the annotation `@API\\' . $annotation . '` expects a FQCN implementing `' . $expectedClassName . '`, but instead got: ' . $actualClassName);
        }
    }
1231571
public function parse(\Imi\Bean\Annotation\Base $annotation, string $className, string $target, string $targetName)
    {
        if($annotation instanceof \Imi\Bean\Annotation\Listener)
        {
            $this->data[] = [$annotation->eventName, $className, $annotation->priority];
            Event::on($annotation->eventName, $className, $annotation->priority);
        }
    }
412399
public function unexpected($token, $className = '\\JsPhpize\\Lexer\\Exception')
    {
        return new $className('Unexpected ' . $token->type . rtrim(' ' . ($token->value ?: '')) . $this->exceptionInfos(), 8);
    }
648014
public function parseClassAnnotations(string $className, array $annotations)
    {
        if (!$this->isParseClass($annotations)) {
            return null;
        }

        $beanData = null;
        foreach ($annotations as $annotation) {
            $annotationClass = get_class($annotation);
            if (!in_array($annotationClass, $this->getClassAnnotations())) {
                continue;
            }

            // annotation parser
            $annotationParser = $this->getAnnotationParser($annotation);
            if ($annotationParser == null) {
                continue;
            }
            $annotationData = $annotationParser->parser($className, $annotation);
            if ($annotationData != null) {
                $beanData = $annotationData;
            }
        }

        return $beanData;
    }
393533
public function parse(\Imi\Bean\Annotation\Base $annotation, string $className, string $target, string $targetName)
    {
        if($annotation instanceof \Imi\Bean\Annotation\ClassEventListener)
        {
            if(!isset($this->data[$annotation->className][$annotation->eventName]))
            {
                $this->data[$annotation->className][$annotation->eventName] = [];
            }
            $this->data[$annotation->className][$annotation->eventName][] = [
                'className' => $className,
                'priority'  => $annotation->priority,
            ];
        }
    }
672177
private void initBeanNameDefaulted(EnhancedAnnotation<T> annotatedAnnotation) {
        if (annotatedAnnotation.isAnnotationPresent(Named.class)) {
            if (!"".equals(annotatedAnnotation.getAnnotation(Named.class).value())) {
                throw MetadataLogger.LOG.valueOnNamedStereotype(annotatedAnnotation);
            }
            beanNameDefaulted = true;
        }
    }
507461
public static Predicate<RequestHandler> withClassAnnotation(final Class<? extends Annotation> annotation) {
    return input -> declaringClass(input).map(annotationPresent(annotation)).orElse(false);
  }
1224418
private function validateAnnotations()
    {
        //Count annotations in the annotation namespace, ignore annotations not in our namespace
        $count = 0;
        foreach($this->annotations as $a)
        {
            //If you find the namespace in the class name, add to count
            if(strrpos(get_class($a), self::ANNOTATION_NAMESPACE) !== false)
            {
                $count++;
            }
        }

        switch($count)
        {
            //0 annotations, just ignore
            case 0:

                return;

            //1 Annotation, it can't be index.
            case 1:

                if($this->getAnnotationIndex(self::INDEX) < 0)
                {
                    //It's not index, return.
                    return;
                }

                throw new Exception("@Index cannot be the only annotation on {$this->name} in {$this->property->getDeclaringClass()->getName()}.");

            //2 Annotations, they have to be index and property.
            case 2:

                if( ($this->getAnnotationIndex(self::PROPERTY) >= 0) && ($this->getAnnotationIndex(self::INDEX) >= 0))
                {
                    //They are index and property, return
                    return;
                }

                break;
        }

        //It didn't fall into any of the categories, must be invalid
        throw new Exception("Invalid annotation combination on {$this->name} in {$this->property->getDeclaringClass()->getName()}.");
    }
368088
private function convertClassAnnotationsToClassMetadata(
        array $classAnnotations,
        ReflectionClass $reflectionClass,
        Mapping\ClassMetadata $parent
    ) {
        switch (true) {
            case isset($classAnnotations[Annotation\Entity::class]):
                return $this->convertClassAnnotationsToEntityClassMetadata(
                    $classAnnotations,
                    $reflectionClass,
                    $parent
                );

                break;

            case isset($classAnnotations[Annotation\MappedSuperclass::class]):
                return $this->convertClassAnnotationsToMappedSuperClassMetadata(
                    $classAnnotations,
                    $reflectionClass,
                    $parent
                );
            case isset($classAnnotations[Annotation\Embeddable::class]):
                return $this->convertClassAnnotationsToEntityClassMetadata(
                    $classAnnotations,
                    $reflectionClass,
                    $parent
                );
            default:
                throw Mapping\MappingException::classIsNotAValidEntityOrMappedSuperClass($reflectionClass->getName());
        }
    }
1264504
public static boolean matchTypeByName(String expectedType, Class<?> actualType) {
		if (expectedType == null)
			return true;
		if (actualType == null)
			throw new NullPointerException("Actual type cannot be null");

		expectedType = expectedType.toLowerCase();

		if (actualType.getName().equalsIgnoreCase(expectedType))
			return true;
		else if (actualType.getSimpleName().equalsIgnoreCase(expectedType))
			return true;
		else if (expectedType.equals("object"))
			return true;
		else if (expectedType.equals("int") || expectedType.equals("integer")) {
			return Integer.class.isAssignableFrom(actualType) || Long.class.isAssignableFrom(actualType);
		} else if (expectedType.equals("long")) {
			return Long.class.isAssignableFrom(actualType);
		} else if (expectedType.equals("float")) {
			return Float.class.isAssignableFrom(actualType) || Double.class.isAssignableFrom(actualType);
		} else if (expectedType.equals("double")) {
			return Double.class.isAssignableFrom(actualType);
		} else if (expectedType.equals("string")) {
			return String.class.isAssignableFrom(actualType);
		} else if (expectedType.equals("bool") || expectedType.equals("boolean")) {
			return Boolean.class.isAssignableFrom(actualType);
		} else if (expectedType.equals("date") || expectedType.equals("datetime")) {
			return Date.class.isAssignableFrom(actualType) || Calendar.class.isAssignableFrom(actualType)
					|| ZonedDateTime.class.isAssignableFrom(actualType);
		} else if (expectedType.equals("timespan") || expectedType.equals("duration")) {
			return Integer.class.isAssignableFrom(actualType) || Long.class.isAssignableFrom(actualType)
					|| Float.class.isAssignableFrom(actualType) || Double.class.isAssignableFrom(actualType);
		} else if (expectedType.equals("enum")) {
			return Enum.class.isAssignableFrom(actualType);
		} else if (expectedType.equals("map") || expectedType.equals("dict") || expectedType.equals("dictionary")) {
			return Map.class.isAssignableFrom(actualType);
		} else if (expectedType.equals("array") || expectedType.equals("list")) {
			return actualType.isArray() || List.class.isAssignableFrom(actualType);
		} else if (expectedType.endsWith("[]")) {
			// Todo: Check subtype
			return actualType.isArray() || List.class.isAssignableFrom(actualType);
		} else
			return false;
	}
345825
protected function assertAnnotationClass(array $annotations)
	{
		if ( !$annotations ) {
			$parameters = array((string)$this->property, $this->property->getDataType());
			$message = '@find-by must be specified in the property "%s" DocBlock or in class "%s" DocBlock';
			throw new AnnotationException(vsprintf($message, $parameters), AnnotationException::TYPE_REQUIRED);
		}

		foreach ( $annotations as $annotation ) {
			if ( !($annotation instanceof FindByAnnotation) ) {
				$parameters = array((string)$this->property, $this->property->getDataType());
				$message = '@find-by must be specified in the property "%s" DocBlock or in class "%s" DocBlock';
				throw new AnnotationException(vsprintf($message, $parameters), AnnotationException::TYPE_REQUIRED);
			}
		}
	}
157629
public function predictException(callable $capture, string $exceptionClass) 
	{
		try {
			call_user_func($capture);
			throw new PredictExceptionFailure("Capture was expected to throw '$exceptionClass' but did not.");
		} catch (\Throwable $ex) {	
			$actualExceptionClass = get_class($ex);

			if ($actualExceptionClass === PredictExceptionFailure::class) {
				throw $ex;
			}

			if ($actualExceptionClass !== $exceptionClass) {
				throw new PredictExceptionFailure("Capture threw an Exception. Expected '$exceptionClass', but got '$actualExceptionClass'.");
			}
		}
	}
1074888
public static Class<? extends Annotation> checkIsBindingAnnotation(
      Class<? extends Annotation> annotation) {
    if (!isBindingAnnotation(annotation)) {
      throw new IllegalArgumentException(
          "Must be a binding annotation (BindingAnnotation or Qualifier)");
    }
    return annotation;
  }
665464
public static function getClassAnnotations($className, $annotationClassName = null)
    {
        if(!isset(static::$annotations[$className]['Class']))
        {
            return [];
        }
        if(null === $annotationClassName)
        {
            return static::$annotations[$className]['Class'];
        }
        else
        {
            $result = [];
            foreach(static::$annotations[$className]['Class'] as $annotation)
            {
                if($annotation instanceof $annotationClassName)
                {
                    $result[] = $annotation;
                }
            }
            return $result;
        }
    }
427567
public static Matcher<Throwable> exceptionWithMessage(Class<? extends Throwable> type, String expectedMessage) {
        return new ExpectedException(type, expectedMessage);
    }
37824
public static function initByPolymorphicOneToMany($model, $propertyName, $annotation)
    {
        $className = BeanFactory::getObjectClass($model);

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

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

        $model->$propertyName = new ArrayList($modelClass);
        if(null !== $model->$leftField)
        {
            $list = $modelClass::query()->where($annotation->type, '=', $annotation->typeValue)->where($rightField, '=', $model->$leftField)->select()->getArray();
            if(null !== $list)
            {
                $model->$propertyName->append(...$list);
            }
        }
    }
226214
public static function collect(string $className, $objectAnnotation = null, string $propertyName = "", string $methodName = "", $propertyValue = null)
    {
        if ($objectAnnotation instanceof Breaker) {
            $breakerName = $objectAnnotation->getName();
            $breakerName = empty($breakerName) ? $className : $breakerName;

            self::$breakers[$breakerName] = $className;
        }
    }