package org.springframework.roo.addon.javabean;

import java.util.LinkedHashMap;
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.osgi.service.component.ComponentContext;
import org.springframework.roo.addon.beaninfo.BeanInfoMetadataProvider;
import org.springframework.roo.classpath.PhysicalTypeDetails;
import org.springframework.roo.classpath.PhysicalTypeIdentifier;
import org.springframework.roo.classpath.PhysicalTypeMetadata;
import org.springframework.roo.classpath.TypeLocationService;
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.itd.AbstractItdMetadataProvider;
import org.springframework.roo.classpath.itd.ItdTypeDetailsProvidingMetadataItem;
import org.springframework.roo.model.JavaType;
import org.springframework.roo.project.Path;
import org.springframework.roo.project.ProjectMetadata;

/**
 * Provides {@link JavaBeanMetadata}.
 * 
 * @author Ben Alex
 * @since 1.0
 */
@Component(immediate = true) 
@Service 
public final class JavaBeanMetadataProvider extends AbstractItdMetadataProvider {
	@Reference private BeanInfoMetadataProvider beanInfoMetadataProvider;
	@Reference private TypeLocationService typeLocationService;
	
	protected void activate(ComponentContext context) {
		metadataDependencyRegistry.registerDependency(PhysicalTypeIdentifier.getMetadataIdentiferType(), getProvidesType());
		beanInfoMetadataProvider.addMetadataTrigger(new JavaType(RooJavaBean.class.getName()));
		addMetadataTrigger(new JavaType(RooJavaBean.class.getName()));
	}

	protected void deactivate(ComponentContext context) {
		metadataDependencyRegistry.deregisterDependency(PhysicalTypeIdentifier.getMetadataIdentiferType(), getProvidesType());
		beanInfoMetadataProvider.removeMetadataTrigger(new JavaType(RooJavaBean.class.getName()));
		removeMetadataTrigger(new JavaType(RooJavaBean.class.getName()));
	}

	protected ItdTypeDetailsProvidingMetadataItem getMetadata(String metadataIdentificationString, JavaType aspectName, PhysicalTypeMetadata governorPhysicalTypeMetadata, String itdFilename) {
		// Work out the MIDs of the other metadata we depend on
		ProjectMetadata projectMetadata = (ProjectMetadata) metadataService.get(ProjectMetadata.getProjectIdentifier());
		if (projectMetadata == null || !projectMetadata.isValid()) {
			return null;
		}

		Map<FieldMetadata, Boolean> declaredFields = new LinkedHashMap<FieldMetadata, Boolean>();
		PhysicalTypeDetails physicalTypeDetails = governorPhysicalTypeMetadata.getMemberHoldingTypeDetails();
		if (physicalTypeDetails != null && physicalTypeDetails instanceof ClassOrInterfaceTypeDetails) {
			ClassOrInterfaceTypeDetails governorTypeDetails = (ClassOrInterfaceTypeDetails) physicalTypeDetails;
			for (FieldMetadata field : governorTypeDetails.getDeclaredFields()) {
				declaredFields.put(field, (projectMetadata.isGaeEnabled() && isGaeInterested(field)));
			}
		}

		return new JavaBeanMetadata(metadataIdentificationString, aspectName, governorPhysicalTypeMetadata, declaredFields);
	}

	private boolean isGaeInterested(FieldMetadata field) {
		JavaType fieldType = field.getFieldType();
		if (fieldType.isCommonCollectionType()) {
			fieldType = field.getFieldType().getParameters().get(0);
		}
		
		try {
			ClassOrInterfaceTypeDetails classOrInterfaceTypeDetails = typeLocationService.getClassOrInterface(fieldType);
			return MemberFindingUtils.getTypeAnnotation(classOrInterfaceTypeDetails, new JavaType("org.springframework.roo.addon.entity.RooEntity")) != null;
		} catch (Exception e) {
			// Don't need to know what happened so just return false;
			return false;
		}
	}
	
	public String getItdUniquenessFilenameSuffix() {
		return "JavaBean";
	}

	protected String getGovernorPhysicalTypeIdentifier(String metadataIdentificationString) {
		JavaType javaType = JavaBeanMetadata.getJavaType(metadataIdentificationString);
		Path path = JavaBeanMetadata.getPath(metadataIdentificationString);
		return PhysicalTypeIdentifier.createIdentifier(javaType, path);
	}

	protected String createLocalIdentifier(JavaType javaType, Path path) {
		return JavaBeanMetadata.createIdentifier(javaType, path);
	}

	public String getProvidesType() {
		return JavaBeanMetadata.getMetadataIdentiferType();
	}
}

@@ -304,80 +304,8 @@
 xt;%0A
-import org.springframework.roo.addon.beaninfo.BeanInfoMetadataProvider;%0A
 impo
@@ -354,32 +354,32 @@
 calTypeDetails;%0A
+
 import org.sprin
@@ -1066,16 +1066,74 @@
 etadata;
+%0Aimport org.springframework.roo.project.ProjectOperations;
 %0A%0A/**%0A *
@@ -1356,57 +1356,43 @@
 ate 
-BeanInfoMetadataProvider beanInfoMetadataProvider
+ProjectOperations projectOperations
 ;%0A%09@
@@ -1628,98 +1628,8 @@
 ));%0A
-%09%09beanInfoMetadataProvider.addMetadataTrigger(new JavaType(RooJavaBean.class.getName()));%0A
 %09%09ad
@@ -1873,101 +1873,8 @@
 ));%0A
-%09%09beanInfoMetadataProvider.removeMetadataTrigger(new JavaType(RooJavaBean.class.getName()));%0A
 %09%09re
@@ -2229,46 +2229,29 @@
 a = 
-(P
+p
 roject
-Metadata) metadataService
+Operations
 .get
-(
 Proj
@@ -2265,31 +2265,9 @@
 data
-.getProjectIdentifier()
+(
 );%0A%09

Remove BeanInfoMetadata from addon-javabean