/** * Returns an ordered class hierarchy for the given type. * @param type the type * @return an ordered list of all classes that the given type extends or implements */ private List<Class<?>> getClassHierarchy(Class<?> type) { List<Class<?>> hierarchy = new ArrayList<>(20); Set<Class<?>> visited = new HashSet<>(20); addToClassHierarchy(0, ClassUtils.resolvePrimitiveIfNecessary(type), false, hierarchy, visited); boolean array = type.isArray(); int i = 0; while (i < hierarchy.size()) { Class<?> candidate = hierarchy.get(i); candidate = (array ? candidate.getComponentType() : ClassUtils.resolvePrimitiveIfNecessary(candidate)); Class<?> superclass = candidate.getSuperclass(); if (superclass != null && superclass != Object.class && superclass != Enum.class) { addToClassHierarchy(i + 1, candidate.getSuperclass(), array, hierarchy, visited); } addInterfacesToClassHierarchy(candidate, array, hierarchy, visited); i++; } if (Enum.class.isAssignableFrom(type)) { addToClassHierarchy(hierarchy.size(), Enum.class, array, hierarchy, visited); addToClassHierarchy(hierarchy.size(), Enum.class, false, hierarchy, visited); addInterfacesToClassHierarchy(Enum.class, array, hierarchy, visited); } addToClassHierarchy(hierarchy.size(), Object.class, array, hierarchy, visited); addToClassHierarchy(hierarchy.size(), Object.class, false, hierarchy, visited); return hierarchy; }
/** * Hook method to lookup the converter for a given sourceType/targetType pair. * First queries this ConversionService's converter cache. * On a cache miss, then performs an exhaustive search for a matching converter. * If no converter matches, returns the default converter. * @param sourceType the source type to convert from * @param targetType the target type to convert to * @return the generic converter that will perform the conversion, * or {@code null} if no suitable converter was found * @see #getDefaultConverter(TypeDescriptor, TypeDescriptor) */ @Nullable protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) { ConverterCacheKey key = new ConverterCacheKey(sourceType, targetType); GenericConverter converter = this.converterCache.get(key); if (converter != null) { return (converter != NO_MATCH ? converter : null); } converter = this.converters.find(sourceType, targetType); if (converter == null) { converter = getDefaultConverter(sourceType, targetType); } if (converter != null) { this.converterCache.put(key, converter); return converter; } this.converterCache.put(key, NO_MATCH); return null; }
@Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("ConversionService converters =\n"); for (String converterString : getConverterStrings()) { builder.append('\t').append(converterString).append('\n'); } return builder.toString(); }
/** * Returns an ordered class hierarchy for the given type. * @param type the type * @return an ordered list of all classes that the given type extends or implements */ private List<Class<?>> getClassHierarchy(Class<?> type) { List<Class<?>> hierarchy = new ArrayList<>(20); Set<Class<?>> visited = new HashSet<>(20); addToClassHierarchy(0, ClassUtils.resolvePrimitiveIfNecessary(type), false, hierarchy, visited); boolean array = type.isArray(); int i = 0; while (i < hierarchy.size()) { Class<?> candidate = hierarchy.get(i); candidate = (array ? candidate.getComponentType() : ClassUtils.resolvePrimitiveIfNecessary(candidate)); Class<?> superclass = candidate.getSuperclass(); if (superclass != null && superclass != Object.class && superclass != Enum.class) { addToClassHierarchy(i + 1, candidate.getSuperclass(), array, hierarchy, visited); } addInterfacesToClassHierarchy(candidate, array, hierarchy, visited); i++; } if (Enum.class.isAssignableFrom(type)) { addToClassHierarchy(hierarchy.size(), Enum.class, array, hierarchy, visited); addToClassHierarchy(hierarchy.size(), Enum.class, false, hierarchy, visited); addInterfacesToClassHierarchy(Enum.class, array, hierarchy, visited); } addToClassHierarchy(hierarchy.size(), Object.class, array, hierarchy, visited); addToClassHierarchy(hierarchy.size(), Object.class, false, hierarchy, visited); return hierarchy; }
/** * Hook method to lookup the converter for a given sourceType/targetType pair. * First queries this ConversionService's converter cache. * On a cache miss, then performs an exhaustive search for a matching converter. * If no converter matches, returns the default converter. * @param sourceType the source type to convert from * @param targetType the target type to convert to * @return the generic converter that will perform the conversion, * or {@code null} if no suitable converter was found * @see #getDefaultConverter(TypeDescriptor, TypeDescriptor) */ @Nullable protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) { ConverterCacheKey key = new ConverterCacheKey(sourceType, targetType); GenericConverter converter = this.converterCache.get(key); if (converter != null) { return (converter != NO_MATCH ? converter : null); } converter = this.converters.find(sourceType, targetType); if (converter == null) { converter = getDefaultConverter(sourceType, targetType); } if (converter != null) { this.converterCache.put(key, converter); return converter; } this.converterCache.put(key, NO_MATCH); return null; }
/** * Hook method to lookup the converter for a given sourceType/targetType pair. * First queries this ConversionService's converter cache. * On a cache miss, then performs an exhaustive search for a matching converter. * If no converter matches, returns the default converter. * @param sourceType the source type to convert from * @param targetType the target type to convert to * @return the generic converter that will perform the conversion, * or {@code null} if no suitable converter was found * @see #getDefaultConverter(TypeDescriptor, TypeDescriptor) */ @Nullable protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) { ConverterCacheKey key = new ConverterCacheKey(sourceType, targetType); GenericConverter converter = this.converterCache.get(key); if (converter != null) { return (converter != NO_MATCH ? converter : null); } converter = this.converters.find(sourceType, targetType); if (converter == null) { converter = getDefaultConverter(sourceType, targetType); } if (converter != null) { this.converterCache.put(key, converter); return converter; } this.converterCache.put(key, NO_MATCH); return null; }
/** * Find a {@link GenericConverter} given a source and target type. * <p>This method will attempt to match all possible converters by working * through the class and interface hierarchy of the types. * @param sourceType the source type * @param targetType the target type * @return a matching {@link GenericConverter}, or {@code null} if none found */ @Nullable public GenericConverter find(TypeDescriptor sourceType, TypeDescriptor targetType) { // Search the full type hierarchy List<Class<?>> sourceCandidates = getClassHierarchy(sourceType.getType()); List<Class<?>> targetCandidates = getClassHierarchy(targetType.getType()); for (Class<?> sourceCandidate : sourceCandidates) { for (Class<?> targetCandidate : targetCandidates) { ConvertiblePair convertiblePair = new ConvertiblePair(sourceCandidate, targetCandidate); GenericConverter converter = getRegisteredConverter(sourceType, targetType, convertiblePair); if (converter != null) { return converter; } } } return null; }
/** * Returns an ordered class hierarchy for the given type. * @param type the type * @return an ordered list of all classes that the given type extends or implements */ private List<Class<?>> getClassHierarchy(Class<?> type) { List<Class<?>> hierarchy = new ArrayList<>(20); Set<Class<?>> visited = new HashSet<>(20); addToClassHierarchy(0, ClassUtils.resolvePrimitiveIfNecessary(type), false, hierarchy, visited); boolean array = type.isArray(); int i = 0; while (i < hierarchy.size()) { Class<?> candidate = hierarchy.get(i); candidate = (array ? candidate.getComponentType() : ClassUtils.resolvePrimitiveIfNecessary(candidate)); Class<?> superclass = candidate.getSuperclass(); if (superclass != null && superclass != Object.class && superclass != Enum.class) { addToClassHierarchy(i + 1, candidate.getSuperclass(), array, hierarchy, visited); } addInterfacesToClassHierarchy(candidate, array, hierarchy, visited); i++; } if (Enum.class.isAssignableFrom(type)) { addToClassHierarchy(hierarchy.size(), Enum.class, array, hierarchy, visited); addToClassHierarchy(hierarchy.size(), Enum.class, false, hierarchy, visited); addInterfacesToClassHierarchy(Enum.class, array, hierarchy, visited); } addToClassHierarchy(hierarchy.size(), Object.class, array, hierarchy, visited); addToClassHierarchy(hierarchy.size(), Object.class, false, hierarchy, visited); return hierarchy; }
/** * Find a {@link GenericConverter} given a source and target type. * <p>This method will attempt to match all possible converters by working * through the class and interface hierarchy of the types. * @param sourceType the source type * @param targetType the target type * @return a matching {@link GenericConverter}, or {@code null} if none found */ @Nullable public GenericConverter find(TypeDescriptor sourceType, TypeDescriptor targetType) { // Search the full type hierarchy List<Class<?>> sourceCandidates = getClassHierarchy(sourceType.getType()); List<Class<?>> targetCandidates = getClassHierarchy(targetType.getType()); for (Class<?> sourceCandidate : sourceCandidates) { for (Class<?> targetCandidate : targetCandidates) { ConvertiblePair convertiblePair = new ConvertiblePair(sourceCandidate, targetCandidate); GenericConverter converter = getRegisteredConverter(sourceType, targetType, convertiblePair); if (converter != null) { return converter; } } } return null; }
public void add(GenericConverter converter) { Set<ConvertiblePair> convertibleTypes = converter.getConvertibleTypes(); if (convertibleTypes == null) { Assert.state(converter instanceof ConditionalConverter, "Only conditional converters may return null convertible types"); this.globalConverters.add(converter); } else { for (ConvertiblePair convertiblePair : convertibleTypes) { ConvertersForPair convertersForPair = getMatchableConverters(convertiblePair); convertersForPair.add(converter); } } }
@Override public String toString() { return this.converters.toString(); }
public void add(GenericConverter converter) { Set<ConvertiblePair> convertibleTypes = converter.getConvertibleTypes(); if (convertibleTypes == null) { Assert.state(converter instanceof ConditionalConverter, "Only conditional converters may return null convertible types"); this.globalConverters.add(converter); } else { for (ConvertiblePair convertiblePair : convertibleTypes) { ConvertersForPair convertersForPair = getMatchableConverters(convertiblePair); convertersForPair.add(converter); } } }
private void addInterfacesToClassHierarchy(Class<?> type, boolean asArray, List<Class<?>> hierarchy, Set<Class<?>> visited) { for (Class<?> implementedInterface : type.getInterfaces()) { addToClassHierarchy(hierarchy.size(), implementedInterface, asArray, hierarchy, visited); } }
@Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("ConversionService converters =\n"); for (String converterString : getConverterStrings()) { builder.append('\t').append(converterString).append('\n'); } return builder.toString(); }
@Override public void removeConvertible(Class<?> sourceType, Class<?> targetType) { this.converters.remove(sourceType, targetType); invalidateCache(); }
@Override public void addConverter(GenericConverter converter) { this.converters.add(converter); invalidateCache(); }
@Override public void addConverter(GenericConverter converter) { this.converters.add(converter); invalidateCache(); }
@Override public void removeConvertible(Class<?> sourceType, Class<?> targetType) { this.converters.remove(sourceType, targetType); invalidateCache(); }
private void addInterfacesToClassHierarchy(Class<?> type, boolean asArray, List<Class<?>> hierarchy, Set<Class<?>> visited) { for (Class<?> implementedInterface : type.getInterfaces()) { addToClassHierarchy(hierarchy.size(), implementedInterface, asArray, hierarchy, visited); } }
@Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("ConversionService converters =\n"); for (String converterString : getConverterStrings()) { builder.append('\t').append(converterString).append('\n'); } return builder.toString(); }