io.twipple.springframework.data.clusterpoint.convert.DefaultClusterpointEntityConverter.java Source code

Java tutorial

Introduction

Here is the source code for io.twipple.springframework.data.clusterpoint.convert.DefaultClusterpointEntityConverter.java

Source

/*
 * 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);
        }
    }

}