/******************************************************************************* * Copyright (c) 2007-2013 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 * * Contributor: * Red Hat, Inc. - initial API and implementation ******************************************************************************/ package org.jboss.tools.common.el.core.resolver; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; 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.IProject; import org.eclipse.core.runtime.NullProgressMonitor; 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.IType; import org.eclipse.jdt.core.ITypeHierarchy; import org.eclipse.jdt.core.ITypeParameter; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.Signature; import org.jboss.tools.common.el.core.ELCorePlugin; import org.jboss.tools.common.el.core.ca.preferences.ELContentAssistPreferences; import org.jboss.tools.common.util.BeanUtil; import org.jboss.tools.common.util.EclipseJavaUtil; /** * This class helps to collect information of java elements used in Seam EL. * @author Victor Rubezhny, Alexey Kazakov */ public class TypeInfoCollector { IType fType; MemberInfo fMember; TypeInfo fTypeInfo; boolean fIncludeStaticMethods; List<MethodInfo> fMethods; List<FieldInfo> fFields; private static class ProjectCache { Map<IMember, MemberInfo> memberInfoCacheFalse = new HashMap<IMember, MemberInfo>(); Map<IMember, MemberInfo> memberInfoCacheTrue = new HashMap<IMember, MemberInfo>(); Map<IType, SuperTypeInfo> superTypesCache = new HashMap<IType, SuperTypeInfo>(); } private static Caches caches = new Caches(); private static class Caches { ProjectCache common = new ProjectCache(); // Map<IProject, ProjectCache> cache = new HashMap<IProject, ProjectCache>(); public boolean contains(IProject p) { // return p != null && cache.containsKey(p); return true; } public void clean(IProject p) { // if(contains(p)) { // cache.remove(p); // } common = new ProjectCache(); } public ProjectCache get(IProject p) { if(p == null || !p.isAccessible()) return null; return common; // ProjectCache c = cache.get(p); // if(c == null) { // c = new ProjectCache(); // cache.put(p, c); // } // return c; } public ProjectCache get(IJavaElement element) { if(element == null) { return null; } IJavaProject jp = element.getJavaProject(); IProject p = jp == null ? null : jp.getProject(); return get(p); } } public static class Type { private String fName; private String fQualifiedName; private Type[] fParameters; private IType fSource; private String fSignature; private boolean fIsArray; private Type fTypeOfArrayElement; private String fQualifiedTypeNameOfArrayElement; private Type() { } public static Type valueOf(String name) { Type instance = new Type(); instance.setName(name); instance.setParameters(new Type[0]); return instance; } public Type(String signature, IType source) { if(signature!=null) { String erasureSignature = Signature.getTypeErasure(signature); String typeOfArraySiganture = Signature.getElementType(erasureSignature); fName = String.valueOf(Signature.toString(erasureSignature)); if(!erasureSignature.equals(typeOfArraySiganture)) { // this is an array fIsArray = true; fTypeOfArrayElement = new Type(typeOfArraySiganture, source); } String[] signaturesOfParametersOfType = Signature.getTypeArguments(signature); fParameters = new Type[signaturesOfParametersOfType.length]; for (int i = 0; i < signaturesOfParametersOfType.length; i++) { fParameters[i] = new Type(signaturesOfParametersOfType[i], source); } } else { fName = source.getFullyQualifiedName(); setParameters(new Type[0]); } fSource = source; } public void initializeParameters(Map<String, Type> parameters) { Type type = parameters.get(fName); if(type!=null) { fName = type.getName(); fParameters = type.getParameters(); fSource = type.getSource(); fIsArray = type.isArray(); fTypeOfArrayElement = type.getTypeOfArrayElement(); } for (int i = 0; i < fParameters.length; i++) { if (fName == null || !fName.equals(fParameters[i].getName())) fParameters[i].initializeParameters(parameters); } } public Type getParameter(int index) { if(fParameters.length>index) { return fParameters[index]; } return null; } public String getQualifiedTypeNameOfArrayElement() { if(fQualifiedTypeNameOfArrayElement==null && fSource!=null) { fQualifiedTypeNameOfArrayElement = EclipseJavaUtil.resolveType(fSource, fTypeOfArrayElement.getName()); } return fQualifiedTypeNameOfArrayElement; } public String getQualifiedName() { if(fQualifiedName == null && fSource!=null) { fQualifiedName = EclipseJavaUtil.resolveType(fSource, fName); } return fQualifiedName; } public boolean isArray() { return fIsArray; } public void setArray(boolean array) { fIsArray = array; } public Type getTypeOfArrayElement() { return fTypeOfArrayElement; } public void setTypeOfArrayElement(Type typeOfArrayElement) { fTypeOfArrayElement = typeOfArrayElement; } public String getName() { return fName; } public void setName(String name) { fName = name; } public Type[] getParameters() { return fParameters; } public void setParameters(Type[] parameters) { this.fParameters = parameters; } public IType getSource() { return fSource; } public void setSource(IType source) { this.fSource = source; } public String getSignature() { return fSignature; } public void setSignature(String signature) { this.fSignature = signature; } } public abstract static class MemberInfo { private String fDeclaringTypeQualifiedName; private String fName; private int fModifiers; private IType fSourceType; private MemberInfo fParentMember; private IType fMemberType; private boolean isDataModel; private Type fType; protected MemberInfo ( IType sourceType, String declaringTypeQualifiedName, String name, int modifiers, MemberInfo parentMember, boolean dataModel, Type type) { setSourceType(sourceType); setDeclaringTypeQualifiedName(declaringTypeQualifiedName); setName(name); setModifiers(modifiers); setParentMember(parentMember); setDataModel(dataModel); setType(type); } abstract void initializeParameters(); protected void setType(Type type) { fType = type; } public Type getType() { return fType; } /** * Returns the name of member type. In most cases it will return the result of call to fType.getName(). * The only exclusion is the MethodInfo that is a Property Setter (isSetter() == true): * - In case of a Setter Method it should return the name of the setter parameter value type * * @return */ public String getMemberTypeName() { return fType.getName(); } public void setSourceType(IType sourceType) { fSourceType = sourceType; } public IType getSourceType() { return fSourceType; } protected void setName (String name) { this.fName = name; } public String getName() { return fName; } protected void setDeclaringTypeQualifiedName(String declaringTypeQualifiedName) { this.fDeclaringTypeQualifiedName = declaringTypeQualifiedName; } public String getDeclaringTypeQualifiedName() { return fDeclaringTypeQualifiedName; } protected void setModifiers (int modifiers) { this.fModifiers = modifiers; } public int getModifiers() { return fModifiers; } public boolean isPublic() { return Modifier.isPublic(fModifiers); } public boolean isStatic() { return Modifier.isStatic(fModifiers); } public boolean isJavaLangObject() { return "java.lang.Object".equals(getDeclaringTypeQualifiedName()); //$NON-NLS-1$ } public MemberInfo getParentMember() { return fParentMember; } protected void setParentMember(MemberInfo parentMember) { fParentMember = parentMember; } public IType getMemberType() { if(fMemberType==null) { initializeParameters(); try { if(isDataModel() && getType().isArray()) { fMemberType = getType().getSource().getJavaProject().findType(getType().getQualifiedTypeNameOfArrayElement()); } else if(getType().getQualifiedName()!=null) { fMemberType = getType().getSource().getJavaProject().findType(getType().getQualifiedName()); } } catch (JavaModelException e) { ELCorePlugin.getPluginLog().logError(e); } } return fMemberType; } public boolean isDataModel() { return isDataModel; } public void setDataModel(boolean isDataModel) { this.isDataModel = isDataModel; } public TypeInfoCollector getTypeCollector(boolean varIsUsed, boolean includeStaticMethods) { // The rev. 7651 results in a deadlock, typeInfo is not stored anymore // The rev. 7623 results in a deadlock, so, it's rolled back // >>> Fix for JBIDE-2090 return new TypeInfoCollector(this, varIsUsed, includeStaticMethods); // <<< Fix for JBIDE-2090 } abstract public IJavaElement getJavaElement(); } public static class ArtificialTypeInfo extends TypeInfo { private IType fType; private IMethod fMethod; private String fMethodName; public ArtificialTypeInfo (IType type, IMethod method, String methodName) throws JavaModelException { super(type, null, false); this.fType = type; this.fMethod = method; this.fMethodName = methodName; } @Override public IJavaElement getJavaElement() { return fType; } @Override public TypeInfoCollector getTypeCollector(boolean varIsUsed, boolean includeStaticMethods) { return new TypeInfoCollector(this, varIsUsed, true) { @Override public List<MemberInfo> getMethods() { // Do not filter the static methods here, just return all the artificial methods List<MemberInfo> methods = new ArrayList<MemberInfo>(); methods.addAll(fMethods); return methods; } @Override public List<MemberInfo> getProperties() { // No properties are allowed here, just return an empty list here return new ArrayList<MemberInfo>(); } @Override public void collectInfo(boolean var) { if (fMethods == null) { fMethods = new ArrayList<MethodInfo>(); } else { fMethods.clear(); } if (fFields == null) { fFields = new ArrayList<FieldInfo>(); } else { fFields.clear(); } if (fType == null) { return; } try { IType binType = fType; if(fMember instanceof TypeInfo) { fTypeInfo = (TypeInfo)fMember; } else { fTypeInfo = new TypeInfo(binType, fMember, fMember.isDataModel()); } TypeInfo parent = fTypeInfo; MethodInfo info = new MethodInfo(fMethod, fMethodName, fTypeInfo, parent, false); fMethods.add(info); } catch (JavaModelException e) { ELCorePlugin.getPluginLog().logError(e); } } }; } } public static class TypeInfo extends MemberInfo { private IType fType; private TypeInfo superType; private Map<String, Type> params = new HashMap<String, Type>(); public TypeInfo(IType type, MemberInfo parentMember, boolean dataModel) throws JavaModelException { super(type.getDeclaringType(), (type.getDeclaringType() == null ? null : type.getDeclaringType().getFullyQualifiedName()), type.getFullyQualifiedName(), type.getFlags(), parentMember, dataModel, Type.valueOf(type.getFullyQualifiedName())); this.fType = type; } public Type getParameterType(String name) { return params.get(name); } @Override public IType getMemberType() { return fType; } @Override public IJavaElement getJavaElement() { return fType; } /* (non-Javadoc) * @see org.jboss.tools.common.model.util.TypeInfoCollector.MemberInfo#initializeParameters() */ @Override void initializeParameters() { try { MemberInfo parent = getParentMember(); if(parent instanceof TypeMemberInfo) { ITypeParameter[] parameters = fType.getTypeParameters(); for (int i = 0; i < parameters.length; i++) { Type type = parent.getType().getParameter(i); if(type!=null) { params.put(parameters[i].getElementName(), type); } } } if(superType!=null) { superType.initializeParameters(this); } } catch (JavaModelException e) { ELCorePlugin.getPluginLog().logError(e); } } private void initializeParameters(TypeInfo inheritedType) throws JavaModelException { ITypeParameter[] parameters = fType.getTypeParameters(); String signature = inheritedType.fType.getSuperclassTypeSignature(); Type classType = new Type(signature, inheritedType.fType); for (int i = 0; i < parameters.length; i++) { Type paramType = classType.getParameter(i); if(paramType!=null) { Type resolvedType = inheritedType.getParameterType(paramType.getName()); if(resolvedType!=null) { paramType = resolvedType; } params.put(parameters[i].getElementName(), paramType); } } if(superType!=null) { superType.initializeParameters(this); } } public TypeInfo getSuperType() { return superType; } public void setSuperType(TypeInfo superType) { this.superType = superType; } } public abstract static class TypeMemberInfo extends MemberInfo { private String[] fParametersNamesOfDeclaringType; private TypeInfo declaratedType; /** * @param sourceType * @param declaringTypeQualifiedName * @param name * @param modifiers * @param parentMember * @param dataModel * @param type */ protected TypeMemberInfo(IType sourceType, String declaringTypeQualifiedName, String name, int modifiers, TypeInfo parentMember, TypeInfo declaratedType, boolean dataModel, Type type) { super(sourceType, declaringTypeQualifiedName, name, modifiers, parentMember, dataModel, type); this.declaratedType = declaratedType; } public String[] getParametersNamesOfDeclaringType() { return fParametersNamesOfDeclaringType; } void setParametersNamesOfDeclaringType( String[] parametersNamesOfDeclaringType) { fParametersNamesOfDeclaringType = parametersNamesOfDeclaringType; } protected void initializeParameters() { if(fParametersNamesOfDeclaringType!=null && fParametersNamesOfDeclaringType.length>0 && getParentMember()!=null) { Map<String, Type> parametersOfDeclaringType = new HashMap<String, Type>(); TypeInfo parentTypeInfo = (TypeInfo)getParentMember(); parentTypeInfo.initializeParameters(); declaratedType.initializeParameters(); for (int i = 0; i < fParametersNamesOfDeclaringType.length; i++) { String parameterName = getParameterNameFromType(fParametersNamesOfDeclaringType[i]); Type paramType = declaratedType.getParameterType(parameterName); if(paramType!=null) { parametersOfDeclaringType.put(parameterName, paramType); } } getType().initializeParameters(parametersOfDeclaringType); } } public TypeInfo getDeclaratedType() { return declaratedType; } protected void setDeclaratedType(TypeInfo declaratedType) { this.declaratedType = declaratedType; } } public static class FieldInfo extends TypeMemberInfo { private IJavaElement fJavaElement; public FieldInfo(IField field, TypeInfo parentMember, TypeInfo declaratedType, boolean dataModel) throws JavaModelException { super(field.getDeclaringType(), (field.getDeclaringType() == null ? null : field.getDeclaringType().getFullyQualifiedName()), field.getElementName(), field.getFlags(), parentMember, declaratedType, dataModel, new Type(field.getTypeSignature(), field.getDeclaringType())); fJavaElement = field; setParametersNamesOfDeclaringType(getTypeErasureFromSignatureArray(field.getDeclaringType().getTypeParameterSignatures())); } public IJavaElement getJavaElement () { if(fJavaElement == null) { try { if(getDeclaringTypeQualifiedName()==null) { return null; } IType declType = getSourceType().getJavaProject().findType(getDeclaringTypeQualifiedName()); fJavaElement = (declType == null ? null : declType.getField(getName())); } catch (JavaModelException e) { ELCorePlugin.getPluginLog().logError(e); } } return fJavaElement; } } public static class MethodInfo extends TypeMemberInfo { private String[] fParameterTypeNames; private String[] fParameterTypeQualifiedNames; private String[] fParameterNames; private IMethod fJavaElement; public MethodInfo(IType sourceType, String declaringTypeQualifiedName, String name, int modifiers, String[] parameterTypeQualifiedNames, String[] parameterNames, String returnTypeQualifiedName, TypeInfo parentMember, TypeInfo declaratedType, boolean dataModel) { super(sourceType, declaringTypeQualifiedName, name, modifiers, parentMember, declaratedType, dataModel, Type.valueOf(name)); setParameterTypeNames(parameterTypeQualifiedNames); setParameterNames(parameterNames); } public MethodInfo(IMethod method, String name, TypeInfo parentMember, TypeInfo declaratedType, boolean dataModel) throws JavaModelException { super(method.getDeclaringType(), (method.getDeclaringType() == null ? null : method.getDeclaringType().getFullyQualifiedName()), name, method.getFlags(), parentMember, declaratedType, dataModel, new Type(method.getReturnType(), method.getDeclaringType())); fJavaElement = method; setParameterNames(method.getParameterNames()); setParameterTypeNames(resolveSignatures(method.getDeclaringType(), method.getParameterTypes())); setParametersNamesOfDeclaringType(getTypeErasureFromSignatureArray(method.getDeclaringType().getTypeParameterSignatures())); } public MethodInfo(IMethod method, TypeInfo parentMember, TypeInfo declaratedType, boolean dataModel) throws JavaModelException { super(method.getDeclaringType(), (method.getDeclaringType() == null ? null : method.getDeclaringType().getFullyQualifiedName()), method.getElementName(), method.getFlags(), parentMember, declaratedType, dataModel, new Type(method.getReturnType(), method.getDeclaringType())); fJavaElement = method; setParameterNames(method.getParameterNames()); setParameterTypeNames(resolveSignatures(method.getDeclaringType(), method.getParameterTypes())); setParametersNamesOfDeclaringType(getTypeErasureFromSignatureArray(method.getDeclaringType().getTypeParameterSignatures())); } protected void setParameterTypeNames(String[] parameterTypeNames) { fParameterTypeNames = (parameterTypeNames == null ? new String[0] : parameterTypeNames); } public String[] getParameterTypeQualifiedNames() { if(fParameterTypeQualifiedNames==null) { fParameterTypeQualifiedNames = new String[fParameterTypeNames.length]; for (int i = 0; i < fParameterTypeQualifiedNames.length; i++) { fParameterTypeQualifiedNames[i] = EclipseJavaUtil.resolveType(getSourceType(), fParameterTypeNames[i]); } } return fParameterTypeQualifiedNames; } public String[] getParameterTypeNames() { return fParameterTypeNames; } protected void setParameterNames(String[] parameterNames) { fParameterNames = (parameterNames == null ? new String[0] : parameterNames); } public String[] getParameterNames() { return fParameterNames; } public int getNumberOfParameters() { return (getParameterNames() == null ? 0 : getParameterNames().length); } public IType getReturnType() { return getMemberType(); } public boolean isConstructor () { return getDeclaringTypeQualifiedName()!=null && getDeclaringTypeQualifiedName().equals(getName()); } public boolean isGetter() { return getType() != null && BeanUtil.isGetter(getName(), getNumberOfParameters()) && BeanUtil.checkPropertyReturnType(getType().getName(), getName()); } public boolean isSetter() { return BeanUtil.isSetter(getName(), getNumberOfParameters()); } public List<String> getAsPresentedStrings() { List<String> list = new ArrayList<String>(2); StringBuffer name = new StringBuffer(getName()); // Add method as 'foo' list.add(name.toString()); // Add method as 'foo(param1,param2)' name.append('('); String[] mParams = getParameterNames(); for (int j = 0; mParams != null && j < mParams.length; j++) { if (j > 0) name.append(", "); //$NON-NLS-1$ name.append(mParams[j]); } name.append(')'); list.add(name.toString()); return list; } @Override public String getMemberTypeName() { if (isSetter()) { return getParameterTypeNames()[0]; } return super.getMemberTypeName(); } @Override public IJavaElement getJavaElement () { if(fJavaElement == null) { try { IType declType = getSourceType().getJavaProject().findType(getDeclaringTypeQualifiedName()); if(declType==null) { return null; } IMethod[] allMethods = declType.getMethods(); // filter methods by name List<IMethod> methods = new ArrayList<IMethod>(); for (int i = 0; allMethods != null && i < allMethods.length; i++) { if (allMethods[i].getElementName().equals(getName())) { methods.add(allMethods[i]); } } if (!methods.isEmpty()) { if (methods.size() == 1) { fJavaElement = methods.get(0); } else { // filter methods by number of parameters List<IMethod> filteredMethods = new ArrayList<IMethod>(); for (IMethod method : methods) { if (method.getNumberOfParameters() == getNumberOfParameters()) { filteredMethods.add(method); } } if (!filteredMethods.isEmpty()) { if (filteredMethods.size() == 1) { fJavaElement = filteredMethods.get(0); } else { methods = filteredMethods; // filter methods by parameter types for(IMethod method : methods) { String[] methodParameterTypes = resolveSignatures(method.getDeclaringType(), method.getParameterTypes()); String[] parameterTypes = getParameterTypeQualifiedNames(); boolean equal = true; for (int i = 0; parameterTypes != null && i < parameterTypes.length; i++) { // simple types must be equal, but complex types may not if (!parameterTypes[i].equals(methodParameterTypes[i])) { // sure - it's Complex Type if (! (parameterTypes[i].indexOf('.') != -1) && (methodParameterTypes[i].indexOf('.') == -1)) { equal = false; break; } } } if (equal) { fJavaElement = method; } } } } } } } catch (JavaModelException e) { ELCorePlugin.getPluginLog().logError(e); } } return fJavaElement; } } public TypeInfoCollector(MemberInfo member, boolean varIsUsed, boolean includeStaticMethods) { this.fMember = member; this.fType = member.getMemberType(); this.fIncludeStaticMethods = includeStaticMethods; collectInfo(varIsUsed); } public IType getType() { return this.fType; } public void collectInfo() { collectInfo(false); } public void collectInfo(boolean var) { if (fMethods == null) { fMethods = new ArrayList<MethodInfo>(); } else { fMethods.clear(); } if (fFields == null) { fFields = new ArrayList<FieldInfo>(); } else { fFields.clear(); } if (fType == null) { return; } try { IType binType = fType; MemberInfo originalParent = fMember; if(fMember instanceof TypeInfo) { fTypeInfo = (TypeInfo)fMember; } else { fTypeInfo = new TypeInfo(binType, fMember, fMember.isDataModel()); } TypeInfo parent = fTypeInfo; initSuperTypes(binType, var, parent, originalParent, new TreeSet<String>()); // This inserts here methods "public int size()" and "public boolean isEmpty()" for javax.faces.model.DataModel // as requested by Gavin in JBIDE-1256 if(isDataModelObject(fType)) { addInfoForDataModelObject(); } // This inserts here methods "public int getRowCount()" for @DataModel variables. if(fMember.isDataModel) { addInfoForDataModelVariable(); } } catch (JavaModelException e) { ELCorePlugin.getPluginLog().logError(e); } } private void initSuperTypes(IType binType, boolean var, TypeInfo parent, MemberInfo originalParent, Collection<String> allTypes) throws JavaModelException { IMethod[] binMethods = binType.getMethods(); for (int i = 0; binMethods != null && i < binMethods.length; i++) { if (binMethods[i].isConstructor()) { continue; } MethodInfo info = new MethodInfo(binMethods[i], fTypeInfo, parent, false); if(info.getType().isArray() && var) { info.setDataModel(true); } fMethods.add(info); } String[] superinterfaceNames = binType.getSuperInterfaceNames(); for (String superinterface : superinterfaceNames) { String fullSuperInterfaceName = EclipseJavaUtil.resolveType(binType, superinterface); if(fullSuperInterfaceName!=null) { if(fullSuperInterfaceName.equals(binType.getFullyQualifiedName())) { break; } IType superBinType = binType.getJavaProject().findType(fullSuperInterfaceName); if(superBinType!=null && !allTypes.contains(superBinType.getFullyQualifiedName())) { initType(superBinType, var, parent, originalParent, allTypes); } } } IType superBinType = getSuperclass(binType); if(superBinType!=null && !allTypes.contains(superBinType.getFullyQualifiedName())) { initType(superBinType, var, parent, originalParent, allTypes); } } private void initType(IType binType, boolean var, TypeInfo parent, MemberInfo originalParent, Collection<String> allTypes) throws JavaModelException { TypeInfo superType = new TypeInfo(binType, originalParent, parent.isDataModel()); parent.setSuperType(superType); allTypes.add(binType.getFullyQualifiedName()); initSuperTypes(binType, var, superType, originalParent, allTypes); } boolean isDataModelObject(IType type) throws JavaModelException { return isInstanceofType(type, "javax.faces.model.DataModel"); //$NON-NLS-1$ } public static boolean isResourceBundle(IType type) { try { return isInstanceofType(type, "java.util.ResourceBundle"); //$NON-NLS-1$ } catch (JavaModelException e) { return false; } } public static boolean isNotParameterizedCollection(TypeInfoCollector.MemberInfo mbr) { try { if(mbr.getType().getParameters()!=null && mbr.getType().getParameters().length>0) { return false; } IType type = mbr.getMemberType(); if(type!=null) { return isInstanceofType(type, "java.util.Map") || isInstanceofType(type, "java.lang.Iterable"); //$NON-NLS-1$ //$NON-NLS-2$ } return false; } catch (JavaModelException e) { return false; } } public static class SuperTypeInfo { IType type; Set<String> names = new HashSet<String>(); IType[] superTypes = new IType[0]; SuperTypeInfo(IType type) throws JavaModelException { this.type = type; ProjectCache cache = caches.get(type); if(cache != null) { cache.superTypesCache.put(type, this); } ITypeHierarchy typeHierarchy = type.newSupertypeHierarchy(new NullProgressMonitor()); superTypes = typeHierarchy == null ? null : typeHierarchy.getAllSupertypes(type); if(superTypes != null) for (int i = 0; i < superTypes.length; i++) { names.add(superTypes[i].getFullyQualifiedName()); } if(superTypes == null) superTypes = new IType[0]; } public Set<String> getNames() { return names; } public IType[] getSuperTypes() { return superTypes; } } public static SuperTypeInfo getSuperTypes(IType type) throws JavaModelException { if(type == null) return null; ProjectCache cache = caches.get(type); SuperTypeInfo ts = (cache != null) ? cache.superTypesCache.get(type) : null; if(ts == null) { ts = new SuperTypeInfo(type); } return ts; } public static boolean isInstanceofType(IType type, String qualifiedTypeName) throws JavaModelException { if (qualifiedTypeName == null || type == null) return false; boolean isInstanceofType = qualifiedTypeName.equals(type.getFullyQualifiedName()); if (!isInstanceofType) { SuperTypeInfo ts = getSuperTypes(type); if(ts != null && ts.getNames().contains(qualifiedTypeName)) { return true; } return false; } return true; } void addInfoForDataModelVariable() { fMethods.add(new MethodInfo(fType, fType.getFullyQualifiedName(), "getRowCount", Modifier.PUBLIC, //$NON-NLS-1$ new String[0], new String[0], "int", //$NON-NLS-1$ fTypeInfo, fTypeInfo, false)); } void addInfoForDataModelObject() { fMethods.add(new MethodInfo(fType, fType.getFullyQualifiedName(), "size", Modifier.PUBLIC, //$NON-NLS-1$ new String[0], new String[0], "int", //$NON-NLS-1$ fTypeInfo, fTypeInfo, false)); fMethods.add(new MethodInfo(fType, fType.getFullyQualifiedName(), "isEmpty", Modifier.PUBLIC, //$NON-NLS-1$ new String[0], new String[0], "boolean", //$NON-NLS-1$ fTypeInfo, fTypeInfo, false)); } public static IType getSuperclass(IType type) throws JavaModelException { String superclassName = type.getSuperclassName(); if(superclassName == null && type.isEnum()) { superclassName = "java.lang.Enum"; //$NON-NLS-1$ } if(superclassName!=null) { String fullySuperclassName = EclipseJavaUtil.resolveType(type, superclassName); if(fullySuperclassName!=null&&!fullySuperclassName.equals("java.lang.Object")) { //$NON-NLS-1$ if(fullySuperclassName.equals(type.getFullyQualifiedName())) { //FIX JBIDE-1642 return null; } IType superType = type.getJavaProject().findType(fullySuperclassName); return superType; } } return null; } public MethodInfo[] findMethodInfos(IMethod iMethod) { List<MethodInfo> methods = new ArrayList<MethodInfo>(); // filter methods by name for (MethodInfo info : fMethods) { if (info.getName().equals(iMethod.getElementName())) { methods.add(info); } } if (methods.isEmpty()) return new MethodInfo[0]; EclipseJavaUtil.getMemberTypeAsString(iMethod); if (methods.size() == 1) return methods.toArray(new MethodInfo[0]); // filter methods by number of parameters List<MethodInfo> filteredMethods = new ArrayList<MethodInfo>(); for (MethodInfo method : methods) { if (method.getNumberOfParameters() == iMethod.getNumberOfParameters()) filteredMethods.add(method); } if (filteredMethods.isEmpty()) return new MethodInfo[0]; if (filteredMethods.size() == 1) return filteredMethods.toArray(new MethodInfo[0]); methods = filteredMethods; // filter methods by parameter types filteredMethods = new ArrayList<MethodInfo>(); for(MethodInfo method : methods) { String[] methodParameterTypes = resolveSignatures(iMethod.getDeclaringType(), iMethod.getParameterTypes()); String[] parameterTypes = method.getParameterTypeQualifiedNames(); boolean equal = true; for (int i = 0; equal && parameterTypes != null && i < parameterTypes.length; i++) { // simple types must be equal, but complex types may not if (!parameterTypes[i].equals(methodParameterTypes[i])) { // sure - it's Complex Type if ((parameterTypes[i].indexOf('.') != -1) && (methodParameterTypes[i].indexOf('.') == -1)) { equal = false; } } } if (equal) { filteredMethods.add(method); } } return filteredMethods.toArray(new MethodInfo[0]); } /** * Returns the methods for the type specified * * @return */ public List<MemberInfo> getMethods() { List<MemberInfo> methods = new ArrayList<MemberInfo>(); for (MethodInfo info : fMethods) { try { if (((info.getSourceType()!=null && info.getSourceType().isInterface()) || info.isPublic()) && !info.isConstructor() && (fIncludeStaticMethods || !info.isStatic()) // Fix for JBIDE-5961: JSF EL Code complition doesn't recognize static methods. && !info.isJavaLangObject() // && !info.isGetter() && !info.isSetter() // Fix for JBIDE-3378 ) { methods.add(info); } } catch (JavaModelException e) { ELCorePlugin.getPluginLog().logError(e); } } return methods; } /** * String presentation of member * @author Alexey Kazakov */ public static class MemberPresentation { private boolean property; private String presentation; private String displayName; private MemberInfo member; private Set<MemberInfo> allMembers = new HashSet<MemberInfo>(); public MemberPresentation(String presentation, String displayName, MemberInfo member) { super(); this.presentation = presentation; this.displayName = displayName; this.member = member; addMember(member); } public String getPresentation() { return presentation; } public String getPresentationDisplayName() { return displayName; } public MemberInfo getMember() { return member; } @Override public boolean equals(Object obj) { if(obj instanceof MemberPresentation) { return presentation.equals(((MemberPresentation)obj).getPresentation()); } return false; } @Override public int hashCode() { return presentation.hashCode(); } @Override public String toString() { return presentation; } /** * Adds a pair member to the member info * In case of a property there may be up to 2 members added: getter and setter * In case of a method there will be a single member added (the method itself) * * @param pair */ public void addMember(MemberInfo pair) { this.allMembers.add(pair); } /** * Returns all the members collected * In case of a property there may be up to 2 members returned: getter and setter * In case of a method there will be a single member returned (the method itself) * * @return */ public Set<MemberInfo> getAllMembers() { return allMembers; } public boolean isProperty() { return property; } public void setProperty(boolean property) { this.property = property; } } private static class MemberPresentationComparator implements Comparator<MemberPresentation> { /* (non-Javadoc) * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) */ public int compare(MemberPresentation m1, MemberPresentation m2) { return m1.getPresentation().compareTo(m2.getPresentation()); } } public final static MemberPresentationComparator MEMBER_PRESENTATION_COMPARATOR = new MemberPresentationComparator(); /** * Returns the method presentation strings for the type specified * @return */ public Set<String> getMethodPresentationStrings(boolean isValidating) { Set<MemberPresentation> set = getMethodPresentations(isValidating); Set<String> result = new HashSet<String>(); for (MemberPresentation presentation : set) { result.add(presentation.getPresentation()); } return result; } /** * Returns the method presentations for the type specified * @return */ public Set<MemberPresentation> getMethodPresentations(boolean isValidating) { Set<MemberPresentation> methods = new TreeSet<MemberPresentation>(MEMBER_PRESENTATION_COMPARATOR); List<MemberInfo> mthds = getMethods(); for (MemberInfo info : mthds) { if (!(info instanceof MethodInfo)) continue; MethodInfo method = (MethodInfo)info; StringBuffer name = new StringBuffer(method.getName()); StringBuffer displayName = new StringBuffer(method.getName()); boolean disabledGettersAndSettersView = !ELCorePlugin.getDefault().getPreferenceStore().getBoolean(ELContentAssistPreferences.SHOW_GETTERS_AND_SETTERS) && (method.isGetter() || method.isSetter()); if (!isValidating && disabledGettersAndSettersView) { continue; } boolean enabledNonParenthesesView = !ELCorePlugin.getDefault().getPreferenceStore().getBoolean(ELContentAssistPreferences.SHOW_METHODS_WITH_PARENTHESES_ONLY); if (isValidating || enabledNonParenthesesView) { // Add method as 'foo' methods.add(new MemberPresentation(name.toString(), displayName.toString(), method)); } // As requirement of JBIDE-7168: Add method as 'foo()' (do not extract the parameters), // but show the method parameters with their types name.append('('); displayName.append('('); String[] mParams = method.getParameterNames(); String[] mTypes = method.getParameterTypeNames(); for (int j = 0; mParams != null && j < mParams.length; j++) { String typeName = mTypes[j] == null ? "" : mTypes[j]; //$NON-NLS-1$ if (typeName.indexOf('.') != -1) typeName = typeName.substring(typeName.lastIndexOf('.') + 1); if (j > 0) displayName.append(", "); //$NON-NLS-1$ displayName.append(typeName).append(' ').append(mParams[j]); } name.append(')'); displayName.append(')'); methods.add(new MemberPresentation(name.toString(), displayName.toString(), method)); } return methods; } /** * Returns the properties for the type specified * * @return */ public List<MemberInfo> getProperties() { List<MemberInfo> properties = new ArrayList<MemberInfo>(); for (MethodInfo info : fMethods) { try { if (((info.getSourceType()!=null && info.getSourceType().isInterface()) || (info.isPublic())) && !info.isConstructor() && !info.isStatic() && !info.isJavaLangObject() && (info.isGetter() || info.isSetter())) { properties.add(info); } } catch (JavaModelException e) { ELCorePlugin.getPluginLog().logError(e); } } /* * The following code was excluded due to the following issue: * * http://jira.jboss.com/jira/browse/JBIDE-1203#action_12385823 * * for (FieldInfo info : fFields) { if (info.isPublic() && !info.isStatic() && !info.isJavaLangObject()) properties.add(info); } */ return properties; } /** * Returns the property presentations for the type specified * * @return */ public Set<MemberPresentation> getPropertyPresentations(boolean isValidating) { return getPropertyPresentations(null, isValidating); } /** * Returns the property presentation strings for the type specified * * @return */ public Set<String> getPropertyPresentationStrings(boolean isValidating) { return getPropertyPresentationStrings(null, isValidating); } /** * Returns the property presentation strings for the type specified * * @param unpairedGettersOrSetters - map of unpaired getters or setters of type's properties. 'key' is property name. * @return */ public Set<String> getPropertyPresentationStrings(Map<String, MethodInfo> unpairedGettersOrSetters, boolean isValidating) { Set<MemberPresentation> set = getPropertyPresentations(unpairedGettersOrSetters, isValidating); Set<String> result = new HashSet<String>(); for (MemberPresentation presentation : set) { result.add(presentation.getPresentation()); } return result; } /** * Returns the property presentations for the type specified * * @param unpairedGettersOrSetters - map of unpaired getters or setters of type's properties. 'key' is property name. * @return */ public Set<MemberPresentation> getPropertyPresentations(Map<String, MethodInfo> unpairedGettersOrSetters, boolean isValidating) { Set<MemberPresentation> properties = new TreeSet<MemberPresentation>(MEMBER_PRESENTATION_COMPARATOR); HashMap<String, MemberPresentation> presentations = new HashMap<String, MemberPresentation>(); List<MemberInfo> props = getProperties(); HashMap<String, MethodInfo> getters = new HashMap<String, MethodInfo>(); HashMap<String, MethodInfo> setters = new HashMap<String, MethodInfo>(); for (MemberInfo info : props) { if (info instanceof MethodInfo) { MethodInfo m = (MethodInfo)info; if (m.isGetter() || m.isSetter()) { String propertyName = BeanUtil.getPropertyName(m.getName()); if(propertyName == null) continue; MemberPresentation pr = new MemberPresentation(propertyName, propertyName, m); if(!properties.contains(pr)) { properties.add(pr); presentations.put(pr.getPresentation(), pr); } else { MemberPresentation existingPresentation = presentations.get(pr.getPresentation()); existingPresentation.addMember(m); } pr.setProperty(true); if(unpairedGettersOrSetters!=null) { MethodInfo previousGetter = getters.get(propertyName); MethodInfo previousSetter = setters.get(propertyName); if((previousGetter!=null && m.isSetter())||(previousSetter!=null && m.isGetter())) { // We have both Getter and Setter unpairedGettersOrSetters.remove(propertyName); } else if(m.isSetter()) { setters.put(propertyName, m); unpairedGettersOrSetters.put(propertyName, m); } else if(m.isGetter()) { getters.put(propertyName, m); unpairedGettersOrSetters.put(propertyName, m); } } } } else { MemberPresentation pr = new MemberPresentation(info.getName(), info.getName(), info); properties.add(pr); presentations.put(pr.getPresentation(), pr); } } return properties; } public static void cleanCache() { caches = new Caches(); } public static MemberInfo createMemberInfo(IMember member, boolean dataModel) { ProjectCache pcache = caches.get(member); Map<IMember, MemberInfo> cache = null; if(pcache != null) { cache = dataModel ? pcache.memberInfoCacheTrue : pcache.memberInfoCacheFalse; } MemberInfo result = cache == null ? null : cache.get(member); if(result != null) return result; try { if (member instanceof IType) { result = new TypeInfo((IType)member, null, dataModel); } else if (member instanceof IField) { IField field = (IField)member; TypeInfo declaringType = new TypeInfo(field.getDeclaringType(), null, dataModel); result = new FieldInfo(field, declaringType, declaringType, dataModel); } else if (member instanceof IMethod) { IMethod method = (IMethod)member; TypeInfo declaringType = new TypeInfo(method.getDeclaringType(), null, dataModel); result = new MethodInfo(method, declaringType, declaringType, dataModel); } } catch (JavaModelException e) { ELCorePlugin.getPluginLog().logError(e); } if(result != null && cache!=null) { cache.put(member, result); } return result; } public static MemberInfo createMemberInfo(IMember member) { return createMemberInfo(member, false); } static String[] resolveSignatures (IType type, String[] signatures) { if (signatures == null || signatures.length == 0) return new String[0]; String[] resolvedSignatures = new String[signatures.length]; for (int i = 0; i < signatures.length; i++) { resolvedSignatures[i] = EclipseJavaUtil.resolveTypeAsString(type, signatures[i]); } return resolvedSignatures; } static String[] convertToStringArray(char[][] names) { if (names == null || names.length == 0) return new String[0]; String[] sNames = new String[names.length]; for (int i = 0; i < sNames.length; i++) { sNames[i] = String.valueOf(names[i]); } return sNames; } static String[] getTypeErasureFromSignatureArray(String[] signatures) { if (signatures == null || signatures.length == 0) return new String[0]; String[] result = new String[signatures.length]; for (int i = 0; i < signatures.length; i++) { result[i] = Signature.getTypeErasure(signatures[i]); } return result; } static String getParameterNameFromType(String typeSignatures) { if(typeSignatures==null) { return null; } return Signature.getTypeVariable(typeSignatures); } }