/* * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.intellij.psi.impl.source; import com.intellij.openapi.util.SimpleModificationTracker; import com.intellij.psi.*; import com.intellij.psi.augment.PsiAugmentProvider; import com.intellij.psi.impl.PsiClassImplUtil; import com.intellij.psi.impl.PsiImplUtil; import com.intellij.psi.impl.light.LightMethod; import com.intellij.psi.util.CachedValueProvider; import com.intellij.psi.util.CachedValuesManager; import com.intellij.util.ArrayUtil; import com.intellij.util.containers.ContainerUtil; import gnu.trove.THashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collections; import java.util.List; import java.util.Map; import static com.intellij.psi.util.PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT; public class ClassInnerStuffCache { private final PsiExtensibleClass myClass; private final SimpleModificationTracker myTracker; public ClassInnerStuffCache(@NotNull PsiExtensibleClass aClass) { myClass = aClass; myTracker = new SimpleModificationTracker(); } @NotNull public PsiMethod[] getConstructors() { return CachedValuesManager.getCachedValue(myClass, new CachedValueProvider() { @Nullable @Override public Result compute() { return Result.create(PsiImplUtil.getConstructors(myClass), OUT_OF_CODE_BLOCK_MODIFICATION_COUNT, myTracker); } }); } @NotNull public PsiField[] getFields() { return CachedValuesManager.getCachedValue(myClass, new CachedValueProvider() { @Nullable @Override public Result compute() { return Result.create(getAllFields(), OUT_OF_CODE_BLOCK_MODIFICATION_COUNT, myTracker); } }); } @NotNull public PsiMethod[] getMethods() { return CachedValuesManager.getCachedValue(myClass, new CachedValueProvider() { @Nullable @Override public Result compute() { return Result.create(getAllMethods(), OUT_OF_CODE_BLOCK_MODIFICATION_COUNT, myTracker); } }); } @NotNull public PsiClass[] getInnerClasses() { return CachedValuesManager.getCachedValue(myClass, new CachedValueProvider() { @Nullable @Override public Result compute() { return Result.create(getAllInnerClasses(), OUT_OF_CODE_BLOCK_MODIFICATION_COUNT, myTracker); } }); } @Nullable public PsiField findFieldByName(String name, boolean checkBases) { if (checkBases) { return PsiClassImplUtil.findFieldByName(myClass, name, true); } return CachedValuesManager.getCachedValue(myClass, new CachedValueProvider>() { @Nullable @Override public Result> compute() { return Result.create(getFieldsMap(), OUT_OF_CODE_BLOCK_MODIFICATION_COUNT, myTracker); } }).get(name); } @NotNull public PsiMethod[] findMethodsByName(String name, boolean checkBases) { if (checkBases) { return PsiClassImplUtil.findMethodsByName(myClass, name, true); } PsiMethod[] methods = CachedValuesManager.getCachedValue(myClass, new CachedValueProvider>() { @Nullable @Override public Result> compute() { return Result.create(getMethodsMap(), OUT_OF_CODE_BLOCK_MODIFICATION_COUNT, myTracker); } }).get(name); return methods == null ? PsiMethod.EMPTY_ARRAY : methods; } @Nullable public PsiClass findInnerClassByName(final String name, final boolean checkBases) { if (checkBases) { return PsiClassImplUtil.findInnerByName(myClass, name, true); } else { return CachedValuesManager.getCachedValue(myClass, new CachedValueProvider>() { @Nullable @Override public Result> compute() { return Result.create(getInnerClassesMap(), OUT_OF_CODE_BLOCK_MODIFICATION_COUNT, myTracker); } }).get(name); } } @Nullable public PsiMethod getValuesMethod() { return !myClass.isEnum() || myClass.getName() == null ? null : CachedValuesManager.getCachedValue(myClass, new CachedValueProvider() { @Nullable @Override public Result compute() { PsiElementFactory factory = JavaPsiFacade.getInstance(myClass.getProject()).getElementFactory(); String text = "public static " + myClass.getName() + "[] values() { }"; PsiMethod physicalMethod = factory.createMethodFromText(text, myClass); PsiMethod method = new LightMethod(myClass.getManager(), physicalMethod, myClass); return new Result(method, OUT_OF_CODE_BLOCK_MODIFICATION_COUNT, myTracker); } }); } @Nullable public PsiMethod getValueOfMethod() { return !myClass.isEnum() || myClass.getName() == null ? null : CachedValuesManager.getCachedValue(myClass, new CachedValueProvider() { @Nullable @Override public Result compute() { PsiElementFactory factory = JavaPsiFacade.getInstance(myClass.getProject()).getElementFactory(); String text = "public static " + myClass.getName() + " valueOf(java.lang.String name) throws java.lang.IllegalArgumentException { }"; PsiMethod physicalMethod = factory.createMethodFromText(text, myClass); PsiMethod method = new LightMethod(myClass.getManager(), physicalMethod, myClass); return new Result(method, OUT_OF_CODE_BLOCK_MODIFICATION_COUNT, myTracker); } }); } @NotNull private PsiField[] getAllFields() { List own = myClass.getOwnFields(); List ext = PsiAugmentProvider.collectAugments(myClass, PsiField.class); return ArrayUtil.mergeCollections(own, ext, PsiField.ARRAY_FACTORY); } @NotNull private PsiMethod[] getAllMethods() { List own = myClass.getOwnMethods(); List ext = PsiAugmentProvider.collectAugments(myClass, PsiMethod.class); return ArrayUtil.mergeCollections(own, ext, PsiMethod.ARRAY_FACTORY); } @NotNull private PsiClass[] getAllInnerClasses() { List own = myClass.getOwnInnerClasses(); List ext = PsiAugmentProvider.collectAugments(myClass, PsiClass.class); return ArrayUtil.mergeCollections(own, ext, PsiClass.ARRAY_FACTORY); } @NotNull private Map getFieldsMap() { PsiField[] fields = getFields(); if (fields.length == 0) return Collections.emptyMap(); Map cachedFields = new THashMap(); for (PsiField field : fields) { String name = field.getName(); if (!(field instanceof ExternallyDefinedPsiElement) || !cachedFields.containsKey(name)) { cachedFields.put(name, field); } } return cachedFields; } @NotNull private Map getMethodsMap() { PsiMethod[] methods = getMethods(); if (methods.length == 0) return Collections.emptyMap(); Map> collectedMethods = ContainerUtil.newHashMap(); for (PsiMethod method : methods) { List list = collectedMethods.get(method.getName()); if (list == null) { collectedMethods.put(method.getName(), list = ContainerUtil.newSmartList()); } list.add(method); } Map cachedMethods = ContainerUtil.newTroveMap(); for (Map.Entry> entry : collectedMethods.entrySet()) { List list = entry.getValue(); cachedMethods.put(entry.getKey(), list.toArray(new PsiMethod[list.size()])); } return cachedMethods; } @NotNull private Map getInnerClassesMap() { PsiClass[] classes = getInnerClasses(); if (classes.length == 0) return Collections.emptyMap(); Map cachedInners = new THashMap(); for (PsiClass psiClass : classes) { String name = psiClass.getName(); if (!(psiClass instanceof ExternallyDefinedPsiElement) || !cachedInners.containsKey(name)) { cachedInners.put(name, psiClass); } } return cachedInners; } public void dropCaches() { myTracker.incModificationCount(); } }