@NotNull
  private static List<VirtualFile> findClasses(File file, boolean isJre) {
    List<VirtualFile> result = ContainerUtil.newArrayList();
    VirtualFileManager fileManager = VirtualFileManager.getInstance();

    String path = file.getPath();
    if (JrtFileSystem.isModularJdk(path)) {
      String url =
          VirtualFileManager.constructUrl(
              JrtFileSystem.PROTOCOL,
              FileUtil.toSystemIndependentName(path) + JrtFileSystem.SEPARATOR);
      for (String module : JrtFileSystem.listModules(path)) {
        ContainerUtil.addIfNotNull(result, fileManager.findFileByUrl(url + module));
      }
    }

    for (File root : JavaSdkUtil.getJdkClassesRoots(file, isJre)) {
      String url = VfsUtil.getUrlForLibraryRoot(root);
      ContainerUtil.addIfNotNull(result, fileManager.findFileByUrl(url));
    }

    Collections.sort(result, (o1, o2) -> o1.getPath().compareTo(o2.getPath()));

    return result;
  }
 private void disposeListeners() {
   FileStatusManager.getInstance(myProject).removeFileStatusListener(myFileStatusListener);
   VirtualFileManager.getInstance().removeVirtualFileListener(myFileListener);
   VirtualFileManager.getInstance().removeVirtualFileManagerListener(myVirtualFileManagerListener);
   myProject
       .getComponent(ProjectLevelVcsManager.class)
       .removeVcsListener(myDirectoryMappingListener);
   ChangeListManager.getInstance(myProject).removeChangeListListener(myChangeListListener);
   myMessageBusConnection.disconnect();
 }
  public ExternalAnnotationsManagerImpl(
      @NotNull final Project project, final PsiManager psiManager) {
    super(psiManager);
    myBus = project.getMessageBus();
    final MessageBusConnection connection = myBus.connect(project);
    connection.subscribe(
        ProjectTopics.PROJECT_ROOTS,
        new ModuleRootAdapter() {
          @Override
          public void rootsChanged(ModuleRootEvent event) {
            dropCache();
          }
        });

    final MyVirtualFileListener fileListener = new MyVirtualFileListener();
    VirtualFileManager.getInstance().addVirtualFileListener(fileListener);
    Disposer.register(
        myPsiManager.getProject(),
        new Disposable() {
          @Override
          public void dispose() {
            VirtualFileManager.getInstance().removeVirtualFileListener(fileListener);
          }
        });
  }
  @Nullable
  public static VirtualFile getDirectory(@NotNull final FindModel findModel) {
    String directoryName = findModel.getDirectoryName();
    if (findModel.isProjectScope() || StringUtil.isEmpty(directoryName)) {
      return null;
    }

    String path = directoryName.replace(File.separatorChar, '/');
    VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(path);
    if (virtualFile == null || !virtualFile.isDirectory()) {
      virtualFile = null;
      for (LocalFileProvider provider :
          ((VirtualFileManagerEx) VirtualFileManager.getInstance()).getLocalFileProviders()) {
        VirtualFile file = provider.findLocalVirtualFileByPath(path);
        if (file != null && file.isDirectory()) {
          if (file.getChildren().length > 0) {
            virtualFile = file;
            break;
          }
          if (virtualFile == null) {
            virtualFile = file;
          }
        }
      }
    }
    return virtualFile;
  }
 @Nullable
 private static VirtualFile findInJar(File jarFile, String relativePath) {
   if (!jarFile.exists()) return null;
   String url =
       JarFileSystem.PROTOCOL_PREFIX
           + jarFile.getAbsolutePath().replace(File.separatorChar, '/')
           + JarFileSystem.JAR_SEPARATOR
           + relativePath;
   return VirtualFileManager.getInstance().findFileByUrl(url);
 }
  private void installListeners() {
    FileStatusManager.getInstance(myProject)
        .addFileStatusListener(myFileStatusListener = new FileStatusChangeListener());
    VirtualFileManager.getInstance()
        .addVirtualFileListener(myFileListener = new FileChangesListener());
    VirtualFileManager.getInstance()
        .addVirtualFileManagerListener(myVirtualFileManagerListener = new RefreshListener());
    myDirectoryMappingListener =
        new VcsListener() {
          public void directoryMappingChanged() {
            rebuildTreeLater();
          }
        };
    myProject.getComponent(ProjectLevelVcsManager.class).addVcsListener(myDirectoryMappingListener);
    ChangeListManager.getInstance(myProject)
        .addChangeListListener(myChangeListListener = new ChangeListUpdateListener());
    myMessageBusConnection = myBus.connect();
    myMessageBusConnection.subscribe(
        FileEditorManagerListener.FILE_EDITOR_MANAGER,
        new FileEditorManagerAdapter() {
          @Override
          public void fileOpened(FileEditorManager source, VirtualFile file) {
            if (myProjectView.isAutoscrollFromSource(getId())) {
              selectNode(file, false);
            }
          }

          @Override
          public void selectionChanged(FileEditorManagerEvent event) {
            if (myProjectView.isAutoscrollFromSource(getId())) {
              VirtualFile newFile = event.getNewFile();
              if (newFile != null) {
                selectNode(newFile, false);
              }
            }
          }
        });
  }
  @Override
  public void dispose() {
    if (mySelectedEditor != null) {
      for (final Map.Entry<PropertiesFile, Editor> entry : myEditors.entrySet()) {
        if (mySelectedEditor.equals(entry.getValue())) {
          writeEditorPropertyValue(mySelectedEditor, entry.getKey(), null);
        }
      }
    }

    VirtualFileManager.getInstance().removeVirtualFileListener(myVfsListener);
    myDisposed = true;
    Disposer.dispose(myStructureViewComponent);
    releaseAllEditors();
  }
  public static void attachJdkAnnotations(@NotNull SdkModificator modificator) {
    LocalFileSystem lfs = LocalFileSystem.getInstance();
    List<String> pathsChecked = new ArrayList<>();
    // community idea under idea
    String path =
        FileUtil.toSystemIndependentName(PathManager.getHomePath()) + "/java/jdkAnnotations";
    VirtualFile root = lfs.findFileByPath(path);
    pathsChecked.add(path);

    if (root == null) { // idea under idea
      path =
          FileUtil.toSystemIndependentName(PathManager.getHomePath())
              + "/community/java/jdkAnnotations";
      root = lfs.findFileByPath(path);
      pathsChecked.add(path);
    }
    if (root == null) { // build
      String url =
          "jar://"
              + FileUtil.toSystemIndependentName(PathManager.getHomePath())
              + "/lib/jdkAnnotations.jar!/";
      root = VirtualFileManager.getInstance().findFileByUrl(url);
      pathsChecked.add(
          FileUtil.toSystemIndependentName(PathManager.getHomePath()) + "/lib/jdkAnnotations.jar");
    }
    if (root == null) {
      String msg = "Paths checked:\n";
      for (String p : pathsChecked) {
        File file = new File(p);
        msg +=
            "Path: '"
                + p
                + "' "
                + (file.exists() ? "Found" : "Not found")
                + "; directory children: "
                + Arrays.toString(file.getParentFile().listFiles())
                + "\n";
      }
      LOG.error("JDK annotations not found", msg);
      return;
    }

    OrderRootType annoType = AnnotationOrderRootType.getInstance();
    modificator.removeRoot(root, annoType);
    modificator.addRoot(root, annoType);
  }
  public FileTreeBuilder(
      JTree tree,
      DefaultTreeModel treeModel,
      AbstractTreeStructure treeStructure,
      Comparator<NodeDescriptor> comparator,
      FileChooserDescriptor chooserDescriptor,
      @SuppressWarnings("UnusedParameters") Runnable onInitialized) {
    super(tree, treeModel, treeStructure, comparator, false);
    myChooserDescriptor = chooserDescriptor;

    initRootNode();

    VirtualFileAdapter listener =
        new VirtualFileAdapter() {
          @Override
          public void propertyChanged(@NotNull VirtualFilePropertyEvent event) {
            doUpdate();
          }

          @Override
          public void fileCreated(@NotNull VirtualFileEvent event) {
            doUpdate();
          }

          @Override
          public void fileDeleted(@NotNull VirtualFileEvent event) {
            doUpdate();
          }

          @Override
          public void fileMoved(@NotNull VirtualFileMoveEvent event) {
            doUpdate();
          }

          private void doUpdate() {
            queueUpdateFrom(getRootNode(), false);
          }
        };
    VirtualFileManager.getInstance().addVirtualFileListener(listener, this);
  }
  public FileDocumentManagerImpl(
      @NotNull VirtualFileManager virtualFileManager, @NotNull ProjectManager projectManager) {
    virtualFileManager.addVirtualFileListener(this);
    projectManager.addProjectManagerListener(this);

    myBus = ApplicationManager.getApplication().getMessageBus();
    InvocationHandler handler =
        new InvocationHandler() {
          @Nullable
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            multiCast(method, args);
            return null;
          }
        };

    final ClassLoader loader = FileDocumentManagerListener.class.getClassLoader();
    myMultiCaster =
        (FileDocumentManagerListener)
            Proxy.newProxyInstance(
                loader, new Class[] {FileDocumentManagerListener.class}, handler);
  }
 @Override
 @NotNull
 protected List<VirtualFile> getExternalAnnotationsRoots(@NotNull VirtualFile libraryFile) {
   final List<OrderEntry> entries =
       ProjectRootManager.getInstance(myPsiManager.getProject())
           .getFileIndex()
           .getOrderEntriesForFile(libraryFile);
   List<VirtualFile> result = new ArrayList<VirtualFile>();
   for (OrderEntry entry : entries) {
     if (entry instanceof ModuleOrderEntry) {
       continue;
     }
     final String[] externalUrls = AnnotationOrderRootType.getUrls(entry);
     for (String url : externalUrls) {
       VirtualFile root = VirtualFileManager.getInstance().findFileByUrl(url);
       if (root != null) {
         result.add(root);
       }
     }
   }
   return result;
 }
  public JavaSdkImpl(final VirtualFileManager fileManager, final FileTypeManager fileTypeManager) {
    super("JavaSDK");

    fileManager.addVirtualFileListener(
        new VirtualFileAdapter() {
          @Override
          public void fileDeleted(@NotNull VirtualFileEvent event) {
            updateCache(event);
          }

          @Override
          public void contentsChanged(@NotNull VirtualFileEvent event) {
            updateCache(event);
          }

          @Override
          public void fileCreated(@NotNull VirtualFileEvent event) {
            updateCache(event);
          }

          private void updateCache(VirtualFileEvent event) {
            final VirtualFile file = event.getFile();
            if (FileTypes.ARCHIVE.equals(
                fileTypeManager.getFileTypeByFileName(event.getFileName()))) {
              final String filePath = file.getPath();
              synchronized (myCachedVersionStrings) {
                for (String sdkHome : myCachedVersionStrings.keySet()) {
                  if (FileUtil.isAncestor(sdkHome, filePath, false)) {
                    myCachedVersionStrings.remove(sdkHome);
                    break;
                  }
                }
              }
            }
          }
        });
  }
  private void installPropertiesChangeListeners() {
    final VirtualFileManager virtualFileManager = VirtualFileManager.getInstance();
    if (myVfsListener != null) {
      assert false;
      virtualFileManager.removeVirtualFileListener(myVfsListener);
    }
    myVfsListener =
        new VirtualFileAdapter() {
          @Override
          public void fileCreated(@NotNull VirtualFileEvent event) {
            if (PropertiesImplUtil.isPropertiesFile(event.getFile(), myProject)) {
              recreateEditorsPanel();
            }
          }

          @Override
          public void fileDeleted(@NotNull VirtualFileEvent event) {
            for (PropertiesFile file : myEditors.keySet()) {
              if (Comparing.equal(file.getVirtualFile(), event.getFile())) {
                recreateEditorsPanel();
                return;
              }
            }
          }

          @Override
          public void propertyChanged(@NotNull VirtualFilePropertyEvent event) {
            if (PropertiesImplUtil.isPropertiesFile(event.getFile(), myProject)) {
              if (VirtualFile.PROP_NAME.equals(event.getPropertyName())) {
                recreateEditorsPanel();
              } else {
                updateEditorsFromProperties();
              }
            }
          }
        };

    virtualFileManager.addVirtualFileListener(myVfsListener, this);
    PsiTreeChangeAdapter psiTreeChangeAdapter =
        new PsiTreeChangeAdapter() {
          @Override
          public void childAdded(@NotNull PsiTreeChangeEvent event) {
            childrenChanged(event);
          }

          @Override
          public void childRemoved(@NotNull PsiTreeChangeEvent event) {
            childrenChanged(event);
          }

          @Override
          public void childReplaced(@NotNull PsiTreeChangeEvent event) {
            childrenChanged(event);
          }

          @Override
          public void childMoved(@NotNull PsiTreeChangeEvent event) {
            childrenChanged(event);
          }

          @Override
          public void childrenChanged(@NotNull PsiTreeChangeEvent event) {
            final PsiFile file = event.getFile();
            PropertiesFile propertiesFile = PropertiesImplUtil.getPropertiesFile(file);
            if (propertiesFile == null) return;
            if (!propertiesFile.getResourceBundle().equals(myResourceBundle)) return;
            updateEditorsFromProperties();
          }
        };
    PsiManager.getInstance(myProject).addPsiTreeChangeListener(psiTreeChangeAdapter, this);
  }
  @Override
  @SuppressWarnings("HardCodedStringLiteral")
  public void setupSdkPaths(@NotNull Sdk sdk) {
    String homePath = sdk.getHomePath();
    assert homePath != null : sdk;

    File jdkHome = new File(homePath);
    List<VirtualFile> classes = findClasses(jdkHome, false);
    VirtualFile sources = findSources(jdkHome);
    VirtualFile docs = findDocs(jdkHome, "docs/api");
    SdkModificator sdkModificator = sdk.getSdkModificator();

    Set<VirtualFile> previousRoots =
        new LinkedHashSet<>(Arrays.asList(sdkModificator.getRoots(OrderRootType.CLASSES)));
    sdkModificator.removeRoots(OrderRootType.CLASSES);
    previousRoots.removeAll(new HashSet<>(classes));
    for (VirtualFile aClass : classes) {
      sdkModificator.addRoot(aClass, OrderRootType.CLASSES);
    }
    for (VirtualFile root : previousRoots) {
      sdkModificator.addRoot(root, OrderRootType.CLASSES);
    }

    if (sources != null) {
      sdkModificator.addRoot(sources, OrderRootType.SOURCES);
    }
    VirtualFile javaFxSources = findSources(jdkHome, "javafx-src");
    if (javaFxSources != null) {
      sdkModificator.addRoot(javaFxSources, OrderRootType.SOURCES);
    }

    if (docs != null) {
      sdkModificator.addRoot(docs, JavadocOrderRootType.getInstance());
    } else if (SystemInfo.isMac) {
      VirtualFile commonDocs = findDocs(jdkHome, "docs");
      if (commonDocs == null) {
        commonDocs = findInJar(new File(jdkHome, "docs.jar"), "doc/api");
        if (commonDocs == null) {
          commonDocs = findInJar(new File(jdkHome, "docs.jar"), "docs/api");
        }
      }
      if (commonDocs != null) {
        sdkModificator.addRoot(commonDocs, JavadocOrderRootType.getInstance());
      }

      VirtualFile appleDocs = findDocs(jdkHome, "appledocs");
      if (appleDocs == null) {
        appleDocs = findInJar(new File(jdkHome, "appledocs.jar"), "appledoc/api");
      }
      if (appleDocs != null) {
        sdkModificator.addRoot(appleDocs, JavadocOrderRootType.getInstance());
      }

      if (commonDocs == null && appleDocs == null && sources == null) {
        String url = getDefaultDocumentationUrl(sdk);
        if (url != null) {
          sdkModificator.addRoot(
              VirtualFileManager.getInstance().findFileByUrl(url),
              JavadocOrderRootType.getInstance());
        }
      }
    } else if (getVersion(sdk) == JavaSdkVersion.JDK_1_7) {
      VirtualFile url =
          VirtualFileManager.getInstance().findFileByUrl("http://docs.oracle.com/javafx/2/api/");
      sdkModificator.addRoot(url, JavadocOrderRootType.getInstance());
    }

    attachJdkAnnotations(sdkModificator);

    sdkModificator.commitChanges();
  }