blob: ba93d166d25b99bcbc75d8b079b1dec4a81e0dc9 [file] [log] [blame]
/*******************************************************************************
* 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 static final String ELEM_TARGET_FILTER = "targetFilter"; //$NON-NLS-1$
// private static final String ATT_TARGET = "target"; //$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 {@link java.lang.annotation.Annotation} that target the given java element type.
*
* @param element 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.
*/
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.getAnnotationClass() != null) {
annotationClassNameToDefinitionMap.put(annotationDefinition.getAnnotationClass()
.getCanonicalName(), 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 IAnnotationAttributeInitializer} for the given {@link org.eclipse.jdt.core.dom.Name}
* or null if none can be found.
* @param name a {@link SimpleName} or {@link 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(ElementType.PACKAGE);
}
if (javaElement instanceof IType) {
IType type = (IType) javaElement;
if (type.isAnnotation()) {
return getAnnotationsForElementType(ElementType.ANNOTATION_TYPE);
}
return getAnnotationsForElementType(ElementType.TYPE);
}
if (javaElement instanceof IField) {
return getAnnotationsForElementType(ElementType.FIELD);
}
if (javaElement instanceof IMethod) {
return getAnnotationsForElementType(ElementType.METHOD);
}
if (javaElement instanceof ILocalVariable) {
return getAnnotationsForElementType(ElementType.PARAMETER);
}
if (javaElement instanceof IAnnotation) {
return getAnnotationsForElementType(ElementType.ANNOTATION_TYPE);
}
return Collections.emptyList();
}
private static List<AnnotationDefinition> getAnnotationsForElementType(ElementType elementType) {
List<AnnotationDefinition> annotationDefinitions = new ArrayList<AnnotationDefinition>();
if (annotationCache == null) {
getAnnotations();
}
for (AnnotationDefinition annotationDefinition : annotationCache) {
if (annotationDefinition.getTargets().contains(elementType) &&
!isDeprecated(annotationDefinition)) {
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.getTargets().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;
}
//TODO Move the Deprecated option to preferences
private static boolean isDeprecated(AnnotationDefinition annotationDefinition) {
Class<?> annotationClass = annotationDefinition.getAnnotationClass();
return annotationClass.getAnnotation(java.lang.Deprecated.class) != null;
}
}