package nl.ipo.cds.attributemapping.operations.discover.annotation; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.PostConstruct; import nl.ipo.cds.attributemapping.operations.InputOperationType; import nl.ipo.cds.attributemapping.operations.OperationType; import nl.ipo.cds.attributemapping.operations.OutputOperationType; import nl.ipo.cds.attributemapping.operations.TransformOperationType; import nl.ipo.cds.attributemapping.operations.annotation.MappingOperation; import nl.ipo.cds.attributemapping.operations.discover.OperationDiscoverer; import nl.ipo.cds.attributemapping.operations.discover.OperationDiscovererException; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.MessageSource; import org.springframework.context.support.ResourceBundleMessageSource; public class AnnotationOperationDiscoverer implements OperationDiscoverer, ApplicationContextAware { private ApplicationContext applicationContext; private Collection<OperationType> operationTypes; @PostConstruct public void discoverOperations () { final Map<String, Object> candidates = getCandidateBeans (); final MessageSource messageSource = createMessageSourceForBeans (candidates); operationTypes = createOperationTypesForBeans (candidates, messageSource); } @Override public Collection<OperationType> getOperationTypes () { return Collections.unmodifiableCollection (operationTypes); } @Override public Collection<OperationType> getPublicOperationTypes () { final List<OperationType> types = new ArrayList<OperationType> (); for (final OperationType ot: operationTypes) { if (ot instanceof AnnotationOperationType && !((AnnotationOperationType)ot).isInternal ()) { types.add (ot); } } return Collections.unmodifiableCollection (types); } @Override public Collection<TransformOperationType> getTransformOperationTypes () { return null; } @Override public Collection<InputOperationType> getInputOperationTypes () { return null; } @Override public Collection<OutputOperationType> getOutputOperationTypes () { return null; } private MessageSource createMessageSourceForBeans (final Map<String, Object> beans) { final Set<String> baseNames = new HashSet<String> (); for (final Map.Entry<String, Object> entry: beans.entrySet ()) { final Object bean = entry.getValue (); final Class<?> cls = bean.getClass (); final MappingOperation annotation = cls.getAnnotation (MappingOperation.class); // Add message references from the annotation: if (annotation != null) { for (final String messageSource: annotation.messageSources ()) { baseNames.add (messageSource); } } // Add the default resource bundles: baseNames.add (cls.getCanonicalName ()); baseNames.add (String.format ("%s.messages", cls.getPackage ().getName ())); } final ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource (); messageSource.setBasenames (baseNames.toArray (new String[baseNames.size ()])); return messageSource; } private Collection<OperationType> createOperationTypesForBeans (final Map<String, Object> beans, final MessageSource messageSource) { final List<OperationType> operationTypes = new ArrayList<OperationType> (); for (final Map.Entry<String, Object> entry: beans.entrySet ()) { operationTypes.add (createOperationTypeForBean (entry.getValue (), entry.getKey (), messageSource)); } return operationTypes; } private OperationType createOperationTypeForBean (final Object bean, final String name, final MessageSource messageSource) { final Method operationMethod = AnnotationOperationType.getOperationMethod (bean); if (AnnotationOperationType.isInput (operationMethod)) { return new AnnotationInputOperationType (bean, name, messageSource); } else if (AnnotationOperationType.isOutput (operationMethod)) { return new AnnotationOutputOperationType (bean, name, messageSource); } else if (AnnotationOperationType.isTransform (operationMethod)) { return new AnnotationTransformOperationType (bean, name, messageSource); } throw new OperationDiscovererException (String.format ("Unknown operation type for %s", name)); } /** * Returns a collection of candidate beans, containing the MappingOperation. * * @return All beans in the application context having the MappingOperation annotation. */ private Map<String, Object> getCandidateBeans () { return Collections.unmodifiableMap (applicationContext.getBeansWithAnnotation (MappingOperation.class)); } @Override public void setApplicationContext (final ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } }