/* * Copyright (c) [2011-2016] "Pivotal Software, Inc." / "Neo Technology" / "Graph Aware Ltd." * * This product is licensed to you under the Apache License, Version 2.0 (the "License"). * You may not use this product except in compliance with the License. * * This product may include a number of subcomponents with * separate copyright notices and license terms. Your use of the source * code for these subcomponents is subject to the terms and * conditions of the subcomponent's license, as noted in the LICENSE file. * */ package org.springframework.data.neo4j.conversion; import org.neo4j.ogm.metadata.ClassInfo; import org.neo4j.ogm.metadata.FieldInfo; import org.neo4j.ogm.metadata.MetaData; import org.neo4j.ogm.metadata.MethodInfo; import org.neo4j.ogm.typeconversion.AttributeConverter; import org.neo4j.ogm.typeconversion.ConversionCallback; import org.neo4j.ogm.typeconversion.ProxyAttributeConverter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.convert.support.GenericConversionService; import org.springframework.core.convert.converter.Converter; import java.lang.reflect.ParameterizedType; /** * Specialisation of {@link GenericConversionService} that creates Spring-compatible converters from those known by the mapping * {@link MetaData}, allowing the OGM type converters to be reused throughout a Spring application. * * @author Adam George * @author Luanne Misquitta * @author Jasper Blues */ public class MetaDataDrivenConversionService extends GenericConversionService implements ConversionCallback { private static final Logger logger = LoggerFactory.getLogger(MetaDataDrivenConversionService.class); /** * Constructs a new {@link MetaDataDrivenConversionService} based on the given {@link MetaData}. * * @param metaData The OGM {@link MetaData} from which to elicit type converters configured in the underlying object-graph * mapping layer */ public MetaDataDrivenConversionService(MetaData metaData) { metaData.registerConversionCallback(this); for (ClassInfo classInfo : metaData.persistentEntities()) { for (FieldInfo fieldInfo : classInfo.propertyFields()) { if (fieldInfo.hasPropertyConverter()) { addWrappedConverter(fieldInfo.getPropertyConverter()); } } } } @SuppressWarnings({ "unchecked", "rawtypes" }) private void addWrappedConverter(final AttributeConverter attributeConverter) { if (attributeConverter instanceof ProxyAttributeConverter) { return; } Converter toGraphConverter = new Converter() { @Override public Object convert(Object source) { return attributeConverter.toGraphProperty(source); } }; Converter toEntityConverter = new Converter() { @Override public Object convert(Object source) { return attributeConverter.toEntityAttribute(source); } }; ParameterizedType pt = (ParameterizedType) attributeConverter.getClass().getGenericInterfaces()[0]; Class<?> sourceType, targetType; if (pt.getActualTypeArguments()[0] instanceof Class) { sourceType = (Class<?>) pt.getActualTypeArguments()[0]; } else { //the argument may be a Collection for example sourceType = (Class<?>)((ParameterizedType) pt.getActualTypeArguments()[0]).getActualTypeArguments()[0]; } if (pt.getActualTypeArguments()[1] instanceof Class) { targetType = (Class<?>) pt.getActualTypeArguments()[1]; } else { targetType = (Class<?>)((ParameterizedType) pt.getActualTypeArguments()[1]).getActualTypeArguments()[1]; } if (canConvert(sourceType, targetType) && canConvert(targetType, sourceType)) { logger.info("Not adding Spring-compatible converter for " + attributeConverter.getClass() + " because one that does the same job has already been registered with the ConversionService."); } else { // It could be argued that this is wrong as it potentially overrides a registered converted that doesn't handle // both directions, but I've decided that it's better to ensure the same converter is used for load and save. addConverter(sourceType, targetType, toGraphConverter); addConverter(targetType, sourceType, toEntityConverter); } } @Override public <T> T convert(Class<T> targetType, Object value) { if (value == null) { return null; } return convert(value, targetType); } }