| /******************************************************************************* |
| * Copyright (c) 2009 Shane Clarke. |
| * All rights reserved. This program and the accompanying materials |
| * are 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: |
| * Shane Clarke - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jst.ws.annotations.core; |
| |
| import java.lang.annotation.Annotation; |
| import java.lang.annotation.ElementType; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.core.runtime.IConfigurationElement; |
| import org.eclipse.core.runtime.IExtensionPoint; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.jdt.core.IAnnotation; |
| import org.eclipse.jdt.core.IField; |
| import org.eclipse.jdt.core.IJavaElement; |
| import org.eclipse.jdt.core.ILocalVariable; |
| import org.eclipse.jdt.core.IMethod; |
| import org.eclipse.jdt.core.IPackageDeclaration; |
| import org.eclipse.jdt.core.IType; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.core.dom.Name; |
| import org.eclipse.jdt.core.dom.SimpleName; |
| import org.eclipse.jst.ws.annotations.core.initialization.IAnnotationAttributeInitializer; |
| |
| /** |
| * Manages the annotation categories, definitions, processors and initializers contributed through the |
| * <code>org.eclipse.jst.ws.annotations.core.annotationDefinition</code>, |
| * <code>org.eclipse.jst.ws.annotations.core.annotationCategory</code>, |
| * <code>org.eclipse.jst.ws.annotations.core.annotationInitializer</code> and |
| * <code>org.eclipse.jst.ws.annotations.core.annotationProcessor</code> extension points. |
| * <p> |
| * <strong>Provisional API:</strong> This class/interface is part of an interim API that is still under |
| * development and expected to change significantly before reaching stability. It is being made available at |
| * this early stage to solicit feedback from pioneering adopters on the understanding that any code that uses |
| * this API will almost certainly be broken (repeatedly) as the API evolves. |
| * </p> |
| */ |
| public final class AnnotationsManager { |
| private static final String ANNOTATION_DEFINITION = "annotationDefinition"; //$NON-NLS-1$ |
| private static final String ANNOTATION_CATEGORY = "annotationCategory"; //$NON-NLS-1$ |
| private static final String ANNOTATION_INITIALIZER = "annotationInitializer"; //$NON-NLS-1$ |
| private static final String ANNOTATION_PROCESSOR = "annotationProcessor"; //$NON-NLS-1$ |
| private static final String ANNOTATION = "annotation"; //$NON-NLS-1$ |
| |
| private static List<AnnotationDefinition> annotationCache = null; |
| private static Map<String, String> annotationCategoryCache = null; |
| private static Map<String, IConfigurationElement> annotationInitializerCache = null; |
| private static Map<String, List<IConfigurationElement>> annotationProcessorCache = null; |
| private static Map<String, List<AnnotationDefinition>> annotationsByCategoryMap = null; |
| private static Map<String, AnnotationDefinition> annotationClassNameToDefinitionMap; |
| private static Map<String, AnnotationDefinition> annotationSimpleNameToDefinitionMap; |
| private static Map<String, AnnotationDefinition> annotationQualifiedNameToDefinitionMap; |
| |
| private static final String ATT_ID = "id"; //$NON-NLS-1$ |
| private static final String ATT_NAME = "name"; //$NON-NLS-1$ |
| private static final String ATT_CATEGORY = "category"; //$NON-NLS-1$ |
| |
| private AnnotationsManager() { |
| } |
| |
| /** |
| * Returns a list of {@link AnnotationDefinition} constructed from contributions to the |
| * <code>org.eclipse.jst.ws.annotations.core.annotationDefinition</code> extension point. |
| * |
| * @return a list of annotation definitions. |
| */ |
| public static synchronized List<AnnotationDefinition> getAnnotations() { |
| if (annotationCache == null) { |
| annotationCache = new ArrayList<AnnotationDefinition>(); |
| |
| IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint( |
| AnnotationsCorePlugin.PLUGIN_ID, ANNOTATION_DEFINITION); |
| if (extensionPoint != null) { |
| IConfigurationElement[] elements = extensionPoint.getConfigurationElements(); |
| for (int i = 0; i < elements.length; i++) { |
| IConfigurationElement element = elements[i]; |
| if (element.getName().equals(ANNOTATION)) { |
| AnnotationDefinition annotationDefinition = new AnnotationDefinition(element, |
| getAnnotationCategory(getAttributeValue(element, ATT_CATEGORY))); |
| annotationCache.add(annotationDefinition); |
| } |
| } |
| } |
| } |
| return annotationCache; |
| } |
| |
| /** |
| * Returns a list of all the contributed annotations that target the given {@link org.eclipse.jdt.core.IJavaElement}. |
| * |
| * @param javaElement one of |
| * <li>org.eclipse.jdt.core.IPackageDeclaration</li> |
| * <li>org.eclipse.jdt.core.IType</li> |
| * <li>org.eclipse.jdt.core.IField</li> |
| * <li>org.eclipse.jdt.core.IMethod</li> |
| * <li>org.eclipse.jdt.core.ILocalVariable</li> |
| * |
| * @return a list of types which represent annotation types. |
| * @since 1.1 |
| */ |
| public static List<IType> getAnnotationTypes(IJavaElement javaElement) { |
| List<IType> annotations = new ArrayList<IType>(); |
| |
| try { |
| List<AnnotationDefinition> annotationDefinitions = getAllAnnotationsForElement(javaElement); |
| |
| filterAnnotationsList(javaElement, annotationDefinitions); |
| |
| for (AnnotationDefinition annotationDefinition : annotationDefinitions) { |
| IType type = annotationDefinition.getAnnotationType(); |
| if (type != null) { |
| annotations.add(type); |
| } |
| } |
| } catch (JavaModelException jme) { |
| AnnotationsCorePlugin.log(jme.getStatus()); |
| } |
| return annotations; |
| } |
| |
| /** |
| * Returns a list of all the contributed {@link java.lang.annotation.Annotation} that target the given java element type. |
| * |
| * @param javaElement one of |
| * <li>org.eclipse.jdt.core.IPackageDeclaration</li> |
| * <li>org.eclipse.jdt.core.IType</li> |
| * <li>org.eclipse.jdt.core.IField</li> |
| * <li>org.eclipse.jdt.core.IMethod</li> |
| * <li>org.eclipse.jdt.core.ILocalVariable</li> |
| * |
| * @return a list of annotations. |
| * @deprecated As of 1.1 replaced by {@link #getAnnotationTypes(IJavaElement)} |
| */ |
| @Deprecated |
| public static List<Class<? extends Annotation>> getAnnotations(IJavaElement javaElement) { |
| List<Class<? extends Annotation>> annotations = new ArrayList<Class<? extends Annotation>>(); |
| |
| try { |
| List<AnnotationDefinition> annotationDefinitions = getAllAnnotationsForElement(javaElement); |
| |
| filterAnnotationsList(javaElement, annotationDefinitions); |
| |
| for (AnnotationDefinition annotationDefinition : annotationDefinitions) { |
| annotations.add(annotationDefinition.getAnnotationClass()); |
| } |
| } catch (JavaModelException jme) { |
| AnnotationsCorePlugin.log(jme.getStatus()); |
| } |
| return annotations; |
| } |
| |
| private static synchronized Map<String, AnnotationDefinition> getAnnotationToClassNameDefinitionMap() { |
| if (annotationClassNameToDefinitionMap == null) { |
| List<AnnotationDefinition> annotationDefinitions = getAnnotations(); |
| |
| annotationClassNameToDefinitionMap = new HashMap<String, AnnotationDefinition>(); |
| |
| for (AnnotationDefinition annotationDefinition : annotationDefinitions) { |
| if (annotationDefinition.getAnnotationClassName() != null) { |
| annotationClassNameToDefinitionMap.put(annotationDefinition.getAnnotationClassName(), annotationDefinition); |
| } |
| } |
| } |
| return annotationClassNameToDefinitionMap; |
| } |
| |
| private static synchronized Map<String, AnnotationDefinition> getSimpleNameToDefinitionMap() { |
| if (annotationSimpleNameToDefinitionMap == null) { |
| List<AnnotationDefinition> annotationDefinitions = getAnnotations(); |
| |
| annotationSimpleNameToDefinitionMap = new HashMap<String, AnnotationDefinition>(); |
| |
| for (AnnotationDefinition annotationDefinition : annotationDefinitions) { |
| annotationSimpleNameToDefinitionMap.put(annotationDefinition.getName(), annotationDefinition); |
| } |
| } |
| return annotationSimpleNameToDefinitionMap; |
| } |
| |
| private static synchronized Map<String, AnnotationDefinition> getQualifiedNameToDefinitionMap() { |
| if (annotationQualifiedNameToDefinitionMap == null) { |
| List<AnnotationDefinition> annotationDefinitions = getAnnotations(); |
| |
| annotationQualifiedNameToDefinitionMap = new HashMap<String, AnnotationDefinition>(); |
| |
| for (AnnotationDefinition annotationDefinition : annotationDefinitions) { |
| annotationQualifiedNameToDefinitionMap.put(annotationDefinition.getAnnotationClassName(), |
| annotationDefinition); |
| } |
| } |
| return annotationQualifiedNameToDefinitionMap; |
| } |
| |
| /** |
| * Returns the {@link AnnotationDefinition} for the given {@link java.lang.annotation.Annotation} class |
| * or null if no annotation definition can be found. |
| * @param annotationClass the <code>java.lang.annotation.Annotation</code> class. |
| * @return the annotation definition for the <code>java.lang.annotation.Annotation</code> class. |
| */ |
| public static AnnotationDefinition getAnnotationDefinitionForClass(Class<? extends Annotation> annotationClass) { |
| return getAnnotationToClassNameDefinitionMap().get(annotationClass.getCanonicalName()); |
| } |
| |
| /** |
| * Returns the {@link AnnotationDefinition} for the given fully qualified {@link java.lang.annotation.Annotation} class |
| * name or null if no annotation definition can be found. |
| * @param canonicalName the fully qualified name of the <code>java.lang.annotation.Annotation</code> class. |
| * @return the annotation definition for the fully qualified <code>java.lang.annotation.Annotation</code> class name. |
| */ |
| public static AnnotationDefinition getAnnotationDefinitionForClass(String canonicalName) { |
| return getAnnotationToClassNameDefinitionMap().get(canonicalName); |
| } |
| |
| /** |
| * Returns the {@link AnnotationDefinition} for the given {@link org.eclipse.jdt.core.IType} |
| * or null if no annotation definition can be found. |
| * |
| * @param annotationType an <code>org.eclipse.jdt.core.IType</code> which represents an annotation type. |
| * @return the annotation definition for the <code>org.eclipse.jdt.core.IType</code>. |
| * @since 1.1 |
| */ |
| public static AnnotationDefinition getAnnotationDefinitionForType(IType annotationType) { |
| return getAnnotationToClassNameDefinitionMap().get(annotationType.getFullyQualifiedName()); |
| } |
| |
| /** |
| * Returns the {@link IAnnotationAttributeInitializer} for the given {@link org.eclipse.jdt.core.dom.Name} |
| * or null if none can be found. |
| * @param name a {@link org.eclipse.jdt.core.dom.SimpleName} or {@link org.eclipse.jdt.core.dom.QualifiedName} for the annotation to search for. |
| * @return an <code>IAnnotationAttributeInitializer</code> for the given name. |
| */ |
| public static IAnnotationAttributeInitializer getAnnotationAttributeInitializerForName(Name name) { |
| if (name != null) { |
| if (name.isSimpleName() && getSimpleNameToDefinitionMap().containsKey(((SimpleName) name).getIdentifier())) { |
| return getSimpleNameToDefinitionMap().get(((SimpleName) name).getIdentifier()).getAnnotationAttributeInitializer(); |
| } else if (name.isQualifiedName() && getQualifiedNameToDefinitionMap().containsKey(name.getFullyQualifiedName())) { |
| return getQualifiedNameToDefinitionMap().get(name.getFullyQualifiedName()).getAnnotationAttributeInitializer(); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Returns a list of all the {@link AnnotationDefinition} with the given annotation category name. |
| * @param categoryName the annotation category name. |
| * @return a list of annotation definitions. |
| */ |
| public static synchronized List<AnnotationDefinition> getAnnotationsByCategory(String categoryName) { |
| if (annotationsByCategoryMap == null) { |
| annotationsByCategoryMap = new HashMap<String, List<AnnotationDefinition>>(); |
| for (AnnotationDefinition annotationDefinition : getAnnotations()) { |
| |
| List<AnnotationDefinition> annotationDefinitionList = annotationsByCategoryMap.get( |
| annotationDefinition.getCategory()); |
| |
| if (annotationDefinitionList == null) { |
| annotationDefinitionList = new ArrayList<AnnotationDefinition>(); |
| annotationDefinitionList.add(annotationDefinition); |
| annotationsByCategoryMap.put(annotationDefinition.getCategory(), |
| annotationDefinitionList); |
| continue; |
| } |
| annotationDefinitionList.add(annotationDefinition); |
| } |
| } |
| return annotationsByCategoryMap.get(categoryName); |
| } |
| |
| /** |
| * Returns a list of the annotation categories. |
| * @return a list of annotation categories. |
| */ |
| public static List<String> getAnnotationCategories() { |
| return Arrays.asList(getAnnotationCategoryCache().values().toArray( |
| new String[getAnnotationCategoryCache().size()])); |
| } |
| |
| private static String getAnnotationCategory(String categoryId) { |
| return getAnnotationCategoryCache().get(categoryId); |
| } |
| |
| private static synchronized Map<String, String> getAnnotationCategoryCache() { |
| if (annotationCategoryCache != null) { |
| return annotationCategoryCache; |
| } |
| annotationCategoryCache = new HashMap<String, String>(); |
| |
| IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint( |
| AnnotationsCorePlugin.PLUGIN_ID, ANNOTATION_CATEGORY); |
| if (extensionPoint != null) { |
| IConfigurationElement[] elements = extensionPoint.getConfigurationElements(); |
| for (int i = 0; i < elements.length; i++) { |
| IConfigurationElement element = elements[i]; |
| annotationCategoryCache.put(getAttributeValue(element, ATT_ID), |
| getAttributeValue(element, ATT_NAME)); |
| } |
| } |
| return annotationCategoryCache; |
| } |
| |
| protected static synchronized Map<String, IConfigurationElement> getAnnotationInitializerCache() { |
| if (annotationInitializerCache != null) { |
| return annotationInitializerCache; |
| } |
| annotationInitializerCache = new HashMap<String, IConfigurationElement>(); |
| |
| IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint( |
| AnnotationsCorePlugin.PLUGIN_ID, ANNOTATION_INITIALIZER); |
| if (extensionPoint != null) { |
| IConfigurationElement[] elements = extensionPoint.getConfigurationElements(); |
| for (int i = 0; i < elements.length; i++) { |
| IConfigurationElement element = elements[i]; |
| annotationInitializerCache.put(getAttributeValue(element, ANNOTATION), |
| element); |
| } |
| } |
| return annotationInitializerCache; |
| } |
| |
| public static synchronized Map<String, List<IConfigurationElement>> getAnnotationProcessorsCache() { |
| if (annotationProcessorCache == null) { |
| annotationProcessorCache = new HashMap<String, List<IConfigurationElement>>(); |
| |
| IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint( |
| AnnotationsCorePlugin.PLUGIN_ID, ANNOTATION_PROCESSOR); |
| if (extensionPoint != null) { |
| IConfigurationElement[] elements = extensionPoint.getConfigurationElements(); |
| for (int i = 0; i < elements.length; i++) { |
| IConfigurationElement element = elements[i]; |
| if (element.getName().equalsIgnoreCase("processor")) { |
| String annotationKey = getAttributeValue(element, ANNOTATION); |
| List<IConfigurationElement> configurationElements = annotationProcessorCache.get( |
| annotationKey); |
| if (configurationElements == null) { |
| configurationElements = new ArrayList<IConfigurationElement>(); |
| configurationElements.add(element); |
| annotationProcessorCache.put(annotationKey, configurationElements); |
| continue; |
| } |
| configurationElements.add(element); |
| } |
| } |
| } |
| } |
| return annotationProcessorCache; |
| } |
| |
| static String getAttributeValue(IConfigurationElement configurationElement, String attributeName) { |
| String attribute = configurationElement.getAttribute(attributeName); |
| if (attribute != null) { |
| return attribute; |
| } |
| return ""; //$NON-NLS-1$ |
| } |
| |
| private static List<AnnotationDefinition> getAllAnnotationsForElement(IJavaElement javaElement) throws JavaModelException { |
| if (javaElement instanceof IPackageDeclaration) { |
| return getAnnotationsForElementType(javaElement, ElementType.PACKAGE); |
| } |
| |
| if (javaElement instanceof IType) { |
| IType type = (IType) javaElement; |
| if (type.isAnnotation()) { |
| return getAnnotationsForElementType(javaElement, ElementType.ANNOTATION_TYPE); |
| } |
| return getAnnotationsForElementType(javaElement, ElementType.TYPE); |
| } |
| |
| if (javaElement instanceof IField) { |
| return getAnnotationsForElementType(javaElement, ElementType.FIELD); |
| } |
| |
| if (javaElement instanceof IMethod) { |
| IMethod method = (IMethod) javaElement; |
| if (method.isConstructor()) { |
| return getAnnotationsForElementType(javaElement, ElementType.CONSTRUCTOR); |
| } else { |
| return getAnnotationsForElementType(javaElement, ElementType.METHOD); |
| } |
| } |
| |
| if (javaElement instanceof ILocalVariable) { |
| return getAnnotationsForElementType(javaElement, ElementType.PARAMETER); |
| } |
| |
| if (javaElement instanceof IAnnotation) { |
| return getAnnotationsForElementType(javaElement, ElementType.ANNOTATION_TYPE); |
| } |
| |
| return Collections.emptyList(); |
| } |
| |
| private static List<AnnotationDefinition> getAnnotationsForElementType(IJavaElement javaElement, ElementType elementType) { |
| List<AnnotationDefinition> annotationDefinitions = new ArrayList<AnnotationDefinition>(); |
| |
| if (annotationCache == null) { |
| getAnnotations(); |
| } |
| |
| for (AnnotationDefinition annotationDefinition : annotationCache) { |
| annotationDefinition.setJavaProject(javaElement.getJavaProject()); |
| |
| if (annotationDefinition.getAnnotationTypeTargets().contains(elementType) && |
| !annotationDefinition.isDeprecated()) { |
| annotationDefinitions.add(annotationDefinition); |
| } |
| } |
| return annotationDefinitions; |
| } |
| |
| private static void filterAnnotationsList(IJavaElement javaElement, |
| List<AnnotationDefinition> annotationDefinitions) throws JavaModelException { |
| Iterator<AnnotationDefinition> annotationIter = annotationDefinitions.iterator(); |
| while (annotationIter.hasNext()) { |
| AnnotationDefinition annotationDefinition = annotationIter.next(); |
| |
| if (javaElement instanceof IType) { |
| IType type = (IType) javaElement; |
| if (isClassRestricted(type, annotationDefinition) |
| || isInterfaceRestricted(type, annotationDefinition) |
| || isEnumRestricted(type, annotationDefinition)) { |
| annotationIter.remove(); |
| } |
| } |
| if (javaElement instanceof IMethod) { |
| IMethod method = (IMethod) javaElement; |
| if (method.isMainMethod()) { |
| annotationIter.remove(); |
| } |
| if (method.isConstructor() |
| && !annotationDefinition.getAnnotationTypeTargets().contains(ElementType.CONSTRUCTOR)) { |
| annotationIter.remove(); |
| } |
| |
| if (isClassRestricted(method, annotationDefinition) |
| || isInterfaceRestricted(method, annotationDefinition) |
| || isEnumRestricted(method, annotationDefinition)) { |
| annotationIter.remove(); |
| } |
| } |
| |
| if (javaElement instanceof IField) { |
| if(isClassRestricted(javaElement, annotationDefinition) |
| || isInterfaceRestricted(javaElement, annotationDefinition) |
| ||isEnumRestricted(javaElement, annotationDefinition)) { |
| annotationIter.remove(); |
| } |
| } |
| } |
| } |
| |
| private static boolean isClassRestricted(IJavaElement javaElement, |
| AnnotationDefinition annotationDefinition) throws JavaModelException { |
| if (javaElement.getElementType() == IJavaElement.TYPE) { |
| return !((IType)javaElement).isClass() && annotationDefinition.isClassOnly(); |
| } |
| if (javaElement.getElementType() == IJavaElement.METHOD) { |
| IType type = (IType)javaElement.getParent(); |
| return !type.isClass() && annotationDefinition.isClassOnly(); |
| } |
| if (javaElement.getElementType() == IJavaElement.FIELD) { |
| IType type = (IType)javaElement.getParent(); |
| return !type.isClass() && annotationDefinition.isClassOnly(); |
| } |
| return false; |
| } |
| |
| private static boolean isInterfaceRestricted(IJavaElement javaElement, |
| AnnotationDefinition annotationDefinition) throws JavaModelException { |
| if (javaElement.getElementType() == IJavaElement.TYPE) { |
| return !((IType)javaElement).isInterface() && annotationDefinition.isInterfaceOnly(); |
| } |
| if (javaElement.getElementType() == IJavaElement.METHOD) { |
| IType type = (IType)javaElement.getParent(); |
| return !type.isInterface() && annotationDefinition.isInterfaceOnly(); |
| } |
| if (javaElement.getElementType() == IJavaElement.FIELD) { |
| IType type = (IType)javaElement.getParent(); |
| return !type.isInterface() && annotationDefinition.isInterfaceOnly(); |
| } |
| return false; |
| } |
| |
| private static boolean isEnumRestricted(IJavaElement javaElement, |
| AnnotationDefinition annotationDefinition) throws JavaModelException { |
| if (javaElement.getElementType() == IJavaElement.TYPE) { |
| return !((IType)javaElement).isEnum() && annotationDefinition.isEnumOnly(); |
| } |
| if (javaElement.getElementType() == IJavaElement.METHOD) { |
| IType type = (IType)javaElement.getParent(); |
| return !type.isEnum() && annotationDefinition.isEnumOnly(); |
| } |
| if (javaElement.getElementType() == IJavaElement.FIELD) { |
| IType type = (IType)javaElement.getParent(); |
| return !type.isEnum() && annotationDefinition.isEnumOnly(); |
| } |
| return false; |
| } |
| } |