/* * Copyright 2013 Guidewire Software, Inc. */ package gw.plugin.ij.lang.psi.impl; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiFile; import gw.lang.reflect.AbstractTypeSystemListener; import gw.lang.reflect.IBlockType; import gw.lang.reflect.IDefaultTypeLoader; import gw.lang.reflect.IFileBasedType; import gw.lang.reflect.IType; import gw.lang.reflect.ITypeLoader; import gw.lang.reflect.RefreshRequest; import gw.lang.reflect.TypeSystem; import gw.lang.reflect.gs.GosuClassTypeLoader; import gw.lang.reflect.gs.IGosuClass; import gw.lang.reflect.java.IJavaType; import gw.lang.reflect.module.IModule; import gw.plugin.ij.lang.psi.custom.CustomGosuClass; import gw.plugin.ij.util.FileUtil; import org.jetbrains.annotations.NotNull; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; public class CustomPsiClassCache extends AbstractTypeSystemListener { private static final CustomPsiClassCache INSTANCE = new CustomPsiClassCache(); @NotNull public static CustomPsiClassCache instance() { return INSTANCE; } private final ConcurrentHashMap<String, CustomGosuClass> _psi2Class = new ConcurrentHashMap<>(); private final ConcurrentHashMap<IModule, Map<String, CustomGosuClass>> _type2Class = new ConcurrentHashMap<>(); private CustomPsiClassCache() { TypeSystem.addTypeLoaderListenerAsWeakRef(this); } public CustomGosuClass getPsiClass(@NotNull IType type) { if (!(type instanceof IFileBasedType)) { return null; } List<VirtualFile> typeResourceFiles = FileUtil.getTypeResourceFiles(type); if (typeResourceFiles.isEmpty()) { return null; } IModule module = type.getTypeLoader().getModule(); String name = type.getName(); Map<String, CustomGosuClass> map = _type2Class.get(module); if (map == null) { map = new ConcurrentHashMap<>(); _type2Class.put(module, map); } CustomGosuClass psiClass = map.get(name); if (psiClass == null) { psiClass = createPsiClass(type); map.put(name, psiClass); VirtualFile virtualFile = psiClass.getContainingFile().getVirtualFile(); _psi2Class.put(virtualFile.getPath(), psiClass); } return psiClass; } public CustomGosuClass getPsiClass(@NotNull PsiFile file) { String virtualFilePath = file.getVirtualFile().getPath(); CustomGosuClass psiClass = _psi2Class.get(virtualFilePath); if (psiClass == null) { psiClass = new CustomGosuClass(file); _psi2Class.put(virtualFilePath, psiClass); } return psiClass; } @NotNull private CustomGosuClass createPsiClass(@NotNull IType type) { if (!(type instanceof IFileBasedType)) { throw new RuntimeException("Only file-based types can have custom PsiClasses: " + type.getClass().getName()); } verifyArgs(type); return new CustomGosuClass((IFileBasedType) type); } private void verifyArgs(@NotNull IType type) { if (type instanceof IGosuClass || type instanceof IJavaType || type instanceof IBlockType) { throw new RuntimeException("It is wrong to cache these types: " + type.getClass().getSimpleName()); } } public Collection<? extends String> getAllClassNames() { // long t1 = System.nanoTime(); Set<String> classes = new HashSet<>(); for (ITypeLoader loader : TypeSystem.getAllTypeLoaders()) { if (!(loader instanceof GosuClassTypeLoader || loader instanceof IDefaultTypeLoader)) { IModule module = loader.getModule(); TypeSystem.pushModule(module); try { for (CharSequence cs : loader.getAllTypeNames()) { String s = cs.toString(); int i = s.lastIndexOf('.'); if (i > 0) { s = s.substring(i + 1); } classes.add(s); } } finally { TypeSystem.popModule(module); } } } // System.out.println((System.nanoTime() - t1)*1e-6); return classes; } @NotNull public Collection<PsiClass> getByShortName(String shortName) { Set<PsiClass> classes = new HashSet<>(); String prefix = "." + shortName; for (ITypeLoader loader : TypeSystem.getAllTypeLoaders()) { if (loader.showTypeNamesInIDE() && !(loader instanceof GosuClassTypeLoader || loader instanceof IDefaultTypeLoader)) { IModule module = loader.getModule(); TypeSystem.pushModule(module); try { for (CharSequence cs : loader.getAllTypeNames()) { String typeName = cs.toString(); if (typeName.endsWith(prefix)) { IType type = TypeSystem.getByFullNameIfValid(typeName, module); if (type instanceof IFileBasedType) { CustomGosuClass psiClass = getPsiClass(type); if (psiClass != null) { classes.add(psiClass); } } } } } finally { TypeSystem.popModule(module); } } } return classes; } @Override public void refreshedTypes(RefreshRequest request) { Map<String, CustomGosuClass> map = _type2Class.get(request.module); if (map != null) { for (String type : request.types) { map.remove(type); } } if (request.file != null) { String pathString = request.file.getPath().getPathString(); _psi2Class.remove(pathString); } } @Override public void refreshed() { _psi2Class.clear(); _type2Class.clear(); } }