package com.jetbrains.lang.dart.util; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.LibraryOrderEntry; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.roots.OrderEntry; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.roots.impl.libraries.LibraryEx; import com.intellij.openapi.roots.libraries.LibraryProperties; import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.ex.temp.TempFileSystem; import com.intellij.util.PairConsumer; import com.jetbrains.lang.dart.ide.index.DartLibraryIndex; import com.jetbrains.lang.dart.sdk.DartPackagesLibraryProperties; import com.jetbrains.lang.dart.sdk.DartPackagesLibraryType; import com.jetbrains.lang.dart.sdk.DartSdk; import gnu.trove.THashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; public class DartUrlResolverImpl extends DartUrlResolver { // TODO Fold into superclass. @NotNull private final Project myProject; @Nullable private final DartSdk myDartSdk; @Nullable private final VirtualFile myPubspecYamlFile; // myLivePackageNameToDirMap also contains packages map from .packages file if applicable @NotNull private final Map<String, VirtualFile> myLivePackageNameToDirMap = new THashMap<>(); // myPackagesMapFromLib is not empty only if pubspec.yaml file is null @NotNull private final Map<String, List<String>> myPackagesMapFromLib = new THashMap<>(); public DartUrlResolverImpl(final @NotNull Project project, final @NotNull VirtualFile contextFile) { myProject = project; myDartSdk = DartSdk.getDartSdk(project); myPubspecYamlFile = PubspecYamlUtil.findPubspecYamlFile(myProject, contextFile); initLivePackageNameToDirMap(); if (myPubspecYamlFile == null) { initPackagesMapFromLib(contextFile); } } @Nullable public VirtualFile getPubspecYamlFile() { return myPubspecYamlFile; } public void processLivePackages(final @NotNull PairConsumer<String, VirtualFile> packageNameAndDirConsumer) { for (Map.Entry<String, VirtualFile> entry : myLivePackageNameToDirMap.entrySet()) { packageNameAndDirConsumer.consume(entry.getKey(), entry.getValue()); } } public Collection<String> getLivePackageNames() { return myLivePackageNameToDirMap.keySet(); } @Nullable public VirtualFile getPackageDirIfNotInOldStylePackagesFolder(@NotNull final String packageName, @Nullable final String pathRelToPackageDir) { final VirtualFile dir = myLivePackageNameToDirMap.get(packageName); if (dir != null) return dir; final List<String> dirPaths = myPackagesMapFromLib.get(packageName); if (dirPaths != null) { VirtualFile notNullPackageDir = null; for (String dirPath : dirPaths) { final VirtualFile packageDir = ApplicationManager.getApplication().isUnitTestMode() ? TempFileSystem.getInstance().findFileByPath(dirPath) : LocalFileSystem.getInstance().findFileByPath(dirPath); if (notNullPackageDir == null && packageDir != null) { notNullPackageDir = packageDir; } if (packageDir != null && (StringUtil.isEmpty(pathRelToPackageDir) || packageDir.findFileByRelativePath(pathRelToPackageDir) != null)) { return packageDir; } } return notNullPackageDir; // file by pathRelToPackageDir was not found, but not-null packageDir still may be useful } return null; } @Nullable public VirtualFile findFileByDartUrl(final @NotNull String url) { if (url.startsWith(DART_PREFIX)) { return findFileInDartSdkLibFolder(myProject, myDartSdk, url); } if (url.startsWith(PACKAGE_PREFIX)) { final String packageRelPath = url.substring(PACKAGE_PREFIX.length()); final int slashIndex = packageRelPath.indexOf('/'); final String packageName = slashIndex > 0 ? packageRelPath.substring(0, slashIndex) : packageRelPath; final String pathRelToPackageDir = slashIndex > 0 ? packageRelPath.substring(slashIndex + 1) : ""; final VirtualFile packageDir = StringUtil.isEmpty(packageName) ? null : myLivePackageNameToDirMap.get(packageName); if (packageDir != null) { return packageDir.findFileByRelativePath(pathRelToPackageDir); } final List<String> packageDirs = myPackagesMapFromLib.get(packageName); if (packageDirs != null) { for (String packageDirPath : packageDirs) { final VirtualFile file = LocalFileSystem.getInstance().findFileByPath(packageDirPath + "/" + pathRelToPackageDir); if (file != null) { return file; } } } } if (url.startsWith(FILE_PREFIX)) { final String path = StringUtil.trimLeading(url.substring(FILE_PREFIX.length()), '/'); return LocalFileSystem.getInstance().findFileByPath(SystemInfo.isWindows ? path : ("/" + path)); } if (ApplicationManager.getApplication().isUnitTestMode() && url.startsWith(TEMP_PREFIX)) { return TempFileSystem.getInstance().findFileByPath(url.substring((TEMP_PREFIX).length())); } return null; } @NotNull public String getDartUrlForFile(final @NotNull VirtualFile file) { String result = null; if (myDartSdk != null) result = getUrlIfFileFromSdkLib(myProject, file, myDartSdk); if (result != null) return result; result = getUrlIfFileFromLivePackage(file, myLivePackageNameToDirMap); if (result != null) return result; result = getUrlIfFileFromDartPackagesLib(file, myPackagesMapFromLib); if (result != null) return result; // see com.google.dart.tools.debug.core.server.ServerBreakpointManager#getAbsoluteUrlForResource() return new File(file.getPath()).toURI().toString(); } @Nullable private static String getUrlIfFileFromSdkLib(final @NotNull Project project, final @NotNull VirtualFile file, final @NotNull DartSdk sdk) { final VirtualFile sdkLibFolder = LocalFileSystem.getInstance().findFileByPath(sdk.getHomePath() + "/lib"); final String relativeToSdkLibFolder = sdkLibFolder == null ? null : VfsUtilCore.getRelativePath(file, sdkLibFolder, '/'); final String sdkLibUri = relativeToSdkLibFolder == null ? null : DartLibraryIndex.getSdkLibUriByRelativePath(project, relativeToSdkLibFolder); return sdkLibUri != null ? sdkLibUri : relativeToSdkLibFolder != null ? DART_PREFIX + relativeToSdkLibFolder : null; } @Nullable private static String getUrlIfFileFromLivePackage(final @NotNull VirtualFile file, final @NotNull Map<String, VirtualFile> livePackageNameToDirMap) { for (Map.Entry<String, VirtualFile> entry : livePackageNameToDirMap.entrySet()) { final String packageName = entry.getKey(); final VirtualFile packageDir = entry.getValue(); final String relPath = VfsUtilCore.getRelativePath(file, packageDir, '/'); if (relPath != null) { return PACKAGE_PREFIX + packageName + "/" + relPath; } } return null; } @Nullable private static String getUrlIfFileFromDartPackagesLib(final @NotNull VirtualFile file, final @NotNull Map<String, List<String>> pubListPackageDirsMap) { for (Map.Entry<String, List<String>> mapEntry : pubListPackageDirsMap.entrySet()) { for (String dirPath : mapEntry.getValue()) { if (file.getPath().startsWith(dirPath + "/")) { final String packageName = mapEntry.getKey(); return PACKAGE_PREFIX + packageName + file.getPath().substring(dirPath.length()); } } } return null; } private void initLivePackageNameToDirMap() { final VirtualFile baseDir = myPubspecYamlFile == null ? null : myPubspecYamlFile.getParent(); if (myPubspecYamlFile == null || baseDir == null) return; final VirtualFile dotPackagesFile = baseDir.findChild(DotPackagesFileUtil.DOT_PACKAGES); if (dotPackagesFile != null && !dotPackagesFile.isDirectory()) { final Map<String, String> packagesMap = DotPackagesFileUtil.getPackagesMap(dotPackagesFile); if (packagesMap != null) { for (Map.Entry<String, String> entry : packagesMap.entrySet()) { final String packageName = entry.getKey(); final String packagePath = entry.getValue(); final VirtualFile packageDir = myPubspecYamlFile.getFileSystem().findFileByPath(packagePath); if (packageDir != null) { myLivePackageNameToDirMap.put(packageName, packageDir); } } } } else { final String name = PubspecYamlUtil.getDartProjectName(myPubspecYamlFile); final VirtualFile libFolder = baseDir.findChild(PubspecYamlUtil.LIB_DIR_NAME); if (name != null && libFolder != null && libFolder.isDirectory()) { myLivePackageNameToDirMap.put(name, libFolder); } PubspecYamlUtil .processInProjectPathPackagesRecursively(myProject, myPubspecYamlFile, myLivePackageNameToDirMap::put); } } private void initPackagesMapFromLib(final @NotNull VirtualFile contextFile) { final Module module = ModuleUtilCore.findModuleForFile(contextFile, myProject); final List<OrderEntry> orderEntries = module != null ? Arrays.asList(ModuleRootManager.getInstance(module).getOrderEntries()) : ProjectRootManager.getInstance(myProject).getFileIndex().getOrderEntriesForFile(contextFile); for (OrderEntry orderEntry : orderEntries) { if (orderEntry instanceof LibraryOrderEntry && LibraryTablesRegistrar.PROJECT_LEVEL.equals(((LibraryOrderEntry)orderEntry).getLibraryLevel()) && DartPackagesLibraryType.DART_PACKAGES_LIBRARY_NAME.equals(((LibraryOrderEntry)orderEntry).getLibraryName())) { final LibraryEx library = (LibraryEx)((LibraryOrderEntry)orderEntry).getLibrary(); final LibraryProperties properties = library == null ? null : library.getProperties(); if (properties instanceof DartPackagesLibraryProperties) { for (Map.Entry<String, List<String>> entry : ((DartPackagesLibraryProperties)properties).getPackageNameToDirsMap().entrySet()) { if (entry != null && entry.getKey() != null && entry.getValue() != null) { myPackagesMapFromLib.put(entry.getKey(), entry.getValue()); } } return; } } } } }