/******************************************************************************* * Copyright (c) 2011 Red Hat, Inc. * Distributed under license by Red Hat, Inc. All rights reserved. * This program is made available under the terms of the * Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Red Hat, Inc. - initial API and implementation ******************************************************************************/ package org.jboss.tools.cdi.seam.solder.core; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.CoreException; import org.eclipse.jdt.core.IMember; import org.eclipse.jdt.core.IMemberValuePair; import org.eclipse.jdt.core.IType; import org.eclipse.osgi.util.NLS; import org.jboss.tools.cdi.core.CDIConstants; import org.jboss.tools.cdi.core.CDICoreNature; import org.jboss.tools.cdi.core.CDICorePlugin; import org.jboss.tools.cdi.core.CDIUtil; import org.jboss.tools.cdi.core.IBean; import org.jboss.tools.cdi.core.ICDIProject; import org.jboss.tools.cdi.core.IClassBean; import org.jboss.tools.cdi.core.IProducer; import org.jboss.tools.cdi.core.IProducerField; import org.jboss.tools.cdi.core.IQualifierDeclaration; import org.jboss.tools.cdi.core.IRootDefinitionContext; import org.jboss.tools.cdi.core.IScope; import org.jboss.tools.cdi.core.extension.ICDIExtension; import org.jboss.tools.cdi.core.extension.feature.IAmbiguousBeanResolverFeature; import org.jboss.tools.cdi.core.extension.feature.IBeanKeyProvider; import org.jboss.tools.cdi.core.extension.feature.IBeanStoreFeature; import org.jboss.tools.cdi.core.extension.feature.IProcessAnnotatedTypeFeature; import org.jboss.tools.cdi.core.extension.feature.IValidatorFeature; import org.jboss.tools.cdi.internal.core.impl.BeanMember; import org.jboss.tools.cdi.internal.core.impl.CDIProject; import org.jboss.tools.cdi.internal.core.impl.definition.AbstractMemberDefinition; import org.jboss.tools.cdi.internal.core.impl.definition.BeanMemberDefinition; import org.jboss.tools.cdi.internal.core.impl.definition.FieldDefinition; import org.jboss.tools.cdi.internal.core.impl.definition.MethodDefinition; import org.jboss.tools.cdi.internal.core.impl.definition.TypeDefinition; import org.jboss.tools.cdi.internal.core.validation.CDICoreValidator; import org.jboss.tools.cdi.seam.solder.core.validation.SeamSolderValidationMessages; import org.jboss.tools.common.java.IAnnotationDeclaration; import org.jboss.tools.common.java.IJavaAnnotation; import org.jboss.tools.common.java.IJavaReference; import org.jboss.tools.common.java.IParametedType; import org.jboss.tools.common.java.ITypeDeclaration; import org.jboss.tools.common.java.impl.AnnotationLiteral; import org.jboss.tools.common.preferences.SeverityPreferences; import org.jboss.tools.common.text.ITextSourceReference; /** * Implements support for org.jboss.seam.solder.bean.defaultbean.DefaultBeanExtension. * * In processing annotated type adds to each bean definition, which is a default bean, * faked @Typed annotation with type set by @DefaultBean. * * In resolving ambiguous beans removes default beans out of the result set if it * contains at least one non-default bean; * * @author Viacheslav Kabanovich * */ public class CDISeamSolderDefaultBeanExtension implements ICDIExtension, IProcessAnnotatedTypeFeature, IAmbiguousBeanResolverFeature, IValidatorFeature, IBeanKeyProvider, IBeanStoreFeature { private static String ID = "org.jboss.solder.bean.defaultbean.DefaultBeanExtension"; //$NON-NLS-1$ private static String ID_30 = "org.jboss.seam.solder.bean.defaultbean.DefaultBeanExtension"; //$NON-NLS-1$ protected Map<String, Set<IBean>> defaultBeansByKey = new HashMap<String, Set<IBean>>(); public static CDISeamSolderDefaultBeanExtension getExtension(CDICoreNature project) { ICDIExtension result = project.getExtensionManager().getExtensionByRuntime(ID); if(result == null) { result = project.getExtensionManager().getExtensionByRuntime(ID_30); } if(result instanceof CDISeamSolderDefaultBeanExtension) { return (CDISeamSolderDefaultBeanExtension)result; } return null; } protected Version getVersion() { return Version.instance; } public void processAnnotatedType(TypeDefinition typeDefinition, IRootDefinitionContext context) { String defaultBeanAnnotationTypeName = getVersion().getDefaultBeanAnnotationTypeName(); boolean defaultBean = typeDefinition.isAnnotationPresent(defaultBeanAnnotationTypeName); IJavaAnnotation beanTyped = null; if(defaultBean) { beanTyped = createFakeTypedAnnotation(typeDefinition, context); if(beanTyped != null) { typeDefinition.addAnnotation(beanTyped, context); } } List<MethodDefinition> ms = typeDefinition.getMethods(); for (MethodDefinition m: ms) { if(m.isAnnotationPresent(CDIConstants.PRODUCES_ANNOTATION_TYPE_NAME)) { if(defaultBean || m.isAnnotationPresent(defaultBeanAnnotationTypeName)) { IJavaAnnotation methodTyped = createFakeTypedAnnotation(m, context); if(methodTyped != null) { m.addAnnotation(methodTyped, context); } } } } List<FieldDefinition> fs = typeDefinition.getFields(); for (FieldDefinition f: fs) { if(f.isAnnotationPresent(CDIConstants.PRODUCES_ANNOTATION_TYPE_NAME)) { if(defaultBean || f.isAnnotationPresent(defaultBeanAnnotationTypeName)) { IJavaAnnotation fieldTyped = createFakeTypedAnnotation(f, context); if(fieldTyped != null) { f.addAnnotation(fieldTyped, context); } } } } } IJavaAnnotation createFakeTypedAnnotation(AbstractMemberDefinition def, IRootDefinitionContext context) { IJavaAnnotation result = null; IAnnotationDeclaration a = def.getAnnotation(getVersion().getDefaultBeanAnnotationTypeName()); if(a != null) { Object n = a.getMemberValue(null); if(n != null && n.toString().length() > 0) { String defaultType = n.toString(); IType typedAnnotation = context.getProject().getType(CDIConstants.TYPED_ANNOTATION_TYPE_NAME); if (typedAnnotation != null) { result = new AnnotationLiteral(def.getResource(), a.getStartPosition(), a.getLength(), defaultType, IMemberValuePair.K_CLASS, typedAnnotation); } } } else if(def instanceof BeanMemberDefinition) { ITypeDeclaration type = BeanMember.getTypeDeclaration(def, context.getProject().getTypeFactory()); if(type != null) { IType typedAnnotation = context.getProject().getType(CDIConstants.TYPED_ANNOTATION_TYPE_NAME); if (typedAnnotation != null) { result = new AnnotationLiteral(def.getResource(), type.getStartPosition(), type.getLength(), type.getType().getFullyQualifiedName(), IMemberValuePair.K_CLASS, typedAnnotation); } } } return result; } public Collection<IBean> getResolvedBeans(Collection<IBean> result) { Set<IBean> defaultBeans = new HashSet<IBean>(); for (IBean b: result) { if(isBeanDefault(b)) { defaultBeans.add(b); } } if(!defaultBeans.isEmpty() && defaultBeans.size() < result.size()) { result.removeAll(defaultBeans); } return result; } public boolean isBeanDefault(IBean bean) { String defaultBeanAnnotationTypeName = getVersion().getDefaultBeanAnnotationTypeName(); if(bean.isAnnotationPresent(defaultBeanAnnotationTypeName)) { return true; } else if(bean instanceof IProducer) { IProducer producer = (IProducer)bean; IClassBean parent = producer.getClassBean(); if(parent != null && parent.isAnnotationPresent(defaultBeanAnnotationTypeName)) { return true; } } return false; } public void validateResource(IFile file, CDICoreValidator validator) { String defaultBeanAnnotationTypeName = getVersion().getDefaultBeanAnnotationTypeName(); ICDIProject cdiProject = CDICorePlugin.getCDIProject(file.getProject(), true); if(cdiProject == null) return; for (IBean bean: cdiProject.getBeans(file.getFullPath())) { if(isBeanDefault(bean)) { ITextSourceReference a = bean.getAnnotation(defaultBeanAnnotationTypeName); if(a == null) { Collection<ITypeDeclaration> ds = bean.getAllTypeDeclarations(); if(!ds.isEmpty()) { IMember e = bean instanceof IJavaReference ? ((IJavaReference)bean).getSourceMember() : bean.getBeanClass(); a = CDIUtil.convertToJavaSourceReference(ds.iterator().next(), e); } else { continue; } } if(bean instanceof IProducerField) { IClassBean cb = ((IProducerField) bean).getClassBean(); IScope scope = cb.getScope(); if(scope != null && scope.isNorlmalScope()) { validator.addError(SeamSolderValidationMessages.DEFAULT_PRODUCER_FIELD_ON_NORMAL_SCOPED_BEAN, CDISeamSolderPreferences.DEFAULT_PRODUCER_FIELD_ON_NORMAL_SCOPED_BEAN, new String[]{}, a, file); } } IQualifierDeclaration[] qs = bean.getQualifierDeclarations().toArray(new IQualifierDeclaration[0]); IParametedType type = getDefaultType(bean); if(type != null) { String key = createKey(type, bean.getQualifierDeclarations(true)); Set<IBean> linked = defaultBeansByKey.get(key); if(linked != null) { for (IBean link: linked) { if(link.getSourcePath() != null) { validator.getValidationContext().addLinkedCoreResource(CDICoreValidator.SHORT_ID, key, link.getSourcePath(), true); } } } StringBuilder otherDefaultBeans = new StringBuilder(); for (IBean b: cdiProject.getBeans(false, type, qs)) { try { if(b != bean && isBeanDefault(b) && CDIProject.areMatchingQualifiers(bean.getQualifierDeclarations(), b.getQualifierDeclarations(true))) { if(otherDefaultBeans.length() > 0) { otherDefaultBeans.append(", "); } otherDefaultBeans.append(b.getElementName()); } } catch (CoreException e) { CDISeamSolderCorePlugin.getDefault().logError(e); } } if(otherDefaultBeans.length() > 0) { String message = NLS.bind(SeamSolderValidationMessages.IDENTICAL_DEFAULT_BEANS, otherDefaultBeans); validator.addError(message, CDISeamSolderPreferences.IDENTICAL_DEFAULT_BEANS, new String[]{}, a, file); } } } } } public SeverityPreferences getSeverityPreferences() { return CDISeamSolderPreferences.getInstance(); } private IParametedType getDefaultType(IBean bean) { Collection<IParametedType> ts = bean.getLegalTypes(); if(ts.size() < 3) { for (IParametedType t: ts) { if(!"java.lang.Object".equals(t.getType().getFullyQualifiedName())) { return t; } } } return null; } @Override public String getKey(IBean bean) { if(isBeanDefault(bean)) { IParametedType type = getDefaultType(bean); if(type != null) { return createKey(type, bean.getQualifierDeclarations(true)); } } return null; } private String createKey(IParametedType type, Collection<IQualifierDeclaration> qs) { Set<String> ss = new TreeSet<String>(); for (IQualifierDeclaration q: qs) { if(!q.getTypeName().equals(CDIConstants.ANY_QUALIFIER_TYPE_NAME) && !q.getTypeName().equals(CDIConstants.DEFAULT_QUALIFIER_TYPE_NAME)) { ss.add(q.getTypeName()); } } StringBuilder sb = new StringBuilder(); sb.append("#DefaultBean_").append(type.getType().getFullyQualifiedName()); for (String s: ss) { sb.append(':').append(s); } return sb.toString(); } public synchronized void updateCaches(ICDIProject project) { Map<String, Set<IBean>> map = new HashMap<String, Set<IBean>>(); IBean[] beans = project.getBeans(); for (IBean b: beans) { String key = getKey(b); if(key != null) { Set<IBean> bs = map.get(key); if(bs == null) { bs = new HashSet<IBean>(); map.put(key, bs); } bs.add(b); } } defaultBeansByKey = map; } }