docs: static Type parseType(String value, ResourceLoader resourceLoader) {
          value = value.trim();
          // Wildcards
          if (value.equals(WILDCARD)) {
              return WildcardTypeImpl.defaultInstance();
          }
          if (value.startsWith(WILDCARD_EXTENDS)) {
              Type upperBound = parseType(value.substring(WILDCARD_EXTENDS.length(), value.length()), resourceLoader);
              if (upperBound == null) {
                  return null;
              }
              return WildcardTypeImpl.withUpperBound(upperBound);
          }
          if (value.startsWith(WILDCARD_SUPER)) {
              Type lowerBound = parseType(value.substring(WILDCARD_SUPER.length(), value.length()), resourceLoader);
              if (lowerBound == null) {
                  return null;
              }
              return WildcardTypeImpl.withLowerBound(lowerBound);
          }
          // Array
          if (value.contains(ARRAY)) {
              Type componentType = parseType(value.substring(0, value.indexOf(ARRAY)), resourceLoader);
              if (componentType == null) {
                  return null;
              }
              return new GenericArrayTypeImpl(componentType);
          }
          int chevLeft = value.indexOf(CHEVRONS_LEFT);
          String rawValue = chevLeft < 0 ? value : value.substring(0, chevLeft);
          Class<?> rawRequiredType = tryLoadClass(rawValue, resourceLoader);
          if (rawRequiredType == null) {
              return null;
          }
          if (rawRequiredType.getTypeParameters().length == 0) {
              return rawRequiredType;
          }
          // Parameterized type
          int chevRight = value.lastIndexOf(CHEVRONS_RIGHT);
          if (chevRight < 0) {
              return null;
          }
          List<String> parts = split(value.substring(chevLeft + 1, chevRight), ',', CHEVRONS_LEFT.charAt(0), CHEVRONS_RIGHT.charAt(0));
          Type[] typeParameters = new Type[parts.size()];
          for (int i = 0; i < typeParameters.length; i++) {
              Type typeParam = parseType(parts.get(i), resourceLoader);
              if (typeParam == null) {
                  return null;
              }
              typeParameters[i] = typeParam;
          }
          return new ParameterizedTypeImpl(rawRequiredType, typeParameters);
      }