package org.springframework.roo.classpath.persistence; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.Service; import org.springframework.roo.classpath.ItdDiscoveryService; import org.springframework.roo.classpath.TypeLocationService; import org.springframework.roo.classpath.customdata.CustomDataKeys; import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetails; import org.springframework.roo.classpath.details.FieldMetadata; import org.springframework.roo.classpath.details.MemberFindingUtils; import org.springframework.roo.classpath.details.MethodMetadata; import org.springframework.roo.classpath.scanner.MemberDetails; import org.springframework.roo.classpath.scanner.MemberDetailsScanner; import org.springframework.roo.model.JavaType; /** * This implementation of {@link PersistenceMemberLocator} scans for the * presence of persistence ID tags for {@link MemberDetails} for a given domain * type. * * @author Stefan Schmidt * @since 1.2.0 */ @Component @Service public class PersistenceMemberLocatorImpl implements PersistenceMemberLocator { @Reference private ItdDiscoveryService itdDiscoveryService; @Reference private MemberDetailsScanner memberDetailsScanner; @Reference private TypeLocationService typeLocationService; private final Map<JavaType, List<FieldMetadata>> domainTypeEmbeddedIdFieldsCache = new HashMap<JavaType, List<FieldMetadata>>(); private final Map<JavaType, MethodMetadata> domainTypeIdAccessorCache = new HashMap<JavaType, MethodMetadata>(); private final Map<JavaType, JavaType> domainTypeIdCache = new HashMap<JavaType, JavaType>(); private final Map<JavaType, List<FieldMetadata>> domainTypeIdFieldsCache = new HashMap<JavaType, List<FieldMetadata>>(); private final Map<JavaType, MethodMetadata> domainTypeVersionAccessorCache = new HashMap<JavaType, MethodMetadata>(); private final Map<JavaType, FieldMetadata> domainTypeVersionFieldCache = new HashMap<JavaType, FieldMetadata>(); public List<FieldMetadata> getEmbeddedIdentifierFields(final JavaType domainType) { updateCache(domainType); if (domainTypeEmbeddedIdFieldsCache.containsKey(domainType)) { return new ArrayList<FieldMetadata>(domainTypeEmbeddedIdFieldsCache.get(domainType)); } return new ArrayList<FieldMetadata>(); } public MethodMetadata getIdentifierAccessor(final JavaType domainType) { updateCache(domainType); return domainTypeIdAccessorCache.get(domainType); } public List<FieldMetadata> getIdentifierFields(final JavaType domainType) { updateCache(domainType); if (domainTypeIdFieldsCache.containsKey(domainType)) { return new ArrayList<FieldMetadata>(domainTypeIdFieldsCache.get(domainType)); } else if (domainTypeEmbeddedIdFieldsCache.containsKey(domainType)) { return new ArrayList<FieldMetadata>(domainTypeEmbeddedIdFieldsCache.get(domainType)); } return new ArrayList<FieldMetadata>(); } public JavaType getIdentifierType(final JavaType domainType) { updateCache(domainType); if (domainTypeIdCache.containsKey(domainType)) { return domainTypeIdCache.get(domainType); } return null; } private MemberDetails getMemberDetails(final ClassOrInterfaceTypeDetails typeDetails) { return memberDetailsScanner.getMemberDetails(getClass().getName(), typeDetails); } private MemberDetails getMemberDetails(final JavaType type) { final ClassOrInterfaceTypeDetails typeDetails = typeLocationService.getTypeDetails(type); if (typeDetails == null) { return null; } return memberDetailsScanner.getMemberDetails(getClass().getName(), typeDetails); } public MethodMetadata getVersionAccessor(final JavaType domainType) { updateCache(domainType); return domainTypeVersionAccessorCache.get(domainType); } public FieldMetadata getVersionField(final JavaType domainType) { updateCache(domainType); return domainTypeVersionFieldCache.get(domainType); } private boolean haveAssociatedTypesChanged(final JavaType javaType) { return typeLocationService.hasTypeChanged(getClass().getName(), javaType) || itdDiscoveryService.haveItdsChanged(getClass().getName(), javaType); } private void populateEmbeddedIdFields(final MemberDetails details, final JavaType type) { final List<FieldMetadata> embeddedIdFields = MemberFindingUtils.getFieldsWithTag(details, CustomDataKeys.EMBEDDED_ID_FIELD); if (!embeddedIdFields.isEmpty()) { domainTypeEmbeddedIdFieldsCache.remove(type); domainTypeEmbeddedIdFieldsCache.put(type, new ArrayList<FieldMetadata>()); final MemberDetails memberDetails = getMemberDetails(embeddedIdFields.get(0).getFieldType()); if (memberDetails != null) { for (final FieldMetadata field : memberDetails.getFields()) { if (!field.getCustomData().keySet().contains(CustomDataKeys.SERIAL_VERSION_UUID_FIELD)) { domainTypeEmbeddedIdFieldsCache.get(type).add(field); } } } } else if (domainTypeEmbeddedIdFieldsCache.containsKey(type)) { domainTypeEmbeddedIdFieldsCache.remove(type); } } private void populateIdAccessors(final MemberDetails details, final JavaType type) { final MethodMetadata idAccessor = MemberFindingUtils.getMostConcreteMethodWithTag(details, CustomDataKeys.IDENTIFIER_ACCESSOR_METHOD); if (idAccessor != null) { domainTypeIdAccessorCache.put(type, idAccessor); } else if (domainTypeIdAccessorCache.containsKey(type)) { domainTypeIdAccessorCache.remove(type); } } private void populateIdFields(final MemberDetails details, final JavaType type) { final List<FieldMetadata> idFields = MemberFindingUtils.getFieldsWithTag(details, CustomDataKeys.IDENTIFIER_FIELD); final List<FieldMetadata> embeddedIdFields = MemberFindingUtils.getFieldsWithTag(details, CustomDataKeys.EMBEDDED_ID_FIELD); if (!idFields.isEmpty()) { domainTypeIdFieldsCache.put(type, idFields); } else if (!embeddedIdFields.isEmpty()) { domainTypeIdFieldsCache.put(type, embeddedIdFields); } else if (domainTypeIdFieldsCache.containsKey(type)) { domainTypeIdFieldsCache.remove(type); } } private void populateIdTypes(final MemberDetails details, final JavaType type) { final List<FieldMetadata> idFields = MemberFindingUtils.getFieldsWithTag(details, CustomDataKeys.IDENTIFIER_FIELD); final List<FieldMetadata> embeddedIdFields = MemberFindingUtils.getFieldsWithTag(details, CustomDataKeys.EMBEDDED_ID_FIELD); if (!idFields.isEmpty()) { domainTypeIdCache.put(type, idFields.get(0).getFieldType()); } else if (!embeddedIdFields.isEmpty()) { domainTypeIdCache.put(type, embeddedIdFields.get(0).getFieldType()); } else { domainTypeIdCache.remove(type); } } private void populateVersionAccessor(final MemberDetails details, final JavaType type) { final MethodMetadata versionAccessor = MemberFindingUtils.getMostConcreteMethodWithTag(details, CustomDataKeys.VERSION_ACCESSOR_METHOD); if (versionAccessor != null) { domainTypeVersionAccessorCache.put(type, versionAccessor); } else if (domainTypeVersionAccessorCache.containsKey(type)) { domainTypeVersionAccessorCache.remove(type); } } private void populateVersionField(final MemberDetails details, final JavaType type) { final List<FieldMetadata> versionFields = MemberFindingUtils.getFieldsWithTag(details, CustomDataKeys.VERSION_FIELD); if (!versionFields.isEmpty()) { domainTypeVersionFieldCache.put(type, versionFields.get(0)); } else if (domainTypeVersionFieldCache.containsKey(type)) { domainTypeVersionFieldCache.remove(type); } } private void updateCache(final JavaType domainType) { if (!haveAssociatedTypesChanged(domainType)) { return; } final ClassOrInterfaceTypeDetails domainTypeDetails = typeLocationService.getTypeDetails(domainType); if (domainTypeDetails == null || !domainTypeDetails.getCustomData().keySet().contains(CustomDataKeys.PERSISTENT_TYPE)) { return; } final MemberDetails memberDetails = getMemberDetails(domainTypeDetails); // Update normal persistence ID fields cache populateIdTypes(memberDetails, domainType); // Update normal persistence ID cache populateIdFields(memberDetails, domainType); // Update embedded ID fields cache populateEmbeddedIdFields(memberDetails, domainType); // Update ID accessor cache populateIdAccessors(memberDetails, domainType); // Update version field cache populateVersionField(memberDetails, domainType); // Update version accessor cache populateVersionAccessor(memberDetails, domainType); } }