package gw.plugin.ij.util; import com.google.common.base.Function; import com.google.common.collect.AbstractLinkedIterator; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterators; import com.google.common.collect.Lists; import com.intellij.injected.editor.VirtualFileWindow; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.util.Computable; import com.intellij.openapi.vfs.JarFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; import com.intellij.psi.SmartPsiElementPointer; import com.intellij.psi.impl.source.resolve.FileContextUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlFile; import com.intellij.psi.xml.XmlTag; import com.intellij.psi.xml.XmlTagChild; import com.intellij.testFramework.LightVirtualFile; import com.intellij.util.indexing.IndexingDataKeys; import gw.config.CommonServices; import gw.fs.IDirectory; import gw.fs.IFile; import gw.fs.IResource; import gw.lang.reflect.IFileBasedType; import gw.lang.reflect.IType; import gw.lang.reflect.module.IModule; import gw.plugin.ij.filesystem.IDEADirectory; import gw.plugin.ij.filesystem.IDEAFile; import gw.plugin.ij.filesystem.IDEAFileSystem; import gw.plugin.ij.filesystem.IDEAResource; import gw.plugin.ij.lang.psi.impl.GosuScratchpadFileImpl; import gw.plugin.ij.lang.psi.util.GosuPsiParseUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; import java.util.Iterator; import java.util.List; import static com.google.common.base.Predicates.compose; import static com.google.common.base.Predicates.equalTo; import static com.google.common.collect.Iterables.skip; import static com.google.common.collect.Iterators.size; import static com.google.common.collect.Lists.reverse; import static com.google.common.collect.Iterators.filter; import static com.google.common.collect.Lists.newArrayList; public class FileUtil { public static final String MAGIC_INJECTED_SUFFIX = "_INJECTED_"; // TODO: implement in a general way private static final Logger LOG = Logger.getInstance(FileUtil.class); private static final Function<XmlTag, String> GET_XML_TAG_NAME = new Function<XmlTag, String>() { @NotNull @Override public String apply(@Nullable XmlTag tag) { return tag.getName(); } }; public static PsiFile[] getFiles(@NotNull final PsiDirectory directory) { return ApplicationManager.getApplication().runReadAction(new Computable<PsiFile[]>() { @NotNull public PsiFile[] compute() { return directory.getFiles(); } }); } @Nullable public static VirtualFile getFileFromPsi(@NotNull PsiFile file) { VirtualFile vfile = file.getUserData(IndexingDataKeys.VIRTUAL_FILE); if (vfile == null) { vfile = file.getVirtualFile(); if (vfile == null) { vfile = file.getOriginalFile().getVirtualFile(); if (vfile == null) { vfile = file.getViewProvider().getVirtualFile(); } } else if (vfile instanceof LightVirtualFile) { PsiFile containingFile = file.getContainingFile(); if (containingFile != null && containingFile != file) { PsiFile originalFile = containingFile.getOriginalFile(); SmartPsiElementPointer owningFile = originalFile.getUserData(FileContextUtil.INJECTED_IN_ELEMENT); if (owningFile != null) { vfile = owningFile.getVirtualFile(); } } } } return vfile; } public static VirtualFile getOriginalFile(VirtualFileWindow window) { VirtualFile file = window.getDelegate(); if (file instanceof LightVirtualFile) { final VirtualFile original = ((LightVirtualFile) file).getOriginalFile(); if (original != null) { file = original; } } return file; } public static String getSourceQualifiedName(@NotNull VirtualFile file, @NotNull IModule module) { if (file.getName().equals(GosuPsiParseUtil.TRANSIENT_PROGRAM)) { return "transient"; } if (file.getName().contains(GosuScratchpadFileImpl.GOSU_SCRATCHPAD_NAME)) { return GosuScratchpadFileImpl.FQN; } if (file instanceof VirtualFileWindow) { final VirtualFileWindow window = (VirtualFileWindow) file; final VirtualFile original = getOriginalFile(window); final List<String> typeNames = TypeUtil.getTypesForFile(module, original); if (typeNames.isEmpty()) { LOG.warn(String.format("No types for '%s', original is '%s'", file, original)); return "unable.to.find.name"; } else { return typeNames.get(0) + getInjectedFileSuffix(window, module); } } if (file instanceof LightVirtualFile) { return file.getNameWithoutExtension().replace(File.separatorChar, '.'); } // General case final List<String> typeNames = TypeUtil.getTypesForFile(module, file); if (typeNames.isEmpty()) { LOG.warn(String.format("File '%s' is outside of any source root", file)); return "unable.to.find.name"; } else { return typeNames.get(0); } } @NotNull public static String getInjectedFileSuffix(@NotNull VirtualFileWindow window, @NotNull IModule gsModule) { final Project project = (Project) gsModule.getExecutionEnvironment().getProject().getNativeProject(); final PsiFile host = PsiManager.getInstance(project).findFile(window.getDelegate()); final int startOffset = window.getDocumentWindow().getHostRanges()[0].getStartOffset(); final PsiElement hostElement = host.findElementAt(startOffset); final String unique; final XmlAttribute attr = PsiTreeUtil.getParentOfType(hostElement, XmlAttribute.class); if (attr != null) { final XmlLocation loc = getXmlAttributeLocation(attr); unique = loc.toString(); } else { final XmlTag tag = PsiTreeUtil.getParentOfType(hostElement, XmlTag.class); if (tag != null) { final XmlLocation loc = getXmlTagLocation(tag); unique = loc.toString(); } else { unique = Integer.toString(startOffset); } } return MAGIC_INJECTED_SUFFIX + unique; } @NotNull public static IDEAResource toIResource(VirtualFile file) { if (file.isDirectory()) { return toIDirectory(file); } else { return toIFile(file); } } public static IDEAFile toIFile(VirtualFile file) { return ((IDEAFileSystem) CommonServices.getFileSystem()).getIFile(file); } @NotNull public static IDEADirectory toIDirectory(VirtualFile file) { return ((IDEAFileSystem) CommonServices.getFileSystem()).getIDirectory(file); } // ID generation @Nullable private static Iterator<XmlTag> selfWithParents(XmlTag tag) { return new AbstractLinkedIterator<XmlTag>(tag) { @Nullable protected XmlTag computeNext(@Nullable XmlTag previous) { return previous != null ? previous.getParentTag() : null; } }; } @Nullable private static Iterator<XmlTagChild> selfWithPrevSiblings(XmlTagChild element) { return new AbstractLinkedIterator<XmlTagChild>(element) { @Nullable protected XmlTagChild computeNext(@Nullable XmlTagChild previous) { return previous != null ? previous.getPrevSiblingInTag() : null; } }; } private static <T extends XmlTagChild> Iterator<T> selfWithPrevSiblingsOfType(XmlTagChild element, Class<T> klass) { return Iterators.filter(selfWithPrevSiblings(element), klass); } protected static List<XmlLocation.Segment> getPath(XmlTag tag) { final List<XmlLocation.Segment> path = Lists.newArrayList(); for (XmlTag parent : skip(reverse(newArrayList(selfWithParents(tag))), 1)) { final String name = parent.getName(); final int index = size(filter( selfWithPrevSiblingsOfType(parent, XmlTag.class), compose(equalTo(name), GET_XML_TAG_NAME))) - 1; path.add(new XmlLocation.Segment(name, index)); } return ImmutableList.copyOf(path); } @NotNull public static XmlLocation getXmlTagLocation(@NotNull XmlTag tag) { final XmlFile file = (XmlFile) tag.getContainingFile(); return new XmlLocation(file.getRootTag().getName(), getPath(tag)); } @NotNull public static XmlLocation getXmlAttributeLocation(@NotNull XmlAttribute attr) { final XmlTag tag = attr.getParent(); final XmlFile file = (XmlFile) tag.getContainingFile(); return new XmlLocation(file.getRootTag().getName(), getPath(tag), attr.getName()); } @NotNull public static List<VirtualFile> getTypeResourceFiles(@NotNull IType type) { final List<VirtualFile> result = Lists.newArrayList(); if (type instanceof IFileBasedType) { for (IFile file : ((IFileBasedType) type).getSourceFiles()) { result.add(((IDEAFile) file).getVirtualFile()); } } return result; } @NotNull public static String removeJarSeparator(@NotNull String path) { if (path.endsWith(JarFileSystem.JAR_SEPARATOR)) { path = path.substring(0, path.length() - JarFileSystem.JAR_SEPARATOR.length()); } return path; } public static boolean underGosuSources(VirtualFile dir, Project project) { if (dir != null && dir.isDirectory()) { final IModule module = GosuModuleUtil.findModuleForFile(dir, project); if (module != null) { IResource sourceRoot = getSourceRoot(module, toIDirectory(dir)); if (sourceRoot != null && sourceRoot.getPath().getPathString().endsWith("gsrc")) { return true; } } } return false; } private static IResource getSourceRoot(IModule module, IResource resource) { for (IDirectory src : module.getSourcePath()) { if (resource.isDescendantOf(src)) { return src; } } return null; } public static VirtualFile findSourceRoot(VirtualFile virtualFile, Project project) { Module module = ModuleUtil.findModuleForFile(virtualFile, project); if (module != null) { final String path = virtualFile.getPath(); for (VirtualFile sourceRoot : ModuleRootManager.getInstance(module).getSourceRoots()) { String sourcePath = sourceRoot.getPath(); if (path.startsWith(sourcePath + "/") || path.equals(sourcePath)) { return sourceRoot; } } } return null; } public static boolean isSourceRoot(VirtualFile file, Project project) { return file.equals(findSourceRoot(file, project)); } }