/* * Copyright (c) [2011-2017] "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.mapping; import org.neo4j.ogm.annotation.typeconversion.Convert; import org.neo4j.ogm.metadata.MetaData; import org.neo4j.ogm.metadata.ClassInfo; import org.neo4j.ogm.metadata.FieldInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.mapping.context.AbstractMappingContext; import org.springframework.data.mapping.model.Property; import org.springframework.data.mapping.model.SimpleTypeHolder; import org.springframework.data.util.TypeInformation; import java.beans.PropertyDescriptor; import java.lang.reflect.Field; import java.util.Objects; import static java.util.Collections.singleton; /** * This class implements Spring Data's MappingContext interface, scavenging the required data from the OGM's metadata in order * to for SDN to play nicely with Spring Data REST. * * The main thing to note is that this class is effectively a container shim for {@code ClassInfo} objects. We don't reload all * the mapping information again. * * @author Vince Bickers * @author Adam George * @author Mark Paluch * @since 4.0.0 */ public class Neo4jMappingContext extends AbstractMappingContext<Neo4jPersistentEntity<?>, Neo4jPersistentProperty> { private static final Logger logger = LoggerFactory.getLogger(Neo4jMappingContext.class); private final MetaData metaData; /** * Constructs a new {@link Neo4jMappingContext} based on the persistent entities in the given {@link MetaData}. * * @param metaData The OGM {@link MetaData} from which to extract the persistent entities */ public Neo4jMappingContext(MetaData metaData) { this.metaData = metaData; metaData.persistentEntities().stream().filter(k -> k.getUnderlyingClass() != null).forEach(k -> addPersistentEntity(k.getUnderlyingClass())); logger.info("Neo4jMappingContext initialisation completed"); } @Override protected <T> Neo4jPersistentEntity<?> createPersistentEntity(TypeInformation<T> typeInformation) { logger.debug("Creating Neo4jPersistentEntity from type information: {}", typeInformation); return new Neo4jPersistentEntity<>(typeInformation); } @Override protected Neo4jPersistentProperty createPersistentProperty(Property property, Neo4jPersistentEntity<?> owner, SimpleTypeHolder simpleTypeHolder) { ClassInfo owningClassInfo = this.metaData.classInfo(owner.getType().getName()); Field propertyField = property.getField().orElse(null); if (!property.isFieldBacked() && owningClassInfo != null) { FieldInfo fieldInfo = owningClassInfo.propertyFieldByName(property.getName()); if (fieldInfo == null) { fieldInfo = owningClassInfo.relationshipFieldByName(property.getName()); } if (fieldInfo != null) { propertyField = owningClassInfo.getField(fieldInfo); } else { // there is no field, probably because descriptor gave us a field name derived from a getter logger.debug("Couldn't resolve a concrete field corresponding to property {} on {} ", property.getName(), owningClassInfo.name()); } } return new Neo4jPersistentProperty(owningClassInfo, property, owner, updateSimpleTypeHolder(simpleTypeHolder, propertyField)); } private SimpleTypeHolder updateSimpleTypeHolder(SimpleTypeHolder currentSimpleTypeHolder, Field field) { if (field == null) { return currentSimpleTypeHolder; } final Class<?> fieldType = field.getType().isArray() ? field.getType().getComponentType() : field.getType(); if (shouldUpdateSimpleTypes(currentSimpleTypeHolder, field, fieldType)) { SimpleTypeHolder updatedSimpleTypeHolder = new SimpleTypeHolder(singleton(fieldType), currentSimpleTypeHolder); setSimpleTypeHolder(updatedSimpleTypeHolder); return updatedSimpleTypeHolder; } return currentSimpleTypeHolder; } private boolean shouldUpdateSimpleTypes(SimpleTypeHolder currentSimpleTypeHolder, Field field, Class<?> rawFieldType) { if (field.isAnnotationPresent(Convert.class)) { return true; } if (currentSimpleTypeHolder.isSimpleType(rawFieldType) || rawFieldType.isInterface()) { return false; } if (this.metaData.classInfo(rawFieldType.getName()) == null) { logger.info("No class information found in OGM meta-data for {} so treating as simple type for SD Commons", rawFieldType); return true; } return false; } }