private boolean isResolvedCompletely() { Type[] typeArguments = type.getActualTypeArguments(); if (typeArguments.length == 0) { return false; } for (Type typeArgument : typeArguments) { TypeInformation<?> info = createInfo(typeArgument); if (info instanceof ParameterizedTypeInformation) { if (!((ParameterizedTypeInformation<?>) info).isResolvedCompletely()) { return false; } } if (!(info instanceof ClassTypeInformation)) { return false; } } return true; }
@Override public String toString() { return String.format("%s<%s>", getType().getName(), StringUtils.collectionToCommaDelimitedString(getTypeArguments())); }
/** * Creates a new {@link ParameterizedTypeInformation} for the given {@link Type} and parent {@link TypeDiscoverer}. * * @param type must not be {@literal null} * @param parent must not be {@literal null} */ public ParameterizedTypeInformation(ParameterizedType type, TypeDiscoverer<?> parent) { super(type, parent, calculateTypeVariables(type, parent)); this.type = type; this.resolved = Lazy.of(() -> isResolvedCompletely()); }
@Override public boolean isAssignableFrom(TypeInformation<?> target) { if (this.equals(target)) { return true; } Class<T> rawType = getType(); Class<?> rawTargetType = target.getType(); if (!rawType.isAssignableFrom(rawTargetType)) { return false; } TypeInformation<?> otherTypeInformation = rawType.equals(rawTargetType) ? target : target.getSuperTypeInformation(rawType); List<TypeInformation<?>> myParameters = getTypeArguments(); List<TypeInformation<?>> typeParameters = otherTypeInformation == null ? Collections.emptyList() : otherTypeInformation.getTypeArguments(); if (myParameters.size() != typeParameters.size()) { return false; } for (int i = 0; i < myParameters.size(); i++) { if (!myParameters.get(i).isAssignableFrom(typeParameters.get(i))) { return false; } } return true; }
@Override public TypeInformation<?> getMapValueType() { if (Map.class.equals(getType())) { Type[] arguments = type.getActualTypeArguments(); return createInfo(arguments[1]); } Class<?> rawType = getType(); Set<Type> supertypes = new HashSet<Type>(); supertypes.add(rawType.getGenericSuperclass()); supertypes.addAll(Arrays.asList(rawType.getGenericInterfaces())); for (Type supertype : supertypes) { Class<?> rawSuperType = GenericTypeResolver.resolveType(supertype, getTypeVariableMap()); if (Map.class.isAssignableFrom(rawSuperType)) { ParameterizedType parameterizedSupertype = (ParameterizedType) supertype; Type[] arguments = parameterizedSupertype.getActualTypeArguments(); return createInfo(arguments[1]); } } return super.getMapValueType(); }
@Override @Nullable protected TypeInformation<?> doGetMapValueType() { if (Map.class.isAssignableFrom(getType())) { Type[] arguments = type.getActualTypeArguments(); if (arguments.length > 1) { return createInfo(arguments[1]); } } Class<?> rawType = getType(); Set<Type> supertypes = new HashSet<>(); Optional.ofNullable(rawType.getGenericSuperclass()).ifPresent(supertypes::add); supertypes.addAll(Arrays.asList(rawType.getGenericInterfaces())); Optional<TypeInformation<?>> result = supertypes.stream()// .map(it -> Pair.of(it, resolveType(it)))// .filter(it -> Map.class.isAssignableFrom(it.getSecond()))// .<TypeInformation<?>> map(it -> { ParameterizedType parameterizedSupertype = (ParameterizedType) it.getFirst(); Type[] arguments = parameterizedSupertype.getActualTypeArguments(); return createInfo(arguments[1]); }).findFirst(); return result.orElseGet(super::doGetMapValueType); }
@Override public TypeInformation<?> getComponentType() { return createInfo(type.getActualTypeArguments()[0]); }
@Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof ParameterizedTypeInformation)) { return false; } ParameterizedTypeInformation<?> that = (ParameterizedTypeInformation<?>) obj; if (this.isResolvedCompletely() && that.isResolvedCompletely()) { return this.type.equals(that.type); } return super.equals(obj); }
return new ParameterizedTypeInformation(parameterizedType, this);
@Override public boolean equals(@Nullable Object obj) { if (obj == this) { return true; } if (!(obj instanceof ParameterizedTypeInformation)) { return false; } ParameterizedTypeInformation<?> that = (ParameterizedTypeInformation<?>) obj; if (this.isResolved() && that.isResolved()) { return this.type.equals(that.type); } return super.equals(obj); }
/** * Recursively resolves the type bound to the given {@link Type} in case it's a {@link TypeVariable} and there's an * entry in the given type variables. * * @param source must not be {@literal null}. * @param variables must not be {@literal null}. * @return will never be {@literal null}. */ private static Type flattenTypeVariable(Type source, Map<TypeVariable<?>, Type> variables) { if (!(source instanceof TypeVariable)) { return source; } Type value = variables.get(source); return value == null ? source : flattenTypeVariable(value, variables); } }
@Override public boolean isAssignableFrom(TypeInformation<?> target) { if (this.equals(target)) { return true; } Class<T> rawType = getType(); Class<?> rawTargetType = target.getType(); if (!rawType.isAssignableFrom(rawTargetType)) { return false; } TypeInformation<?> otherTypeInformation = rawType.equals(rawTargetType) ? target : target .getSuperTypeInformation(rawType); List<TypeInformation<?>> myParameters = getTypeArguments(); List<TypeInformation<?>> typeParameters = otherTypeInformation.getTypeArguments(); if (myParameters.size() != typeParameters.size()) { return false; } for (int i = 0; i < myParameters.size(); i++) { if (!myParameters.get(i).isAssignableFrom(typeParameters.get(i))) { return false; } } return true; }
@Override @Nullable protected TypeInformation<?> doGetComponentType() { return createInfo(type.getActualTypeArguments()[0]); }
@Override public int hashCode() { return super.hashCode() + (isResolvedCompletely() ? this.type.hashCode() : 0); }
return new ParameterizedTypeInformation(parameterizedType, this);
@Override public int hashCode() { return isResolved() ? this.type.hashCode() : super.hashCode(); }
/** * Resolves the type variables to be used. Uses the parent's type variable map but overwrites variables locally * declared. * * @param type must not be {@literal null}. * @param parent must not be {@literal null}. * @return will never be {@literal null}. */ private static Map<TypeVariable<?>, Type> calculateTypeVariables(ParameterizedType type, TypeDiscoverer<?> parent) { Class<?> resolvedType = parent.resolveType(type); TypeVariable<?>[] typeParameters = resolvedType.getTypeParameters(); Type[] arguments = type.getActualTypeArguments(); Map<TypeVariable<?>, Type> localTypeVariables = new HashMap<>(parent.getTypeVariableMap()); IntStream.range(0, typeParameters.length) // .mapToObj(it -> Pair.of(typeParameters[it], flattenTypeVariable(arguments[it], localTypeVariables))) // .forEach(it -> localTypeVariables.put(it.getFirst(), it.getSecond())); return localTypeVariables; }
private boolean isResolvedCompletely() { Type[] types = type.getActualTypeArguments(); if (types.length == 0) { return false; } for (Type type : types) { TypeInformation<?> info = createInfo(type); if (info instanceof ParameterizedTypeInformation) { if (!((ParameterizedTypeInformation<?>) info).isResolvedCompletely()) { return false; } } if (!(info instanceof ClassTypeInformation)) { return false; } } return true; } }
@Override public List<TypeInformation<?>> getTypeArguments() { List<TypeInformation<?>> result = new ArrayList<>(); for (Type argument : type.getActualTypeArguments()) { result.add(createInfo(argument)); } return result; }
@Override @SuppressWarnings("unchecked") public TypeInformation<? extends T> specialize(ClassTypeInformation<?> type) { return isResolvedCompletely() ? (TypeInformation<? extends T>) type : super.specialize(type); }