@Override
  public void contentsChanged(VirtualFileEvent event) {
    if (event.isFromSave()) return;
    final VirtualFile file = event.getFile();
    final Document document = getCachedDocument(file);
    if (document == null) {
      myMultiCaster.fileWithNoDocumentChanged(file);
      return;
    }

    if (isBinaryWithDecompiler(file)) {
      myMultiCaster.fileWithNoDocumentChanged(
          file); // This will generate PSI event at FileManagerImpl
    }

    long documentStamp = document.getModificationStamp();
    long oldFileStamp = event.getOldModificationStamp();
    if (documentStamp != oldFileStamp) {
      LOG.info("reload from disk?");
      LOG.info("  documentStamp:" + documentStamp);
      LOG.info("  oldFileStamp:" + oldFileStamp);

      if (file.isValid() && askReloadFromDisk(file, document)) {
        reloadFromDisk(document);
      }
    } else {
      reloadFromDisk(document);
    }
  }
  private void doSaveDocument(@NotNull final Document document)
      throws IOException, SaveVetoException {
    VirtualFile file = getFile(document);

    if (file == null
        || file instanceof LightVirtualFile
        || file.isValid() && !isFileModified(file)) {
      removeFromUnsaved(document);
      return;
    }

    if (file.isValid() && needsRefresh(file)) {
      file.refresh(false, false);
      if (!myUnsavedDocuments.contains(document)) return;
    }

    for (FileDocumentSynchronizationVetoer vetoer :
        Extensions.getExtensions(FileDocumentSynchronizationVetoer.EP_NAME)) {
      if (!vetoer.maySaveDocument(document)) {
        throw new SaveVetoException();
      }
    }

    final AccessToken token =
        ApplicationManager.getApplication().acquireWriteActionLock(getClass());
    try {
      doSaveDocumentInWriteAction(document, file);
    } finally {
      token.finish();
    }
  }
 @Override
 public void reloadFiles(final VirtualFile... files) {
   for (VirtualFile file : files) {
     if (file.exists()) {
       final Document doc = getCachedDocument(file);
       if (doc != null) {
         reloadFromDisk(doc);
       }
     }
   }
 }
  private static boolean isSaveNeeded(@NotNull Document document, @NotNull VirtualFile file)
      throws IOException {
    if (file.getFileType().isBinary()
        || document.getTextLength() > 1000 * 1000) { // don't compare if the file is too big
      return true;
    }

    byte[] bytes = file.contentsToByteArray();
    CharSequence loaded = LoadTextUtil.getTextByBinaryPresentation(bytes, file, false, false);

    return !Comparing.equal(document.getCharsSequence(), loaded);
  }
 @Override
 public boolean requestWriting(@NotNull Document document, Project project) {
   final VirtualFile file = getInstance().getFile(document);
   if (project != null && file != null && file.isValid()) {
     return !file.getFileType().isBinary()
         && ReadonlyStatusHandler.ensureFilesWritable(project, file);
   }
   if (document.isWritable()) {
     return true;
   }
   document.fireReadOnlyModificationAttempt();
   return false;
 }
  @Override
  @Nullable
  public Document getCachedDocument(@NotNull VirtualFile file) {
    Reference<Document> reference = file.getUserData(DOCUMENT_KEY);
    Document document = reference == null ? null : reference.get();

    if (document != null && isBinaryWithoutDecompiler(file)) {
      file.putUserData(DOCUMENT_KEY, null);
      document.putUserData(FILE_KEY, null);
      return null;
    }

    return document;
  }
 @Override
 public boolean isFileModified(@NotNull VirtualFile file) {
   final Document doc = getCachedDocument(file);
   return doc != null
       && isDocumentUnsaved(doc)
       && doc.getModificationStamp() != file.getModificationStamp();
 }
  protected boolean askReloadFromDisk(final VirtualFile file, final Document document) {
    ApplicationManager.getApplication().assertIsDispatchThread();
    if (!isDocumentUnsaved(document)) return true;

    String message = UIBundle.message("file.cache.conflict.message.text", file.getPresentableUrl());
    if (ApplicationManager.getApplication().isUnitTestMode()) throw new RuntimeException(message);
    final DialogBuilder builder = new DialogBuilder((Project) null);
    builder.setCenterPanel(new JLabel(message, Messages.getQuestionIcon(), SwingConstants.CENTER));
    builder.addOkAction().setText(UIBundle.message("file.cache.conflict.load.fs.changes.button"));
    builder
        .addCancelAction()
        .setText(UIBundle.message("file.cache.conflict.keep.memory.changes.button"));
    builder.addAction(
        new AbstractAction(UIBundle.message("file.cache.conflict.show.difference.button")) {
          @Override
          public void actionPerformed(ActionEvent e) {
            String title =
                UIBundle.message(
                    "file.cache.conflict.for.file.dialog.title", file.getPresentableUrl());
            final ProjectEx project =
                (ProjectEx) ProjectLocator.getInstance().guessProjectForFile(file);

            SimpleDiffRequest request = new SimpleDiffRequest(project, title);
            FileType fileType = file.getFileType();
            String fsContent = LoadTextUtil.loadText(file).toString();
            request.setContents(
                new SimpleContent(fsContent, fileType),
                new DocumentContent(project, document, fileType));
            request.setContentTitles(
                UIBundle.message("file.cache.conflict.diff.content.file.system.content"),
                UIBundle.message("file.cache.conflict.diff.content.memory.content"));
            DialogBuilder diffBuilder = new DialogBuilder(project);
            DiffPanelImpl diffPanel =
                (DiffPanelImpl)
                    DiffManager.getInstance()
                        .createDiffPanel(diffBuilder.getWindow(), project, diffBuilder, null);
            diffPanel.getOptions().setShowSourcePolicy(DiffPanelOptions.ShowSourcePolicy.DONT_SHOW);
            diffBuilder.setCenterPanel(diffPanel.getComponent());
            diffBuilder.setDimensionServiceKey("FileDocumentManager.FileCacheConflict");
            diffPanel.setDiffRequest(request);
            diffBuilder
                .addOkAction()
                .setText(UIBundle.message("file.cache.conflict.save.changes.button"));
            diffBuilder.addCancelAction();
            diffBuilder.setTitle(title);
            if (diffBuilder.show() == DialogWrapper.OK_EXIT_CODE) {
              builder.getDialogWrapper().close(DialogWrapper.CANCEL_EXIT_CODE);
            }
          }
        });
    builder.setTitle(UIBundle.message("file.cache.conflict.dialog.title"));
    builder.setButtonsAlignment(SwingConstants.CENTER);
    builder.setHelpId("reference.dialogs.fileCacheConflict");
    return builder.show() == 0;
  }
 public static void registerDocument(
     @NotNull final Document document, @NotNull VirtualFile virtualFile) {
   synchronized (lock) {
     virtualFile.putUserData(
         DOCUMENT_KEY,
         new SoftReference<Document>(document) {
           @Override
           public Document get() {
             return document;
           }
         });
     document.putUserData(FILE_KEY, virtualFile);
   }
 }
  private void doSaveDocumentInWriteAction(@NotNull Document document, @NotNull VirtualFile file)
      throws IOException {
    if (!file.isValid()) {
      removeFromUnsaved(document);
      return;
    }

    if (!file.equals(getFile(document))) {
      registerDocument(document, file);
    }

    if (!isSaveNeeded(document, file)) {
      if (document instanceof DocumentEx) {
        ((DocumentEx) document).setModificationStamp(file.getModificationStamp());
      }
      removeFromUnsaved(document);
      updateModifiedProperty(file);
      return;
    }

    myMultiCaster.beforeDocumentSaving(document);

    LOG.assertTrue(file.isValid());

    String text = document.getText();
    String lineSeparator = getLineSeparator(document, file);
    if (!lineSeparator.equals("\n")) {
      text = StringUtil.convertLineSeparators(text, lineSeparator);
    }

    Project project = ProjectLocator.getInstance().guessProjectForFile(file);
    LoadTextUtil.write(project, file, this, text, document.getModificationStamp());

    myUnsavedDocuments.remove(document);
    LOG.assertTrue(!myUnsavedDocuments.contains(document));
    myTrailingSpacesStripper.clearLineModificationFlags(document);
  }
  @Override
  @Nullable
  public Document getDocument(@NotNull final VirtualFile file) {
    DocumentEx document = (DocumentEx) getCachedDocument(file);
    if (document == null) {
      if (file.isDirectory()
          || isBinaryWithoutDecompiler(file)
          || SingleRootFileViewProvider.isTooLargeForContentLoading(file)) {
        return null;
      }
      final CharSequence text = LoadTextUtil.loadText(file);

      synchronized (lock) {
        document = (DocumentEx) getCachedDocument(file);
        if (document != null) return document; // Double checking

        document = (DocumentEx) createDocument(text);
        document.setModificationStamp(file.getModificationStamp());
        final FileType fileType = file.getFileType();
        document.setReadOnly(!file.isWritable() || fileType.isBinary());
        file.putUserData(DOCUMENT_KEY, new WeakReference<Document>(document));
        document.putUserData(FILE_KEY, file);

        if (!(file instanceof LightVirtualFile
            || file.getFileSystem() instanceof DummyFileSystem)) {
          document.addDocumentListener(
              new DocumentAdapter() {
                @Override
                public void documentChanged(DocumentEvent e) {
                  final Document document = e.getDocument();
                  myUnsavedDocuments.add(document);
                  final Runnable currentCommand =
                      CommandProcessor.getInstance().getCurrentCommand();
                  Project project =
                      currentCommand == null
                          ? null
                          : CommandProcessor.getInstance().getCurrentCommandProject();
                  String lineSeparator = CodeStyleFacade.getInstance(project).getLineSeparator();
                  document.putUserData(LINE_SEPARATOR_KEY, lineSeparator);

                  // avoid documents piling up during batch processing
                  if (areTooManyDocumentsInTheQueue(myUnsavedDocuments)) {
                    saveAllDocumentsLater();
                  }
                }
              });
        }
      }

      myMultiCaster.fileContentLoaded(file, document);
    }

    return document;
  }
 private static boolean isBinaryWithoutDecompiler(VirtualFile file) {
   final FileType ft = file.getFileType();
   return ft.isBinary() && BinaryFileTypeDecompilers.INSTANCE.forFileType(ft) == null;
 }
 private static boolean needsRefresh(final VirtualFile file) {
   final VirtualFileSystem fs = file.getFileSystem();
   return fs instanceof NewVirtualFileSystem
       && file.getTimeStamp() != ((NewVirtualFileSystem) fs).getTimeStamp(file);
 }