/*******************************************************************************
* Copyright (c) 2007 Exadel, Inc. and 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:
* Exadel, Inc. and Red Hat, Inc. - initial API and implementation
******************************************************************************/
package org.jboss.tools.common.util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.IAnnotatable;
import org.eclipse.jdt.core.IAnnotation;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeHierarchy;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.jboss.tools.common.core.CommonCorePlugin;
public class EclipseJavaUtil {
public static String getMemberTypeAsString(IMember member) {
if(member instanceof IField) return getMemberTypeAsString((IField)member);
if(member instanceof IMethod) return getMemberTypeAsString((IMethod)member);
return null;
}
public static String getMemberTypeAsString(IField f) {
if(f == null) return null;
try {
String typeName = new String(Signature.toCharArray(f.getTypeSignature().toCharArray()));
return resolveType(f.getDeclaringType(), typeName);
} catch (JavaModelException e) {
CommonCorePlugin.getPluginLog().logError(e);
}
return null;
}
public static String getMemberTypeAsString(IMethod m) {
if(m == null) return null;
try {
return resolveTypeAsString(m.getDeclaringType(), m.getReturnType());
} catch (JavaModelException e) {
CommonCorePlugin.getPluginLog().logError(e);
}
return null;
}
public static String resolveTypeAsString(IType type, String typeName) {
if(type == null || typeName == null) return null;
typeName = new String(Signature.toCharArray(typeName.toCharArray()));
int i = typeName.indexOf(Signature.C_GENERIC_START);
if(i > 0) typeName = typeName.substring(0, i);
return resolveType(type, typeName);
}
public static String resolveType(IType type, String typeName) {
return TypeResolutionCache.getInstance().resolveType(type, typeName);
}
static Map<String, Map<String, IType>> typeCache = new Hashtable<String, Map<String,IType>>();
/**
* Returns IType found in a Java project by its qualified name.
* The cache is used that is should be cleared explicitly by
* TypeResolutionCache.getInstance().clear();
* Currently, it is done by KBBuilder so that this method works fine for all clients that
* request only projects with KB nature. Otherwise, non-existent object may be
* returned from the cache.
*
* Now, it is not clear if the search over package roots,
* fulfilled when IJavaProject.findType(String) fails, makes sense.
* There are neither tests nor use-cases that would support the need of it.
*
* @param javaProject
* @param qualifiedName
* @return
* @throws JavaModelException
*/
public static IType findType(IJavaProject javaProject, String qualifiedName) throws JavaModelException {
if(qualifiedName == null || qualifiedName.length() == 0 || "void".equals(qualifiedName)) return null;
Map<String, IType> cache = typeCache.get(javaProject.getElementName());
if(cache == null) {
cache = new Hashtable<String, IType>();
typeCache.put(javaProject.getElementName(), cache);
} else {
IType type = cache.get(qualifiedName);
if(type != null) {
if(type.exists()) {
return type;
} else {
cache.remove(qualifiedName);
}
}
}
IType type = javaProject.findType(qualifiedName);
if(type != null && type.exists()) {
return register(cache, qualifiedName, type);
}
//TODO Either provide use-case that justifies the
// direct search over roots when IJavaProject.findType(String) fails
// or remove this obsolete code.
// int dot = qualifiedName.lastIndexOf('.');
// String packageName = (dot < 0) ? "" : qualifiedName.substring(0, dot); //$NON-NLS-1$
// String shortName = qualifiedName.substring(dot + 1);
// IPackageFragmentRoot[] rs = javaProject.getPackageFragmentRoots();
// for (int i = 0; i < rs.length; i++) {
// IPackageFragment f = rs[i].getPackageFragment(packageName);
// if(f == null || !f.exists()) continue;
// ICompilationUnit[] us = f.getCompilationUnits();
// for (int j = 0; j < us.length; j++) {
// IType t = us[j].getType(shortName);
// if(t != null && t.exists()) return register(cache, qualifiedName, t);
// }
// }
return null;
}
private static IType register(Map<String, IType> cache, String qualifiedName, IType type) {
cache.put(qualifiedName, type);
return type;
}
public static List<IType> getSupperTypes(IType type) throws JavaModelException {
ITypeHierarchy typeHierarchy = type.newSupertypeHierarchy(new NullProgressMonitor());
IType[] superTypes = typeHierarchy == null ? null : typeHierarchy.getAllSupertypes(type);
if(superTypes == null) {
return Collections.emptyList();
}
List<IType> suppers = new ArrayList<IType>();
for (int i = 0; i < superTypes.length; i++) {
suppers.add(superTypes[i]);
}
return suppers;
}
public static IAnnotation findAnnotation(IType sourceType, IAnnotatable member, String qulifiedAnnotationName) throws JavaModelException{
IAnnotation[] annotations = member.getAnnotations();
String simpleAnnotationTypeName = qulifiedAnnotationName;
int lastDot = qulifiedAnnotationName.lastIndexOf('.');
if(lastDot>-1) {
simpleAnnotationTypeName = simpleAnnotationTypeName.substring(lastDot + 1);
}
for (IAnnotation annotation : annotations) {
if(qulifiedAnnotationName.equals(annotation.getElementName())) {
return annotation;
}
if(simpleAnnotationTypeName.equals(annotation.getElementName())) {
String fullAnnotationclassName = EclipseJavaUtil.resolveType(sourceType, simpleAnnotationTypeName);
if(fullAnnotationclassName!=null) {
IType annotationType = sourceType.getJavaProject().findType(fullAnnotationclassName);
if(annotationType!=null && annotationType.getFullyQualifiedName().equals(qulifiedAnnotationName)) {
return annotation;
}
}
}
}
return null;
}
/**
* Finds field declared in the given type or its super types.
*
* @param type
* @param name
* @return
* @throws CoreException
*/
public static IField findField(IType type, String name) throws CoreException {
return findField(type, name, new HashSet<IType>());
}
private static IField findField(IType type, String name, Set<IType> processed) throws CoreException {
if(!type.exists() || processed.contains(type)) {
return null;
}
processed.add(type);
if(type.getField(name).exists()) {
return type.getField(name);
}
IField f = findField(type, type.getSuperclassName(), name, processed);
String[] is = type.getSuperInterfaceNames();
for (int i = 0; f == null && i < is.length; i++) {
f = findField(type, is[i], name, processed);
}
if(f == null) {
IType d = type.getDeclaringType();
if(d != null && d != type && d.exists()) {
f = findField(d, name);
}
}
return f;
}
private static IField findField(IType context, String typeName, String fieldName, Set<IType> processed) throws CoreException {
typeName = resolveType(context, typeName);
if(typeName != null) {
IType s = findType(context.getJavaProject(), typeName);
return (s != null) ? findField(s, fieldName, processed) : null;
}
return null;
}
/**
* Returns true if the given annotation has the given full name
* @param annotation
* @param fullName
* @return
* @throws JavaModelException
*/
public static boolean checkAnnotationByFulltName(IAnnotation annotation, String fullName) throws JavaModelException {
if(annotation.getElementName().equals(fullName)) {
return true;
}
boolean result = true;
IType sourceType = null;
IJavaElement parent = annotation.getParent();
if(parent instanceof IMember) {
if(parent instanceof IType) {
sourceType = (IType)parent;
} else {
sourceType = ((IMember)parent).getDeclaringType();
}
String fullAnnotationName = EclipseJavaUtil.resolveType(sourceType, annotation.getElementName());
if(fullAnnotationName!=null) {
IType annotationType = sourceType.getJavaProject().findType(fullAnnotationName);
result = annotationType!=null && annotationType.getFullyQualifiedName().equals(fullName);
} else {
result = false;
}
}
return result;
}
/**
* Returns annotation by the short name declared for the given java member and its parents.
* Returns null if no annotation found.
* @param member
* @param name
* @param checkParents
* @return
* @throws JavaModelException
*/
public static Set<IAnnotation> findAnnotationsByShortName(IJavaElement element, String name, boolean checkParents) throws JavaModelException {
return findAnnotationsByShortName(null, element, name, checkParents);
}
private static Set<IAnnotation> findAnnotationsByShortName(Set<IAnnotation> result, IJavaElement element, String name, boolean checkParents) throws JavaModelException {
if(element instanceof IAnnotatable) {
IAnnotation[] annotations = ((IAnnotatable)element).getAnnotations();
for (IAnnotation annotation : annotations) {
String aName = annotation.getElementName();
int i = aName.lastIndexOf('.');
if(i>-1) {
aName = aName.substring(i+1);
}
if(aName.equals(name)) {
if(result==null) {
result = new HashSet<IAnnotation>();
}
result.add(annotation);
break;
}
}
}
if(checkParents) {
IJavaElement parent = element.getParent();
if(parent instanceof IAnnotatable) {
return findAnnotationsByShortName(result, parent, name, true);
}
}
return result;
}
}