package com.anjlab.eclipse.tapestry5.internal.visitors; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; import org.apache.commons.lang3.StringUtils; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jdt.core.IAnnotatable; import org.eclipse.jdt.core.IAnnotation; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.JavaModelException; import com.anjlab.eclipse.tapestry5.Activator; import com.anjlab.eclipse.tapestry5.DeclarationReference.JavaElementReference; import com.anjlab.eclipse.tapestry5.EclipseUtils; import com.anjlab.eclipse.tapestry5.ObjectCallback; import com.anjlab.eclipse.tapestry5.TapestryModule; import com.anjlab.eclipse.tapestry5.TapestryService; import com.anjlab.eclipse.tapestry5.TapestryService.InstrumenterType; import com.anjlab.eclipse.tapestry5.TapestryService.Matcher; import com.anjlab.eclipse.tapestry5.TapestryService.ServiceDefinition; import com.anjlab.eclipse.tapestry5.TapestryService.ServiceInstrumenter; import com.anjlab.eclipse.tapestry5.TapestryUtils; import com.anjlab.eclipse.tapestry5.internal.AndMatcher; import com.anjlab.eclipse.tapestry5.internal.GlobPatternMatcher; import com.anjlab.eclipse.tapestry5.internal.IdentityIdMatcher; import com.anjlab.eclipse.tapestry5.internal.MarkerMatcher; import com.anjlab.eclipse.tapestry5.internal.OrMatcher; import com.anjlab.eclipse.tapestry5.internal.ServiceIntfMatcher; import com.anjlab.eclipse.tapestry5.internal.TapestryModuleMatcher; public class TapestryServiceDiscovery { private final IProgressMonitor monitor; private final TapestryModule tapestryModule; private final ObjectCallback<TapestryService, RuntimeException> serviceFound; private final ObjectCallback<ServiceInstrumenter, RuntimeException> advisorFound; private final ObjectCallback<ServiceInstrumenter, RuntimeException> contributorFound; private final ObjectCallback<ServiceInstrumenter, RuntimeException> decoratorFound; public TapestryServiceDiscovery( IProgressMonitor monitor, TapestryModule tapestryModule, ObjectCallback<TapestryService, RuntimeException> serviceFound, ObjectCallback<ServiceInstrumenter, RuntimeException> advisorFound, ObjectCallback<ServiceInstrumenter, RuntimeException> contributorFound, ObjectCallback<ServiceInstrumenter, RuntimeException> decoratorFound) { this.monitor = monitor; this.tapestryModule = tapestryModule; this.serviceFound = serviceFound; this.advisorFound = advisorFound; this.contributorFound = contributorFound; this.decoratorFound = decoratorFound; } public void run() { try { for (IMethod method : tapestryModule.getModuleClass().getMethods()) { if (monitor.isCanceled()) { return; } try { if (TapestryUtils.isServiceBuilderMethod(method)) { addServiceFromBuilderMethod(method); } else if (TapestryUtils.isDecoratorMethod(method)) { addServiceDecorator(method); } else if (TapestryUtils.isAdvisorMethod(method)) { addServiceAdvisor(method); } else if (TapestryUtils.isContributorMethod(method)) { addContributionMethod(method); } else if (TapestryUtils.isStartupMethod(method)) { addStartupContributor(method); } } catch (Exception e) { Activator.getDefault().logError( "Error handling method " + method.getElementName() + " for " + tapestryModule.getModuleClass().getFullyQualifiedName(), e); } } } catch (Exception e) { Activator.getDefault().logError( "Error enumerating methods for " + tapestryModule.getModuleClass().getFullyQualifiedName(), e); } } private void addStartupContributor(IMethod method) throws JavaModelException { contributorFound.callback( new ServiceInstrumenter() .setType(InstrumenterType.CONTRIBUTOR) .setId("RegistryStartup") .setReference(new JavaElementReference(tapestryModule, method)) .setServiceMatcher(new IdentityIdMatcher("RegistryStartup")) .setConstraints(extractConstraints(method))); } private void addContributionMethod(IMethod method) throws JavaModelException { IAnnotation annotation = TapestryUtils.findAnnotation(method.getAnnotations(), TapestryUtils.ORG_APACHE_TAPESTRY5_IOC_ANNOTATIONS_CONTRIBUTE); String serviceInterface = annotation != null ? EclipseUtils.readFirstValueFromAnnotation(tapestryModule.getEclipseProject(), annotation, "value") : null; String serviceSimpleId = annotation == null ? stripMethodPrefix(method, TapestryUtils.CONTRIBUTE_METHOD_NAME_PREFIX) : null; List<String> markers = extractMarkers( method, new HashSet<String>(Arrays.asList( TapestryUtils.ORG_APACHE_TAPESTRY5_IOC_ANNOTATIONS_CONTRIBUTE, TapestryUtils.ORG_APACHE_TAPESTRY5_IOC_ANNOTATIONS_ORDER, TapestryUtils.ORG_APACHE_TAPESTRY5_IOC_ANNOTATIONS_MATCH, TapestryUtils.ORG_APACHE_TAPESTRY5_IOC_ANNOTATIONS_LOCAL))); Matcher serviceMatcher = createServiceMatcherForConfigurationContribution( method, serviceSimpleId, serviceInterface, markers); String instrumenterLabel = StringUtils.isNotEmpty(serviceInterface) ? TapestryUtils.getSimpleName(serviceInterface) : serviceSimpleId; contributorFound.callback( new ServiceInstrumenter() .setType(InstrumenterType.CONTRIBUTOR) .setId(instrumenterLabel) .setReference(new JavaElementReference(tapestryModule, method)) .setServiceMatcher(serviceMatcher) .setConstraints(extractConstraints(method))); } private void addServiceAdvisor(IMethod method) throws JavaModelException { advisorFound.callback( createInstrumenter(method, TapestryUtils.ORG_APACHE_TAPESTRY5_IOC_ANNOTATIONS_ADVISE, TapestryUtils.ADVISE_METHOD_NAME_PREFIX, InstrumenterType.ADVISOR)); } private void addServiceDecorator(IMethod method) throws JavaModelException { decoratorFound.callback( createInstrumenter(method, TapestryUtils.ORG_APACHE_TAPESTRY5_IOC_ANNOTATIONS_DECORATE, TapestryUtils.DECORATE_METHOD_NAME_PREFIX, InstrumenterType.DECORATOR)); } private ServiceInstrumenter createInstrumenter( IMethod method, String instrumenterAnnotation, String methodNamePrefix, InstrumenterType instrumenterType) throws JavaModelException { IAnnotation annotation = TapestryUtils.findAnnotation(method.getAnnotations(), instrumenterAnnotation); String serviceInterface = annotation == null ? null : EclipseUtils.readFirstValueFromAnnotation(tapestryModule.getEclipseProject(), annotation, "serviceInterface"); String id = annotation == null ? stripMethodPrefix(method, methodNamePrefix) : extractId(serviceInterface, EclipseUtils.readFirstValueFromAnnotation (tapestryModule.getEclipseProject(), annotation, "id")); List<String> markers = extractMarkers( method, new HashSet<String>(Arrays.asList( instrumenterAnnotation, TapestryUtils.ORG_APACHE_TAPESTRY5_IOC_ANNOTATIONS_ORDER, TapestryUtils.ORG_APACHE_TAPESTRY5_IOC_ANNOTATIONS_MATCH))); Matcher serviceMatcher = createMatcherForInstrumenter(method, id, serviceInterface, markers); return new ServiceInstrumenter() .setType(instrumenterType) .setId(id) .setReference(new JavaElementReference(tapestryModule, method)) .setServiceMatcher(serviceMatcher) .setConstraints(extractConstraints(method)); } private String[] extractConstraints(IMethod method) throws JavaModelException { IAnnotation annotation = TapestryUtils.findAnnotation(method.getAnnotations(), TapestryUtils.ORG_APACHE_TAPESTRY5_IOC_ANNOTATIONS_ORDER); return annotation == null ? null : EclipseUtils.readValuesFromAnnotation( tapestryModule.getEclipseProject(), annotation, "value"); } private Matcher createMatcherForInstrumenter( IMethod method, String serviceId, String serviceInterface, List<String> markers) throws JavaModelException { AndMatcher matcher = new AndMatcher(); IAnnotation match = TapestryUtils.findAnnotation(method.getAnnotations(), TapestryUtils.ORG_APACHE_TAPESTRY5_IOC_ANNOTATIONS_MATCH); if (StringUtils.isNotEmpty(serviceInterface)) { matcher.add(new ServiceIntfMatcher(serviceInterface)); } if (match != null) { OrMatcher serviceIdMatcher = new OrMatcher(); String[] patterns = EclipseUtils.readValuesFromAnnotation( tapestryModule.getEclipseProject(), match, "value"); for (String pattern : patterns) { serviceIdMatcher.add(new GlobPatternMatcher(pattern)); } if (patterns.length > 0) { matcher.add(serviceIdMatcher); } } if (markers.size() > 0) { for (String marker : markers) { matcher.add(new MarkerMatcher(marker)); } } return matcher; } private Matcher createServiceMatcherForConfigurationContribution( IMethod method, String serviceId, String serviceIntf, List<String> markers) throws JavaModelException { AndMatcher matcher = new AndMatcher(); IAnnotation annotation = TapestryUtils.findAnnotation( method.getAnnotations(), TapestryUtils.ORG_APACHE_TAPESTRY5_IOC_ANNOTATIONS_LOCAL); if (annotation != null) { matcher.add(new TapestryModuleMatcher(tapestryModule)); } if (StringUtils.isNotEmpty(serviceId)) { matcher.add(new IdentityIdMatcher(serviceId)); } if (StringUtils.isNotEmpty(serviceIntf)) { matcher.add(new ServiceIntfMatcher(serviceIntf)); } if (markers.size() > 0) { for (String marker : markers) { matcher.add(new MarkerMatcher(marker)); } } return matcher; } private String extractId(String serviceInterface, String id) { return StringUtils.isNotEmpty(id) ? id : StringUtils.isNotEmpty(serviceInterface) ? TapestryUtils.getSimpleName(serviceInterface) : null; } private void addServiceFromBuilderMethod(IMethod method) throws JavaModelException { IAnnotation annotation = TapestryUtils.findAnnotation( method.getAnnotations(), TapestryUtils.ORG_APACHE_TAPESTRY5_IOC_ANNOTATIONS_SERVICE_ID); String typeName = EclipseUtils.resolveTypeNameForMember( tapestryModule.getModuleClass(), method, method.getReturnType()); final AtomicReference<String> serviceId = new AtomicReference<String>(); if (annotation != null) { serviceId.set(EclipseUtils.readFirstValueFromAnnotation( tapestryModule.getEclipseProject(), annotation, "value")); } else { String id = stripMethodPrefix(method, TapestryUtils.BUILD_METHOD_NAME_PREFIX); if (StringUtils.isEmpty(id)) { id = TapestryUtils.getSimpleName(typeName); } serviceId.set(id); } serviceFound.callback( new TapestryService( tapestryModule, new ServiceDefinition() .setIntfClass(typeName) .setId(serviceId.get()) .addMarkers(tapestryModule.markers()) .addMarkers(tapestryModule.readMarkerAnnotation(method)), new JavaElementReference(tapestryModule, method))); } private String stripMethodPrefix(IMethod method, String prefix) { return method.getElementName().substring(prefix.length()); } private List<String> extractMarkers(IAnnotatable annotatable, Set<String> skipAnnotations) throws JavaModelException { List<String> markers = new ArrayList<String>(); for (IAnnotation annotation : annotatable.getAnnotations()) { String typeName = EclipseUtils.resolveTypeName( tapestryModule.getModuleClass(), annotation.getElementName()); if (skipAnnotations.contains(typeName)) { continue; } markers.add(typeName); } return markers; } }