package org.fandev.lang.fan.resolve; import com.intellij.psi.infos.CandidateInfo; import com.intellij.psi.util.CachedValue; import com.intellij.psi.util.CachedValueProvider; import com.intellij.psi.util.PsiModificationTracker; import com.intellij.psi.util.TypeConversionUtil; import com.intellij.psi.*; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Key; import com.intellij.util.containers.HashMap; import com.intellij.util.containers.HashSet; import java.util.Map; import java.util.List; import java.util.Set; import java.util.ArrayList; import org.fandev.lang.fan.psi.api.statements.typeDefs.FanTypeDefinition; /** * Date: Sep 29, 2009 * Time: 11:27:44 PM * * @author Dror Bereznitsky */ public class CollectClassMembersUtil { private static final Key<CachedValue<Pair<Map<String, CandidateInfo>, Map<String, List<CandidateInfo>>>>> CACHED_MEMBERS = Key.create("CACHED_CLASS_MEMBERS"); private static final Key<CachedValue<Pair<Map<String, CandidateInfo>, Map<String, List<CandidateInfo>>>>> CACHED_MEMBERS_INCLUDING_SYNTHETIC = Key.create("CACHED_MEMBERS_INCLUDING_SYNTHETIC"); public static Map<String, List<CandidateInfo>> getAllMethods(final PsiClass aClass, final boolean includeSynthetic) { final Key<CachedValue<Pair<Map<String, CandidateInfo>, Map<String, List<CandidateInfo>>>>> key = includeSynthetic ? CACHED_MEMBERS_INCLUDING_SYNTHETIC : CACHED_MEMBERS; CachedValue<Pair<Map<String, CandidateInfo>, Map<String, List<CandidateInfo>>>> cachedValue = aClass.getUserData(key); if (cachedValue == null) { cachedValue = buildCache(aClass, includeSynthetic); } final Pair<Map<String, CandidateInfo>, Map<String, List<CandidateInfo>>> value = cachedValue.getValue(); assert value != null; return value.getSecond(); } public static Map<String, CandidateInfo> getAllFields(final PsiClass aClass) { CachedValue<Pair<Map<String, CandidateInfo>, Map<String, List<CandidateInfo>>>> cachedValue = aClass.getUserData(CACHED_MEMBERS); if (cachedValue == null) { cachedValue = buildCache(aClass, false); } final Pair<Map<String, CandidateInfo>, Map<String, List<CandidateInfo>>> value = cachedValue.getValue(); assert value != null; return value.getFirst(); } private static CachedValue<Pair<Map<String, CandidateInfo>, Map<String, List<CandidateInfo>>>> buildCache(final PsiClass aClass, final boolean includeSynthetic) { return aClass.getManager().getCachedValuesManager().createCachedValue(new CachedValueProvider<Pair<Map<String, CandidateInfo>, Map<String, List<CandidateInfo>>>>() { public Result<Pair<Map<String, CandidateInfo>, Map<String, List<CandidateInfo>>>> compute() { final Map<String, CandidateInfo> allFields = new HashMap<String, CandidateInfo>(); final Map<String, List<CandidateInfo>> allMethods = new HashMap<String, List<CandidateInfo>>(); processClass(aClass, allFields, allMethods, new HashSet<PsiClass>(), PsiSubstitutor.EMPTY, includeSynthetic); return new Result<Pair<Map<String, CandidateInfo>, Map<String, List<CandidateInfo>>>>(new Pair<Map<String, CandidateInfo>, Map<String, List<CandidateInfo>>>(allFields, allMethods), PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT); } }, false); } private static void processClass(final PsiClass aClass, final Map<String, CandidateInfo> allFields, final Map<String, List<CandidateInfo>> allMethods, final Set<PsiClass> visitedClasses, final PsiSubstitutor substitutor, final boolean includeSynthetic) { if (visitedClasses.contains(aClass)) { return; } visitedClasses.add(aClass); for (final PsiField field : aClass.getFields()) { final String name = field.getName(); if (!allFields.containsKey(name)) { allFields.put(name, new CandidateInfo(field, substitutor)); } } for (final PsiMethod method : includeSynthetic || !(aClass instanceof FanTypeDefinition) ? aClass.getMethods() : ((FanTypeDefinition) aClass).getFanMethods()) { addMethod(allMethods, method, substitutor); } for (final PsiClassType superType : aClass.getSuperTypes()) { final PsiClass superClass = superType.resolve(); if (superClass != null) { final PsiSubstitutor superSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(superClass, aClass, substitutor); processClass(superClass, allFields, allMethods, visitedClasses, superSubstitutor, includeSynthetic); } } } private static void addMethod(final Map<String, List<CandidateInfo>> allMethods, final PsiMethod method, final PsiSubstitutor substitutor) { final String name = method.getName(); List<CandidateInfo> methods = allMethods.get(name); if (methods == null) { methods = new ArrayList<CandidateInfo>(); allMethods.put(name, methods); methods.add(new CandidateInfo(method, substitutor)); } else { methods.add(new CandidateInfo(method, substitutor)); } } }