/** * Registers the given {@link ConvertiblePair} as reading or writing pair depending on the type sides being basic * Redis types. * * @param converterRegistration */ private void register(ConverterRegistration converterRegistration) { ConvertiblePair pair = converterRegistration.getConvertiblePair(); if (converterRegistration.isReading()) { readingPairs.add(pair); if (LOG.isWarnEnabled() && !converterRegistration.isSimpleSourceType()) { LOG.warn(String.format(READ_CONVERTER_NOT_SIMPLE, pair.getSourceType(), pair.getTargetType())); } } if (converterRegistration.isWriting()) { writingPairs.add(pair); customSimpleTypes.add(pair.getSourceType()); if (LOG.isWarnEnabled() && !converterRegistration.isSimpleTargetType()) { LOG.warn(String.format(WRITE_CONVERTER_NOT_SIMPLE, pair.getSourceType(), pair.getTargetType())); } } }
/** * Returns the target type we can readTargetWriteLocl an inject of the given source type to. The returned type might * be a subclass of the given expected type though. If {@code expectedTargetType} is {@literal null} we will simply * return the first target type matching or {@literal null} if no conversion can be found. * * @param sourceType must not be {@literal null} * @param requestedTargetType * @return */ public Class<?> getCustomWriteTarget(final Class<?> sourceType, final Class<?> requestedTargetType) { if (requestedTargetType == null) { return getCustomWriteTarget(sourceType); } return getOrCreateAndCache(new ConvertiblePair(sourceType, requestedTargetType), customWriteTargetTypes, new Producer() { @Override public Class<?> get() { return getCustomTarget(sourceType, requestedTargetType, writingPairs); } }); }
/** * Returns the actual target type for the given {@code sourceType} and {@code requestedTargetType}. Note that the * returned {@link Class} could be an assignable type to the given {@code requestedTargetType}. * * @param sourceType must not be {@literal null}. * @param requestedTargetType can be {@literal null}. * @return */ private Class<?> getCustomReadTarget(final Class<?> sourceType, final Class<?> requestedTargetType) { if (requestedTargetType == null) { return null; } return getOrCreateAndCache(new ConvertiblePair(sourceType, requestedTargetType), customReadTargetTypes, new Producer() { @Override public Class<?> get() { return getCustomTarget(sourceType, requestedTargetType, readingPairs); } }); }
/** * Inspects the given {@link ConvertiblePair}s for ones that have a source compatible type as source. Additionally * checks assignability of the target type if one is given. * * @param sourceType must not be {@literal null}. * @param requestedTargetType can be {@literal null}. * @param pairs must not be {@literal null}. * @return */ private static Class<?> getCustomTarget(Class<?> sourceType, Class<?> requestedTargetType, Collection<ConvertiblePair> pairs) { Assert.notNull(sourceType); Assert.notNull(pairs); if (requestedTargetType != null && pairs.contains(new ConvertiblePair(sourceType, requestedTargetType))) { return requestedTargetType; } for (ConvertiblePair typePair : pairs) { if (typePair.getSourceType().isAssignableFrom(sourceType)) { Class<?> targetType = typePair.getTargetType(); if (requestedTargetType == null || targetType.isAssignableFrom(requestedTargetType)) { return targetType; } } } 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 */ 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; }
private GenericConverter getRegisteredConverter(TypeDescriptor sourceType, TypeDescriptor targetType, ConvertiblePair convertiblePair) { // Check specifically registered converters ConvertersForPair convertersForPair = this.converters.get(convertiblePair); if (convertersForPair != null) { GenericConverter converter = convertersForPair.getConverter(sourceType, targetType); if (converter != null) { return converter; } } // Check ConditionalConverters for a dynamic match for (GenericConverter globalConverter : this.globalConverters) { if (((ConditionalConverter) globalConverter).matches(sourceType, targetType)) { return globalConverter; } } return null; }
/** * Create new instance registering given converters * * @param converters */ @SuppressWarnings({ "unchecked", "rawtypes" }) public CustomConversions(List converters) { this.converters = (converters != null ? new ArrayList<Object>(converters) : new ArrayList<Object>()); this.readingPairs = new HashSet<ConvertiblePair>(); this.writingPairs = new HashSet<ConvertiblePair>(); this.customSimpleTypes = new HashSet<Class<?>>(); this.simpleTypeHolder = new SimpleTypeHolder(customSimpleTypes, SolrSimpleTypes.HOLDER); this.converters.add(GeoConverters.StringToPointConverter.INSTANCE); this.converters.add(GeoConverters.Point3DToStringConverter.INSTANCE); this.converters.add(new SolrjConverters.UpdateToSolrInputDocumentConverter()); // Register Joda-Time converters only if Joda-Time was found in the classpath. if (VersionUtil.isJodaTimeAvailable()) { this.converters.add(DateTimeConverters.DateToJodaDateTimeConverter.INSTANCE); this.converters.add(DateTimeConverters.JodaDateTimeToDateConverter.INSTANCE); this.converters.add(DateTimeConverters.DateToLocalDateTimeConverter.INSTANCE); this.converters.add(DateTimeConverters.JodaLocalDateTimeToDateConverter.INSTANCE); } for (Object converter : this.converters) { registerConversion(converter); } }
Class<?> getCustomTarget(Class<?> sourceType, Class<?> expectedTargetType, Iterable<ConvertiblePair> pairs) { Assert.notNull(sourceType); Assert.notNull(pairs); ConvertiblePair expectedTypePair = new ConvertiblePair(sourceType, expectedTargetType != null ? expectedTargetType : Any.class); if (cache.containsKey(expectedTypePair)) { Class<?> cachedTargetType = cache.get(expectedTypePair); return cachedTargetType != Any.class ? cachedTargetType : null; } for (ConvertiblePair typePair : pairs) { if (typePair.getSourceType().isAssignableFrom(sourceType)) { Class<?> targetType = typePair.getTargetType(); if (expectedTargetType == null || targetType.isAssignableFrom(expectedTargetType)) { cache.putIfAbsent(expectedTypePair, targetType); return targetType; } } } cache.putIfAbsent(expectedTypePair, Any.class); return null; }
private void registerConversion(Object converter) { Class<?> type = converter.getClass(); boolean isWriting = type.isAnnotationPresent(WritingConverter.class); boolean isReading = type.isAnnotationPresent(ReadingConverter.class); if (!isReading && !isWriting) { isReading = true; isWriting = true; } if (converter instanceof GenericConverter) { GenericConverter genericConverter = (GenericConverter) converter; for (ConvertiblePair pair : genericConverter.getConvertibleTypes()) { register(new ConvertibleContext(pair, isReading, isWriting)); } } else if (converter instanceof Converter) { Class<?>[] arguments = GenericTypeResolver.resolveTypeArguments(converter.getClass(), Converter.class); register(new ConvertibleContext(arguments[0], arguments[1], isReading, isWriting)); } else { throw new IllegalArgumentException("Unsupported Converter type! Expected either GenericConverter if Converter."); } }
private GenericConverter getRegisteredConverter(TypeDescriptor sourceType, TypeDescriptor targetType, ConvertiblePair convertiblePair) { // Check specifically registered converters ConvertersForPair convertersForPair = this.converters.get(convertiblePair); if (convertersForPair != null) { GenericConverter converter = convertersForPair.getConverter(sourceType, targetType); if (converter != null) { return converter; } } // Check ConditionalGenericConverter that match all types for (GenericConverter globalConverter : this.globalConverters) { if (((ConditionalConverter)globalConverter).matches(sourceType, targetType)) { return globalConverter; } } return null; }
/** * Creates a new {@link CustomConversions} instance registering the given converters. * * @param converters */ public CustomConversions(List<?> converters) { Assert.notNull(converters); this.readingPairs = new LinkedHashSet<ConvertiblePair>(); this.writingPairs = new LinkedHashSet<ConvertiblePair>(); this.customSimpleTypes = new HashSet<Class<?>>(); this.customReadTargetTypes = new ConcurrentHashMap<ConvertiblePair, CacheValue<Class<?>>>(); this.customWriteTargetTypes = new ConcurrentHashMap<ConvertiblePair, CacheValue<Class<?>>>(); this.rawWriteTargetTypes = new ConcurrentHashMap<Class<?>, CacheValue<Class<?>>>(); List<Object> toRegister = new ArrayList<Object>(); // Add user provided converters to make sure they can override the defaults toRegister.addAll(converters); toRegister.add(new BinaryConverters.EnumToStringConverter()); toRegister.add(new BinaryConverters.StringToEnumConverterFactory()); toRegister.add(new BinaryConverters.DateToNumberConverter()); toRegister.add(new BinaryConverters.NumberToDateConverter()); for (Object c : toRegister) { registerConversion(c); } Collections.reverse(toRegister); this.converters = Collections.unmodifiableList(toRegister); this.simpleTypeHolder = new SimpleTypeHolder(customSimpleTypes, true); }
/** * Creates a new {@link ConverterRegistration}. * * @param convertiblePair must not be {@literal null}. * @param isReading whether to force to consider the converter for reading. * @param isWriting whether to force to consider the converter for reading. */ public ConverterRegistration(ConvertiblePair convertiblePair, boolean isReading, boolean isWriting) { Assert.notNull(convertiblePair); this.convertiblePair = convertiblePair; this.reading = isReading; this.writing = isWriting; }
@Override public void addConverterFactory(ConverterFactory<?, ?> converterFactory) { ResolvableType[] typeInfo = getRequiredTypeInfo(converterFactory, ConverterFactory.class); Assert.notNull(typeInfo, "Unable to the determine source type <S> and target range type R which your " + "ConverterFactory<S, R> converts between; declare these generic types."); addConverter(new ConverterFactoryAdapter(converterFactory, new ConvertiblePair(typeInfo[0].resolve(), typeInfo[1].resolve()))); }
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 ConvertersForPair getMatchableConverters(ConvertiblePair convertiblePair) { ConvertersForPair convertersForPair = this.converters.get(convertiblePair); if (convertersForPair == null) { convertersForPair = new ConvertersForPair(); this.converters.put(convertiblePair, convertersForPair); } return convertersForPair; }
/** * Test method for {@link io.springlets.format.EntityToStringConverter#getConvertibleTypes()}. */ @Test public void testGetConvertibleTypes() { // Prepare // Exercise Set<ConvertiblePair> convertibleTypes = converter.getConvertibleTypes(); // Validate assertThat(convertibleTypes).isNotEmpty() .containsExactly(new ConvertiblePair(Object.class, String.class)); }
@Test public void checkValidConvertibleTypes() { // Prepare // Exercise Set<ConvertiblePair> convertibleTypes = converter.getConvertibleTypes(); // Validate assertThat(convertibleTypes).isNotEmpty() .containsExactly(new ConvertiblePair(Enum.class, String.class)); }
private void register(ConvertibleContext context) { ConvertiblePair pair = context.getConvertible(); if (context.isReading()) { readingPairs.add(pair); } if (context.isWriting()) { writingPairs.add(pair); customSimpleTypes.add(pair.getSourceType()); } }
/** * Creates a new {@link ConverterRegistration}. * * @param convertiblePair must not be {@literal null}. * @param isReading whether to force to consider the converter for reading. * @param isWriting whether to force to consider the converter for reading. */ public ConverterRegistration(ConvertiblePair convertiblePair, boolean isReading, boolean isWriting) { notNull(convertiblePair, "Convertible Pair is required"); this.convertiblePair = convertiblePair; reading = isReading; writing = isWriting; }
public void addConverterFactory(ConverterFactory<?, ?> converterFactory) { GenericConverter.ConvertiblePair typeInfo = getRequiredTypeInfo(converterFactory, ConverterFactory.class); if (typeInfo == null) { throw new IllegalArgumentException("Unable to the determine sourceType <S> and " + "targetRangeType R which your ConverterFactory<S, R> converts between; " + "declare these generic types."); } addConverter(new ConverterFactoryAdapter(converterFactory, typeInfo)); }
/** * Create new instance registering given converters * * @param converters */ @SuppressWarnings({ "unchecked", "rawtypes" }) public CustomConversions(List converters) { this.converters = (converters != null ? new ArrayList<Object>(converters) : new ArrayList<Object>()); this.readingPairs = new HashSet<ConvertiblePair>(); this.writingPairs = new HashSet<ConvertiblePair>(); this.customSimpleTypes = new HashSet<Class<?>>(); this.simpleTypeHolder = new SimpleTypeHolder(customSimpleTypes, SolrSimpleTypes.HOLDER); this.converters.add(StringToGeoLocationConverter.INSTANCE); this.converters.add(GeoLocationToStringConverter.INSTANCE); this.converters.add(StringToPointConverter.INSTANCE); this.converters.add(new SolrjConverters.UpdateToSolrInputDocumentConverter()); // Register Joda-Time converters only if Joda-Time was found in the classpath. if (VersionUtil.isJodaTimeAvailable()) { this.converters.add(DateTimeConverters.DateToJodaDateTimeConverter.INSTANCE); this.converters.add(DateTimeConverters.JodaDateTimeToDateConverter.INSTANCE); this.converters.add(DateTimeConverters.DateToLocalDateTimeConverter.INSTANCE); this.converters.add(DateTimeConverters.JodaLocalDateTimeToDateConverter.INSTANCE); } for (Object converter : this.converters) { registerConversion(converter); } }
public ConverterAdapter(Converter<?, ?> converter, ResolvableType sourceType, ResolvableType targetType) { this.converter = (Converter<Object, Object>) converter; this.typeInfo = new ConvertiblePair(sourceType.resolve(Object.class), targetType.resolve(Object.class)); this.targetType = targetType; }
@Override public Set<ConvertiblePair> getConvertibleTypes() { return Collections.singleton(this.typeInfo); }
public ConverterFactoryAdapter(ConverterFactory<?, ?> converterFactory, ConvertiblePair typeInfo) { this.converterFactory = (ConverterFactory<Object, Object>) converterFactory; this.typeInfo = typeInfo; }
public void remove(Class<?> sourceType, Class<?> targetType) { this.converters.remove(new ConvertiblePair(sourceType, targetType)); }
@Override public Set<ConvertiblePair> getConvertibleTypes() { return null; }