/******************************************************************************* * Copyright (c) 2010 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.internal.core.validation; import java.util.Collection; import org.eclipse.core.resources.IResource; import org.eclipse.jdt.core.JavaModelException; import org.jboss.tools.cdi.core.CDIConstants; import org.jboss.tools.cdi.core.CDICorePlugin; import org.jboss.tools.cdi.core.CDIUtil; import org.jboss.tools.cdi.core.CDIVersion; import org.jboss.tools.cdi.core.ICDIAnnotation; import org.jboss.tools.cdi.core.IInterceptorBinding; import org.jboss.tools.cdi.core.IInterceptorBindingDeclaration; import org.jboss.tools.cdi.core.IQualifier; import org.jboss.tools.cdi.core.IScope; import org.jboss.tools.cdi.core.IStereotype; import org.jboss.tools.cdi.core.IStereotypeDeclaration; import org.jboss.tools.cdi.core.preferences.CDIPreferences; import org.jboss.tools.common.java.IAnnotationDeclaration; import org.jboss.tools.common.java.IAnnotationType; /** * CDI annotation validator. * * @author Alexey Kazakov */ public class AnnotationValidationDelegate extends CDICoreValidationDelegate { public static final String ELEMENT_TYPE_TYPE_NAME = "java.lang.annotation.ElementType"; public static final String TARGET_METHOD = "METHOD"; public static final String TARGET_FIELD = "FIELD"; public static final String TARGET_PARAMETER = "PARAMETER"; public static final String TARGET_TYPE = "TYPE"; static final String[] TMF = {TARGET_METHOD, TARGET_FIELD, TARGET_TYPE}; static final String[] MF = {TARGET_METHOD, TARGET_FIELD}; static final String[][] STEREOTYPE_GENERAL_TARGET_VARAINTS = {TMF, MF, {TARGET_TYPE}, {TARGET_METHOD}, {TARGET_FIELD}}; static final String[][] QUALIFIER_GENERAL_TARGET_VARIANTS = {{TARGET_METHOD, TARGET_FIELD, TARGET_PARAMETER, TARGET_TYPE}, {TARGET_FIELD, TARGET_PARAMETER}}; static final String[][] SCOPE_GENERAL_TARGET_VARIANTS = {TMF}; static final String[][] STEREOTYPE_TMF_VARIANTS = {TMF}; static final String[][] STEREOTYPE_MF_VARIANTS = {MF}; static final String[][] STEREOTYPE_M_VARIANTS = {{TARGET_METHOD}}; static final String[][] STEREOTYPE_F_VARIANTS = {{TARGET_FIELD}}; static final String[][] TYPE_VARIANTS = {{TARGET_TYPE}}; static final String[][] TYPE__METHOD_VARIANTS = {{TARGET_TYPE, TARGET_METHOD}}; public AnnotationValidationDelegate(CDICoreValidator validator) { super(validator); } public void validateStereotypeAnnotationTypeAnnotations(IStereotype stereotype, IResource resource) throws JavaModelException { /* * Stereotype annotation type should be annotated with @Target with correct targets [JSR-299 ยง2.7.1] * Stereotype annotation type should be annotated with @Retention(RUNTIME) */ if(stereotype.getCDIProject().getVersion() == CDIVersion.CDI_1_0) { validateTargetAnnotation(stereotype, STEREOTYPE_GENERAL_TARGET_VARAINTS, CDIValidationMessages.MISSING_TARGET_ANNOTATION_IN_STEREOTYPE_TYPE[validator.getVersionIndex(stereotype)], resource, CDIValidationErrorManager.MISSING_TARGET_ANNOTATION_IN_STEREOTYPE_TYPE_ID); } /* * Stereotype annotation type should be annotated with @Retention(RUNTIME) */ validateRetentionAnnotation(stereotype, CDIValidationMessages.MISSING_RETENTION_ANNOTATION_IN_STEREOTYPE_TYPE[validator.getVersionIndex(stereotype)], resource, CDIValidationErrorManager.MISSING_RETENTION_ANNOTATION_IN_STEREOTYPE_TYPE_ID); IAnnotationDeclaration target = stereotype.getAnnotationDeclaration(CDIConstants.TARGET_ANNOTATION_TYPE_NAME); if(target!=null) { /* * 2.7.1.5. Stereotypes with additional stereotypes * - Stereotypes declared @Target(TYPE) may not be applied to stereotypes declared @Target({TYPE, METHOD, FIELD}), * @Target(METHOD), @Target(FIELD) or @Target({METHOD, FIELD}). */ for (IStereotypeDeclaration stereotypeDeclaration : stereotype.getStereotypeDeclarations()) { IStereotype superStereotype = stereotypeDeclaration.getStereotype(); if(superStereotype!=null) { Boolean result = CDIUtil.checkTargetAnnotation(superStereotype, TYPE_VARIANTS); if(result!=null && result) { result = CDIUtil.checkTargetAnnotation(target, STEREOTYPE_TMF_VARIANTS); String stName = stereotype.getSourceType().getElementName(); String superStName = superStereotype.getSourceType().getElementName(); if(result) { validator.addProblem(CDIValidationMessages.ILLEGAL_TARGET_IN_STEREOTYPE_TYPE_TMF[validator.getVersionIndex(stereotype)], CDIPreferences.MISSING_OR_INCORRECT_TARGET_OR_RETENTION_IN_ANNOTATION_TYPE, new String[]{superStName, stName}, stereotypeDeclaration, resource); continue; } result = CDIUtil.checkTargetAnnotation(target, STEREOTYPE_M_VARIANTS); if(result) { validator.addProblem(CDIValidationMessages.ILLEGAL_TARGET_IN_STEREOTYPE_TYPE_M[validator.getVersionIndex(stereotype)], CDIPreferences.MISSING_OR_INCORRECT_TARGET_OR_RETENTION_IN_ANNOTATION_TYPE, new String[]{superStName, stName}, stereotypeDeclaration, resource); continue; } result = CDIUtil.checkTargetAnnotation(target, STEREOTYPE_F_VARIANTS); if(result) { validator.addProblem(CDIValidationMessages.ILLEGAL_TARGET_IN_STEREOTYPE_TYPE_F[validator.getVersionIndex(stereotype)], CDIPreferences.MISSING_OR_INCORRECT_TARGET_OR_RETENTION_IN_ANNOTATION_TYPE, new String[]{superStName, stName}, stereotypeDeclaration, resource); continue; } result = CDIUtil.checkTargetAnnotation(target, STEREOTYPE_MF_VARIANTS); if(result) { validator.addProblem(CDIValidationMessages.ILLEGAL_TARGET_IN_STEREOTYPE_TYPE_MF[validator.getVersionIndex(stereotype)], CDIPreferences.MISSING_OR_INCORRECT_TARGET_OR_RETENTION_IN_ANNOTATION_TYPE, new String[]{superStName, stName}, stereotypeDeclaration, resource); } } } } /* * 9.1.2. Interceptor bindings for stereotypes * - If a stereotype declares interceptor bindings, it must be defined as @Target(TYPE). */ Collection<IInterceptorBindingDeclaration> interceptorBindingDeclarations = stereotype.getInterceptorBindingDeclarations(false); if(!interceptorBindingDeclarations.isEmpty() && !CDIUtil.checkTargetAnnotation(target, TYPE_VARIANTS)) { StringBuffer bindings = new StringBuffer(); boolean first = true; for (IInterceptorBindingDeclaration binding : interceptorBindingDeclarations) { if(!first) { bindings.append(", "); } bindings.append(binding.getType().getElementName()); first = false; } String stName = stereotype.getSourceType().getElementName(); validator.addProblem(CDIValidationMessages.ILLEGAL_TARGET_IN_INTERCEPTOR_BINDING_TYPE_FOR_STEREOTYPE[validator.getVersionIndex(stereotype)], CDIPreferences.MISSING_OR_INCORRECT_TARGET_OR_RETENTION_IN_ANNOTATION_TYPE, new String[]{stName, bindings.toString()}, target, resource); } } } public void validateInterceptorBindingAnnotationTypeAnnotations(IInterceptorBinding binding) throws JavaModelException { /* * 9.1.1. Interceptor binding types with additional interceptor bindings * - Interceptor binding types declared @Target(TYPE) may not be applied to interceptor binding types declared * @Target({TYPE, METHOD}). */ Collection<IInterceptorBindingDeclaration> declarations = binding.getInterceptorBindingDeclarations(false); if(!declarations.isEmpty()) { IAnnotationDeclaration target = binding.getAnnotationDeclaration(CDIConstants.TARGET_ANNOTATION_TYPE_NAME); if(target!=null) { if(CDIUtil.checkTargetAnnotation(target, TYPE__METHOD_VARIANTS)) { for (IInterceptorBindingDeclaration declaration : declarations) { IAnnotationType superBinding = declaration.getAnnotation(); Boolean result = CDIUtil.checkTargetAnnotation(superBinding, TYPE_VARIANTS); if(result!=null && result) { validator.addProblem(CDIValidationMessages.ILLEGAL_TARGET_IN_INTERCEPTOR_BINDING_TYPE[validator.getVersionIndex(binding)], CDIPreferences.MISSING_OR_INCORRECT_TARGET_OR_RETENTION_IN_ANNOTATION_TYPE, new String[]{superBinding.getSourceType().getElementName(), binding.getSourceType().getElementName()}, declaration, binding.getResource()); } } } } } } /** * Validates a scope type. * * @param qualifier */ public void validateScopeType(IScope scope) { if(scope==null) { return; } IResource resource = scope.getResource(); if(!validator.shouldValidateResourceOfElement(resource)) { return; } try { validateScopeAnnotationTypeAnnotations(scope, resource); } catch (JavaModelException e) { CDICorePlugin.getDefault().logError(e); } } private void validateScopeAnnotationTypeAnnotations(IScope scope, IResource resource) throws JavaModelException { /* * Scope annotation type should be annotated with @Target({TYPE, METHOD, FIELD}) */ if(scope.getCDIProject().getVersion() == CDIVersion.CDI_1_0) { validateTargetAnnotation(scope, SCOPE_GENERAL_TARGET_VARIANTS, CDIValidationMessages.MISSING_TARGET_ANNOTATION_IN_SCOPE_TYPE[validator.getVersionIndex(scope)], resource, CDIValidationErrorManager.MISSING_TARGET_ANNOTATION_IN_SCOPE_TYPE_ID); } /* * Scope annotation type should be annotated with @Retention(RUNTIME) */ validateRetentionAnnotation(scope, CDIValidationMessages.MISSING_RETENTION_ANNOTATION_IN_SCOPE_TYPE[validator.getVersionIndex(scope)], resource, CDIValidationErrorManager.MISSING_RETENTION_ANNOTATION_IN_SCOPE_TYPE_ID); } void validateRetentionAnnotation(ICDIAnnotation type, String message, IResource resource, int message_id) throws JavaModelException { IAnnotationDeclaration retention = type.getAnnotationDeclaration(CDIConstants.RETENTION_ANNOTATION_TYPE_NAME); if(retention == null) { validator.addProblem(message, CDIPreferences.MISSING_OR_INCORRECT_TARGET_OR_RETENTION_IN_ANNOTATION_TYPE, CDIUtil.convertToSourceReference(type.getSourceType().getNameRange(), resource, type.getSourceType()), resource, message_id); } else { Object o = retention.getMemberValue(null); if(o == null || !CDIConstants.RETENTION_POLICY_RUNTIME_TYPE_NAME.equals(o.toString())) { validator.addProblem(message, CDIPreferences.MISSING_OR_INCORRECT_TARGET_OR_RETENTION_IN_ANNOTATION_TYPE, retention, resource, message_id); } } } public void validateQualifierAnnotationTypeAnnotations(IQualifier qualifier, IResource resource) throws JavaModelException { /* * Qualifier annotation type should be annotated with @Target({METHOD, FIELD, PARAMETER, TYPE}) or @Target({"FIELD", "PARAMETER"}) * Qualifier annotation type should be annotated with @Retention(RUNTIME) */ if(qualifier.getCDIProject().getVersion() == CDIVersion.CDI_1_0) { validateTargetAnnotation(qualifier, QUALIFIER_GENERAL_TARGET_VARIANTS, CDIValidationMessages.MISSING_TARGET_ANNOTATION_IN_QUALIFIER_TYPE[validator.getVersionIndex(qualifier)], resource, CDIValidationErrorManager.MISSING_TARGET_ANNOTATION_IN_QUALIFIER_TYPE_ID); } /* * Qualifier annotation type should be annotated with @Retention(RUNTIME) */ validateRetentionAnnotation(qualifier, CDIValidationMessages.MISSING_RETENTION_ANNOTATION_IN_QUALIFIER_TYPE[validator.getVersionIndex(qualifier)], resource, CDIValidationErrorManager.MISSING_RETENTION_ANNOTATION_IN_QUALIFIER_TYPE_ID); } private void validateTargetAnnotation(ICDIAnnotation annotationType, String[][] variants, String message, IResource resource, int message_id) throws JavaModelException { IAnnotationDeclaration target = annotationType.getAnnotationDeclaration(CDIConstants.TARGET_ANNOTATION_TYPE_NAME); if(target==null) { validator.addProblem(message, CDIPreferences.MISSING_OR_INCORRECT_TARGET_OR_RETENTION_IN_ANNOTATION_TYPE, CDIUtil.convertToSourceReference(annotationType.getSourceType().getNameRange(), resource, annotationType.getSourceType()), resource, message_id); } else if(!CDIUtil.checkTargetAnnotation(target, variants)) { validator.addProblem(message, CDIPreferences.MISSING_OR_INCORRECT_TARGET_OR_RETENTION_IN_ANNOTATION_TYPE, target, resource, message_id); } } }