Java tutorial
/* * This file is part of Spring Data Clusterpoint. * * The MIT License (MIT) * * Copyright (c) 2015 the author or authors * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package io.twipple.springframework.data.clusterpoint.convert; import io.twipple.springframework.data.clusterpoint.convert.support.CustomConversions; import io.twipple.springframework.data.clusterpoint.mapping.ClusterpointDocument; import io.twipple.springframework.data.clusterpoint.mapping.ClusterpointMappingContext; import io.twipple.springframework.data.clusterpoint.mapping.ClusterpointPersistentEntity; import io.twipple.springframework.data.clusterpoint.mapping.ClusterpointPersistentProperty; import org.springframework.beans.BeansException; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.core.convert.support.GenericConversionService; import org.springframework.data.convert.EntityConverter; import org.springframework.data.convert.EntityInstantiator; import org.springframework.data.convert.EntityInstantiators; import org.springframework.data.mapping.*; import org.springframework.data.mapping.model.*; import org.springframework.data.util.ClassTypeInformation; import org.springframework.data.util.TypeInformation; import org.springframework.util.Assert; import javax.validation.constraints.NotNull; import java.util.Collection; import java.util.Map; /** * The default implementation of the Clusterpoint {@link EntityConverter}. * * @author Olegs Briska */ public class DefaultClusterpointEntityConverter implements ClusterpointEntityConverter, InitializingBean, ApplicationContextAware { /** * The generic mapping context. */ private final ClusterpointMappingContext mappingContext; /** * Contains the conversion service. */ private final GenericConversionService conversionService; /** * Spring Expression Language context. */ private final SpELContext spELContext; /** * Contains the entity instantiators. */ private EntityInstantiators instantiators = new EntityInstantiators(); /** * Holds the custom conversions. */ private CustomConversions conversions = new CustomConversions(); /** * The overall application context. */ private ApplicationContext applicationContext; /** * The Clusterpoint specific type mapper in use. */ private ClusterpointTypeMapper typeMapper; /** * Create a new converter and hand it over the {@link ConversionService} * * @param mappingContext the mapping context to use */ public DefaultClusterpointEntityConverter(@NotNull ClusterpointMappingContext mappingContext) { Assert.notNull(mappingContext); this.mappingContext = mappingContext; conversionService = new DefaultConversionService(); typeMapper = new DefaultClusterpointTypeMapper(mappingContext); spELContext = new SpELContext(ClusterpointDocumentPropertyAccessor.INSTANCE); } @Override @NotNull public ClusterpointMappingContext getMappingContext() { return mappingContext; } /** * Return the conversion service. * * @return the conversion service. */ @Override @NotNull public ConversionService getConversionService() { return conversionService; } /** * Set the custom conversions. * * @param conversions the conversions. */ public void setCustomConversions(@NotNull CustomConversions conversions) { Assert.notNull(conversions); this.conversions = conversions; } /** * Set the entity instantiators. * * @param instantiators the instantiators. */ public void setInstantiators(@NotNull EntityInstantiators instantiators) { Assert.notNull(instantiators); this.instantiators = instantiators; } /** * Do nothing after the properties set on the bean. */ @Override public void afterPropertiesSet() { conversions.registerConvertersIn(conversionService); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } @Override @NotNull public <R> R read(@NotNull Class<R> type, @NotNull ClusterpointDocument source) { Assert.notNull(type); Assert.notNull(source); return read(ClassTypeInformation.from(type), source, null); } /** * Read an incoming {@link ClusterpointDocument} into the target entity. * * @param type the type information of the target entity. * @param source the document to convert. * @param <R> the entity type. * @return the converted entity. */ @NotNull protected <R> R read(@NotNull TypeInformation<R> type, @NotNull ClusterpointDocument source) { Assert.notNull(type); Assert.notNull(source); return read(type, source, null); } /** * Read an incoming {@link ClusterpointDocument} into the target entity. * * @param type the type information of the target entity. * @param source the document to convert. * @param parent an optional parent object. * @param <R> the entity type. * @return the converted entity. */ @NotNull @SuppressWarnings("unchecked") protected <R> R read(@NotNull TypeInformation<R> type, @NotNull ClusterpointDocument source, Object parent) { Assert.notNull(type); Assert.notNull(source); TypeInformation<? extends R> typeToUse = typeMapper.readType(source, type); Class<? extends R> rawType = typeToUse.getType(); // TODO: handle custom conversions for DOMDocument and DOMElement. // Since ClusterpointDocument is a holder of a DOMDocument, it might happen that there is a custom conversion // for either DOMDocument or the root DOMElement. if (conversions.hasCustomReadTarget(source.getClass(), rawType)) { return conversionService.convert(source, rawType); } if (typeToUse.isMap()) { return (R) readMap(typeToUse, source, parent); } if (typeToUse.isCollectionLike()) { return (R) readCollection(typeToUse, source, parent); } ClusterpointPersistentEntity<R> entity = (ClusterpointPersistentEntity<R>) mappingContext .getPersistentEntity(typeToUse); if (entity == null) { throw new MappingException("No mapping metadata found for " + rawType.getName()); } return read(entity, source, parent); } /** * Read an incoming {@link ClusterpointDocument} into the target entity. * * @param entity the target entity. * @param source the document to convert. * @param parent an optional parent object. * @param <R> the entity type. * @return the converted entity. */ @NotNull @SuppressWarnings("unchecked") protected <R> R read(@NotNull final ClusterpointPersistentEntity<R> entity, @NotNull final ClusterpointDocument source, Object parent) { Assert.notNull(entity); Assert.notNull(source); final DefaultSpELExpressionEvaluator evaluator = new DefaultSpELExpressionEvaluator(source, spELContext); ParameterValueProvider<ClusterpointPersistentProperty> parameterValueProvider = getParameterValueProvider( entity, source, evaluator, parent); final PersistentPropertyAccessor propertyAccessor = getPersistentPropertyAccessor(entity, parameterValueProvider); final PropertyValueProvider<ClusterpointPersistentProperty> propertyValueProvider = getPropertyValueProvider( source, propertyAccessor); entity.doWithProperties(new PropertyHandler<ClusterpointPersistentProperty>() { @Override public void doWithPersistentProperty(@NotNull ClusterpointPersistentProperty property) { Assert.notNull(property); if (!source.doesPropertyExist(property) || entity.isConstructorArgument(property)) { return; } Object obj = propertyValueProvider.getPropertyValue(property); setPropertyInternal(propertyAccessor, property, obj); } }); entity.doWithAssociations(new AssociationHandler<ClusterpointPersistentProperty>() { @Override public void doWithAssociation(@NotNull Association<ClusterpointPersistentProperty> association) { Assert.notNull(association); ClusterpointPersistentProperty inverseProperty = association.getInverse(); Object obj = propertyValueProvider.getPropertyValue(inverseProperty); setPropertyInternal(propertyAccessor, inverseProperty, obj); } }); return (R) propertyAccessor.getBean(); } /** * Sets the property to the given value. * This method attempts to apply custom conversions if such exist. * * @param propertyAccessor the property accessor. Must not be {@literal null}. * @param property the property being set. Must not be {@literal null}. * @param newValue the value to set. */ protected void setPropertyInternal(@NotNull PersistentPropertyAccessor propertyAccessor, @NotNull ClusterpointPersistentProperty property, Object newValue) { Assert.notNull(propertyAccessor); Assert.notNull(property); if (newValue != null) { Class<?> valueType = newValue.getClass(); Class<?> propertyType = property.getType(); if (conversions.hasCustomReadTarget(valueType, propertyType)) { newValue = conversionService.convert(newValue, propertyType); } } propertyAccessor.setProperty(property, newValue); } /** * Creates a new {@link ParameterValueProvider}. * * @param entity the persistent entity. * @param source the source document. * @param evaluator the SPEL expression evaluator. * @param parent the optional parent. * @return a new {@link ParameterValueProvider}. */ @NotNull private ParameterValueProvider<ClusterpointPersistentProperty> getParameterValueProvider( @NotNull ClusterpointPersistentEntity<?> entity, @NotNull ClusterpointDocument source, @NotNull DefaultSpELExpressionEvaluator evaluator, Object parent) { Assert.notNull(entity); Assert.notNull(source); Assert.notNull(evaluator); ClusterpointPropertyValueProvider provider = new ClusterpointPropertyValueProvider(source, evaluator, parent); PersistentEntityParameterValueProvider<ClusterpointPersistentProperty> parameterProvider = new PersistentEntityParameterValueProvider<ClusterpointPersistentProperty>( entity, provider, parent); return new ConverterAwareSpELExpressionParameterValueProvider(evaluator, conversionService, parameterProvider, parent); } /** * Creates a new {@link PersistentPropertyAccessor} for the given entity. * * @param entity the persistent entity. * @param provider the parameter value provider. * @param <R> the entity type. * @return a new {@link PersistentPropertyAccessor}. */ @NotNull private <R> PersistentPropertyAccessor getPersistentPropertyAccessor( @NotNull ClusterpointPersistentEntity<R> entity, @NotNull ParameterValueProvider<ClusterpointPersistentProperty> provider) { Assert.notNull(entity); Assert.notNull(provider); EntityInstantiator instantiator = instantiators.getInstantiatorFor(entity); R instance = instantiator.createInstance(entity, provider); return entity.getPropertyAccessor(instance); } /** * Creates a new {@link PropertyValueProvider}. * * @param source the source document. * @param propertyAccessor the property accessor. * @return a new {@link PropertyValueProvider}. */ @NotNull private PropertyValueProvider<ClusterpointPersistentProperty> getPropertyValueProvider( @NotNull ClusterpointDocument source, @NotNull PersistentPropertyAccessor propertyAccessor) { Assert.notNull(source); Assert.notNull(propertyAccessor); Object parent = propertyAccessor.getBean(); return new ClusterpointPropertyValueProvider(source, spELContext, parent); } /** * Recursively parses the a map from the source document. * * @param type the type information for the document. * @param source the source document. * @param parent the optional parent. * @return the recursively parsed map. */ @SuppressWarnings("unchecked") protected Map<Object, Object> readMap(@NotNull TypeInformation<?> type, @NotNull ClusterpointDocument source, Object parent) { Assert.notNull(type); Assert.notNull(source); /* Class<?> mapType = typeMapper.readType(source, type).getType(); Map<Object, Object> map = CollectionFactory.createMap(mapType, source.export().keySet().size()); Map<String, Object> sourceMap = source.getPayload(); for (Map.Entry<String, Object> entry : sourceMap.entrySet()) { Object key = entry.getKey(); Object value = entry.getValue(); TypeInformation<?> keyTypeInformation = type.getComponentType(); if (keyTypeInformation != null) { Class<?> keyType = keyTypeInformation.getType(); key = conversionService.convert(key, keyType); } TypeInformation<?> valueType = type.getMapValueType(); if (value instanceof ClusterpointDocument) { map.put(key, read(valueType, (ClusterpointDocument) value, parent)); } else { Class<?> valueClass = valueType == null ? null : valueType.getType(); map.put(key, getPotentiallyConvertedSimpleRead(value, valueClass)); } } return map; */ return null; } /** * Potentially convert simple values like ENUMs. * * @param value the value to convert. * @param target the target object. * @return the potentially converted object. */ @SuppressWarnings("unchecked") private Object getPotentiallyConvertedSimpleRead(Object value, Class<?> target) { if (value == null || target == null) { return value; } if (conversions.hasCustomReadTarget(value.getClass(), target)) { return conversionService.convert(value, target); } if (Enum.class.isAssignableFrom(target)) { return Enum.valueOf((Class<Enum>) target, value.toString()); } if (Class.class.isAssignableFrom(target)) { try { return Class.forName(value.toString()); } catch (ClassNotFoundException e) { throw new MappingException("Unable to create class from " + value.toString()); } } return target.isAssignableFrom(value.getClass()) ? value : conversionService.convert(value, target); } @Override public void write(Object source, ClusterpointDocument target) { /* if (source == null) { return; } boolean isCustom = conversions.getCustomWriteTarget(source.getClass(), ClusterpointDocument.class) != null; TypeInformation<?> type = ClassTypeInformation.from(source.getClass()); if (!isCustom) { typeMapper.writeType(type, target); } writeInternal(source, target, type); if (target.getId() == null) { throw new MappingException("An ID property is needed, but not found on this entity."); } */ } /** * Convert a source object into a {@link ClusterpointDocument} target. * * @param source the source object. * @param target the target document. * @param typeHint the type information for the source. */ @SuppressWarnings("unchecked") protected void writeInternal(final Object source, ClusterpointDocument target, final TypeInformation<?> typeHint) { if (source == null) { return; } Class<?> customTarget = conversions.getCustomWriteTarget(source.getClass(), ClusterpointDocument.class); if (customTarget != null) { copyClusterpointDocument(conversionService.convert(source, ClusterpointDocument.class), target); return; } if (Map.class.isAssignableFrom(source.getClass())) { writeMapInternal((Map<Object, Object>) source, target, ClassTypeInformation.MAP); return; } if (Collection.class.isAssignableFrom(source.getClass())) { throw new IllegalArgumentException("Root Document must be either CouchbaseDocument or Map."); } ClusterpointPersistentEntity<?> entity = mappingContext.getPersistentEntity(source.getClass()); writeInternal(source, target, entity); addCustomTypeKeyIfNecessary(typeHint, source, target); } /** * Helper method to copy the internals from a source document into a target document. * * @param source the source document. * @param target the target document. */ protected void copyClusterpointDocument(final ClusterpointDocument source, final ClusterpointDocument target) { /* for (Map.Entry<String, Object> entry : source.export().entrySet()) { target.put(entry.getKey(), entry.getValue()); } target.setId(source.getId()); target.setExpiration(source.getExpiration()); */ } /** * Internal helper method to write the source object into the target document. * * @param source the source object. * @param target the target document. * @param entity the persistent entity to convert from. */ protected void writeInternal(final Object source, final ClusterpointDocument target, final ClusterpointPersistentEntity<?> entity) { /* if (source == null) { return; } if (entity == null) { throw new MappingException("No mapping metadata found for entity of type " + source.getClass().getName()); } final BeanWrapper<Object> wrapper = BeanWrapper.create(source, conversionService); final ClusterpointPersistentProperty idProperty = entity.getIdProperty(); final ClusterpointPersistentProperty versionProperty = entity.getVersionProperty(); if (idProperty != null && target.getId() == null) { String id = wrapper.getProperty(idProperty, String.class); target.setId(id); } target.setExpiration(entity.getExpiry()); entity.doWithProperties(new PropertyHandler<ClusterpointPersistentProperty>() { @Override public void doWithPersistentProperty(final ClusterpointPersistentProperty prop) { if (prop.equals(idProperty) || (versionProperty != null && prop.equals(versionProperty))) { return; } Object propertyObj = wrapper.getProperty(prop, prop.getType()); if (null != propertyObj) { if (!conversions.isSimpleType(propertyObj.getClass())) { writePropertyInternal(propertyObj, target, prop); } else { writeSimpleInternal(propertyObj, target, prop.getFieldName()); } } } }); entity.doWithAssociations(new AssociationHandler<ClusterpointPersistentProperty>() { @Override public void doWithAssociation(final Association<ClusterpointPersistentProperty> association) { ClusterpointPersistentProperty inverseProp = association.getInverse(); Class<?> type = inverseProp.getType(); Object propertyObj = wrapper.getProperty(inverseProp, type); if (null != propertyObj) { writePropertyInternal(propertyObj, target, inverseProp); } } }); */ } /** * Helper method to write a property into the target document. * * @param source the source object. * @param target the target document. * @param prop the property information. */ @SuppressWarnings("unchecked") private void writePropertyInternal(final Object source, final ClusterpointDocument target, final ClusterpointPersistentProperty prop) { /* if (source == null) { return; } String name = prop.getFieldName(); TypeInformation<?> valueType = ClassTypeInformation.from(source.getClass()); TypeInformation<?> type = prop.getTypeInformation(); if (valueType.isCollectionLike()) { ClusterpointList collectionDoc = createCollection(ConversionUtils.asCollection(source), prop); target.put(name, collectionDoc); return; } if (valueType.isMap()) { ClusterpointDocument mapDoc = createMap((Map<Object, Object>) source, prop); target.put(name, mapDoc); return; } Class<?> basicTargetType = conversions.getCustomWriteTarget(source.getClass(), null); if (basicTargetType != null) { target.put(name, conversionService.convert(source, basicTargetType)); return; } ClusterpointDocument propertyDoc = new ClusterpointDocument(); addCustomTypeKeyIfNecessary(type, source, propertyDoc); ClusterpointPersistentEntity<?> entity = isSubtype(prop.getType(), source.getClass()) ? mappingContext .getPersistentEntity(source.getClass()) : mappingContext.getPersistentEntity(type); writeInternal(source, propertyDoc, entity); target.put(name, propertyDoc); */ } /** * Wrapper method to create the underlying map. * * @param map the source map. * @param prop the persistent property. * @return the written couchbase document. */ private ClusterpointDocument createMap(final Map<Object, Object> map, final ClusterpointPersistentProperty prop) { /* Assert.notNull(map, "Given map must not be null!"); Assert.notNull(prop, "PersistentProperty must not be null!"); return writeMapInternal(map, new ClusterpointDocument(), prop.getTypeInformation()); */ return null; } /** * Helper method to write the map into the couchbase document. * * @param source the source object. * @param target the target document. * @param type the type information for the document. * @return the written couchbase document. */ private ClusterpointDocument writeMapInternal(final Map<Object, Object> source, final ClusterpointDocument target, final TypeInformation<?> type) { /* for (Map.Entry<Object, Object> entry : source.entrySet()) { Object key = entry.getKey(); Object val = entry.getValue(); if (conversions.isSimpleType(key.getClass())) { String simpleKey = key.toString(); if (val == null || conversions.isSimpleType(val.getClass())) { writeSimpleInternal(val, target, simpleKey); } else if (val instanceof Collection || val.getClass().isArray()) { target.put(simpleKey, writeCollectionInternal(ConversionUtils.asCollection(val), new ClusterpointList(conversions.getSimpleTypeHolder()), type.getMapValueType())); } else { ClusterpointDocument embeddedDoc = new ClusterpointDocument(); TypeInformation<?> valueTypeInfo = type.isMap() ? type.getMapValueType() : ClassTypeInformation.OBJECT; writeInternal(val, embeddedDoc, valueTypeInfo); target.put(simpleKey, embeddedDoc); } } else { throw new MappingException("Cannot use a complex object as a key value."); } } return target; */ return null; } /** * Helper method to create the underlying collection/list. * * @param collection the collection to write. * @param prop the property information. * @return the created couchbase list. */ /* private ClusterpointList createCollection(final Collection<?> collection, final ClusterpointPersistentProperty prop) { return writeCollectionInternal(collection, new ClusterpointList(conversions.getSimpleTypeHolder()), prop.getTypeInformation()); } */ /** * Helper method to write the internal collection. * * @param source the source object. * @param target the target document. * @param type the type information for the document. * @return the created couchbase list. */ /* private ClusterpointList writeCollectionInternal(final Collection<?> source, final ClusterpointList target, final TypeInformation<?> type) { TypeInformation<?> componentType = type == null ? null : type.getComponentType(); for (Object element : source) { Class<?> elementType = element == null ? null : element.getClass(); if (elementType == null || conversions.isSimpleType(elementType)) { target.put(element); } else if (element instanceof Collection || elementType.isArray()) { target.put(writeCollectionInternal(ConversionUtils.asCollection(element), new ClusterpointList(conversions.getSimpleTypeHolder()), componentType)); } else { ClusterpointDocument embeddedDoc = new ClusterpointDocument(); writeInternal(element, embeddedDoc, componentType); target.put(embeddedDoc); } } return target; } */ /** * Read a collection from the source object. * * @param targetType the target type. * @param source the list as source. * @param parent the optional parent. * @return the instantiated collection. */ @SuppressWarnings("unchecked") private Object readCollection(final TypeInformation<?> targetType, final ClusterpointDocument source, final Object parent) { Assert.notNull(targetType); /* Class<?> collectionType = targetType.getType(); if (source.isEmpty()) { return getPotentiallyConvertedSimpleRead(new HashSet<Object>(), collectionType); } collectionType = Collection.class.isAssignableFrom(collectionType) ? collectionType : List.class; Collection<Object> items = targetType.getType().isArray() ? new ArrayList<Object>() : CollectionFactory .createCollection(collectionType, source.size(false)); TypeInformation<?> componentType = targetType.getComponentType(); Class<?> rawComponentType = componentType == null ? null : componentType.getType(); for (int i = 0; i < source.size(false); i++) { Object dbObjItem = source.get(i); if (dbObjItem instanceof ClusterpointDocument) { items.add(read(componentType, (ClusterpointDocument) dbObjItem, parent)); } else if (dbObjItem instanceof ClusterpointList) { items.add(readCollection(componentType, (ClusterpointList) dbObjItem, parent)); } else { items.add(getPotentiallyConvertedSimpleRead(dbObjItem, rawComponentType)); } } return getPotentiallyConvertedSimpleRead(items, targetType.getType()); */ return null; } /** * Write the given source into the couchbase document target. * * @param source the source object. * @param target the target document. * @param key the key of the object. */ private void writeSimpleInternal(final Object source, final ClusterpointDocument target, final String key) { /* target.put(key, getPotentiallyConvertedSimpleWrite(source)); */ } private Object getPotentiallyConvertedSimpleWrite(final Object value) { /* if (value == null) { return null; } Class<?> customTarget = conversions.getCustomWriteTarget(value.getClass(), null); if (customTarget != null) { return conversionService.convert(value, customTarget); } else { return Enum.class.isAssignableFrom(value.getClass()) ? ((Enum<?>) value).name() : value; } */ return null; } /** * Add a custom type key if needed. * * @param type the type information. * @param source th the source object. * @param target the target document. */ protected void addCustomTypeKeyIfNecessary(TypeInformation<?> type, Object source, ClusterpointDocument target) { /* TypeInformation<?> actualType = type != null ? type.getActualType() : type; Class<?> reference = actualType == null ? Object.class : actualType.getType(); boolean notTheSameClass = !source.getClass().equals(reference); if (notTheSameClass) { typeMapper.writeType(source.getClass(), target); } */ } /** * Helper method to read the value based on the value type. * * @param value the value to convert. * @param type the type information. * @param parent the optional parent. * @param <R> the target type. * @return the converted object. */ @SuppressWarnings("unchecked") private <R> R readValue(Object value, TypeInformation<?> type, Object parent) { /* Class<?> rawType = type.getType(); if (conversions.hasCustomReadTarget(value.getClass(), rawType)) { return (R) conversionService.convert(value, rawType); } else if (value instanceof ClusterpointDocument) { return (R) read(type, (ClusterpointDocument) value, parent); } else if (value instanceof ClusterpointList) { return (R) readCollection(type, (ClusterpointList) value, parent); } else { return (R) getPotentiallyConvertedSimpleRead(value, rawType); } */ return null; } /** * A property value provider for Clusterpoint documents. */ private class ClusterpointPropertyValueProvider implements PropertyValueProvider<ClusterpointPersistentProperty> { /** * The source document. */ private final ClusterpointDocument source; /** * The expression evaluator. */ private final SpELExpressionEvaluator evaluator; /** * The optional parent object. */ private final Object parent; public ClusterpointPropertyValueProvider(final ClusterpointDocument source, final SpELContext factory, final Object parent) { this(source, new DefaultSpELExpressionEvaluator(source, factory), parent); } public ClusterpointPropertyValueProvider(final ClusterpointDocument source, final DefaultSpELExpressionEvaluator evaluator, final Object parent) { Assert.notNull(source); Assert.notNull(evaluator); this.source = source; this.evaluator = evaluator; this.parent = parent; } @Override @SuppressWarnings("unchecked") public <R> R getPropertyValue(final ClusterpointPersistentProperty property) { /* String expression = property.getSpelExpression(); Object value = expression != null ? evaluator.evaluate(expression) : source.get(property.getFieldName()); if (value == null) { return null; } return (R) readValue(value, property.getTypeInformation(), parent); */ return null; } } /** * A expression parameter value provider. */ private class ConverterAwareSpELExpressionParameterValueProvider extends SpELExpressionParameterValueProvider<ClusterpointPersistentProperty> { private final Object parent; public ConverterAwareSpELExpressionParameterValueProvider(final SpELExpressionEvaluator evaluator, final ConversionService conversionService, final ParameterValueProvider<ClusterpointPersistentProperty> delegate, final Object parent) { super(evaluator, conversionService, delegate); this.parent = parent; } @Override protected <T> T potentiallyConvertSpelValue(final Object object, final PreferredConstructor.Parameter<T, ClusterpointPersistentProperty> parameter) { return readValue(object, parameter.getType(), parent); } } }