code: private static void populateTypeArgs(ParameterizedType type, Map<TypeVariable<?>, Type> map, boolean depthFirst) {
      if (type.getRawType() instanceof Class) {
        TypeVariable<?>[] typeVariables = ((Class<?>) type.getRawType()).getTypeParameters();
        Type[] typeArguments = type.getActualTypeArguments();
  
        if (type.getOwnerType() != null) {
          Type owner = type.getOwnerType();
          if (owner instanceof ParameterizedType)
            populateTypeArgs((ParameterizedType) owner, map, depthFirst);
        }
  
        for (int i = 0; i < typeArguments.length; i++) {
          TypeVariable<?> variable = typeVariables[i];
          Type typeArgument = typeArguments[i];
  
          if (typeArgument instanceof Class) {
            map.put(variable, typeArgument);
          } else if (typeArgument instanceof GenericArrayType) {
            map.put(variable, typeArgument);
          } else if (typeArgument instanceof ParameterizedType) {
            map.put(variable, typeArgument);
          } else if (typeArgument instanceof TypeVariable) {
            TypeVariable<?> typeVariableArgument = (TypeVariable<?>) typeArgument;
            if (depthFirst) {
              Type existingType = map.get(variable);
              if (existingType != null) {
                map.put(typeVariableArgument, existingType);
                continue;
              }
            }
  
            Type resolvedType = map.get(typeVariableArgument);
            if (resolvedType == null)
              resolvedType = resolveBound(typeVariableArgument);
            map.put(variable, resolvedType);
          }
        }
      }
    }
docs: Populates the {@code map} with variable/argument pairs for the given {@code type}.