<commit_before>package org.plantuml.idea.toolwindow;

import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.editor.event.DocumentListener;
import com.intellij.openapi.fileChooser.FileChooserFactory;
import com.intellij.openapi.fileChooser.FileSaverDescriptor;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.FileEditorManagerEvent;
import com.intellij.openapi.fileEditor.FileEditorManagerListener;
import com.intellij.openapi.ide.CopyPasteManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileWrapper;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowFactory;
import com.intellij.ui.content.Content;
import com.intellij.ui.content.ContentFactory;
import com.intellij.util.messages.MessageBus;
import org.plantuml.idea.plantuml.PlantUml;
import org.plantuml.idea.plantuml.PlantUmlResult;
import org.plantuml.idea.util.LazyApplicationPoolExecutor;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * @author <NAME>
 */

public class PlantUmlToolWindowFactory implements ToolWindowFactory {
    Logger logger = Logger.getInstance(PlantUmlToolWindowFactory.class);
    private Project myProject;
    private ToolWindow toolWindow;
    private JPanel mainPanel;
    private JLabel imageLabel;
    private BufferedImage diagram;
    private Document currentDocument;
    private JButton copyToClipboard;
    private JButton saveToFile;
    private FileEditorManagerListener plantUmlVirtualFileListener = new PlantUmlFileManagerListener();
    private DocumentListener plantUmlDocumentListener = new PlantUmlDocumentListener();

    private LazyApplicationPoolExecutor lazyExecutor = new LazyApplicationPoolExecutor();

    public PlantUmlToolWindowFactory() {

    }

    public void createToolWindowContent(Project project, ToolWindow toolWindow) {
        myProject = project;
        this.toolWindow = toolWindow;

        createUI();

        registerListeners();

        renderSelectedDocument();

    }

    private void createUI() {
        ContentFactory contentFactory = ContentFactory.SERVICE.getInstance();
        Content content = contentFactory.createContent(mainPanel, "", false);
        toolWindow.getContentManager().addContent(content);


        copyToClipboard.addActionListener(new copyToClipboardActionListener());
        saveToFile.addActionListener(new saveToFileActionListener());
    }

    private void renderSelectedDocument() {
        Editor selectedTextEditor = FileEditorManager.getInstance(myProject).getSelectedTextEditor();
        if (selectedTextEditor != null) {
            lazyRender(selectedTextEditor.getDocument());
        }
    }


    private void registerListeners() {
        logger.debug("Registering listeners");
        MessageBus messageBus = myProject.getMessageBus();
        messageBus.connect().subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, plantUmlVirtualFileListener);

        EditorFactory.getInstance().getEventMulticaster().addDocumentListener(plantUmlDocumentListener);
    }

    private void setDiagram(BufferedImage image) {
        if (image != null) {
            diagram = image;
            imageLabel.setIcon(new ImageIcon(diagram));
            imageLabel.setPreferredSize(new Dimension(diagram.getWidth(), diagram.getHeight()));
        }
    }

    private class PlantUmlFileManagerListener implements FileEditorManagerListener {
        public void fileOpened(FileEditorManager source, VirtualFile file) {
            logger.debug("file opened " + file);
        }

        public void fileClosed(FileEditorManager source, VirtualFile file) {
            logger.debug("file closed = " + file);
        }

        public void selectionChanged(FileEditorManagerEvent event) {
            logger.debug("selection changed" + event);

            VirtualFile newFile = event.getNewFile();
            if (newFile != null) {
                Document document = FileDocumentManager.getInstance().getDocument(newFile);
                if (document != null)
                    lazyRender(document);
            }

        }
    }

    private class PlantUmlDocumentListener implements DocumentListener {
        public void beforeDocumentChange(DocumentEvent event) {
            // nothing

        }

        public void documentChanged(DocumentEvent event) {
            logger.debug("document changed " + event);
            lazyRender(event.getDocument());
        }
    }

    private void lazyRender(final Document document) {
        currentDocument = document;
        lazyExecutor.execute(new Runnable() {
            public void run() {
                render(document.getText());
            }
        });
    }

    private void render(String source) {
        PlantUmlResult result = PlantUml.render(source);
        try {
            final BufferedImage image = getBufferedImage(result.getDiagramBytes());
            ApplicationManager.getApplication().invokeLater(new Runnable() {
                public void run() {
                    setDiagram(image);
                }
            });
        } catch (IOException e) {
            logger.warn("Exception occurred rendering source = " + source + ": " + e);
        }

    }

    private static BufferedImage getBufferedImage(byte[] imageBytes) throws IOException {
        ByteArrayInputStream input = new ByteArrayInputStream(imageBytes);
        BufferedImage bufferedImage;
        try {
            bufferedImage = ImageIO.read(input);
        } finally {<commit_msg>Issue #18 Race condition between document.getText and autocompletion functionality<commit_after>package org.plantuml.idea.toolwindow;

import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.editor.event.DocumentListener;
import com.intellij.openapi.fileChooser.FileChooserFactory;
import com.intellij.openapi.fileChooser.FileSaverDescriptor;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.FileEditorManagerEvent;
import com.intellij.openapi.fileEditor.FileEditorManagerListener;
import com.intellij.openapi.ide.CopyPasteManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileWrapper;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowFactory;
import com.intellij.ui.content.Content;
import com.intellij.ui.content.ContentFactory;
import com.intellij.util.messages.MessageBus;
import org.plantuml.idea.plantuml.PlantUml;
import org.plantuml.idea.plantuml.PlantUmlResult;
import org.plantuml.idea.util.LazyApplicationPoolExecutor;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * @author <NAME>
 */

public class PlantUmlToolWindowFactory implements ToolWindowFactory {
    Logger logger = Logger.getInstance(PlantUmlToolWindowFactory.class);
    private Project myProject;
    private ToolWindow toolWindow;
    private JPanel mainPanel;
    private JLabel imageLabel;
    private BufferedImage diagram;
    private Document currentDocument;
    private JButton copyToClipboard;
    private JButton saveToFile;
    private FileEditorManagerListener plantUmlVirtualFileListener = new PlantUmlFileManagerListener();
    private DocumentListener plantUmlDocumentListener = new PlantUmlDocumentListener();

    private LazyApplicationPoolExecutor lazyExecutor = new LazyApplicationPoolExecutor();

    public PlantUmlToolWindowFactory() {

    }

    public void createToolWindowContent(Project project, ToolWindow toolWindow) {
        myProject = project;
        this.toolWindow = toolWindow;

        createUI();

        registerListeners();

        renderSelectedDocument();

    }

    private void createUI() {
        ContentFactory contentFactory = ContentFactory.SERVICE.getInstance();
        Content content = contentFactory.createContent(mainPanel, "", false);
        toolWindow.getContentManager().addContent(content);


        copyToClipboard.addActionListener(new copyToClipboardActionListener());
        saveToFile.addActionListener(new saveToFileActionListener());
    }

    private void renderSelectedDocument() {
        Editor selectedTextEditor = FileEditorManager.getInstance(myProject).getSelectedTextEditor();
        if (selectedTextEditor != null) {
            lazyRender(selectedTextEditor.getDocument());
        }
    }


    private void registerListeners() {
        logger.debug("Registering listeners");
        MessageBus messageBus = myProject.getMessageBus();
        messageBus.connect().subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, plantUmlVirtualFileListener);

        EditorFactory.getInstance().getEventMulticaster().addDocumentListener(plantUmlDocumentListener);
    }

    private void setDiagram(BufferedImage image) {
        if (image != null) {
            diagram = image;
            imageLabel.setIcon(new ImageIcon(diagram));
            imageLabel.setPreferredSize(new Dimension(diagram.getWidth(), diagram.getHeight()));
        }
    }

    private class PlantUmlFileManagerListener implements FileEditorManagerListener {
        public void fileOpened(FileEditorManager source, VirtualFile file) {
            logger.debug("file opened " + file);
        }

        public void fileClosed(FileEditorManager source, VirtualFile file) {
            logger.debug("file closed = " + file);
        }

        public void selectionChanged(FileEditorManagerEvent event) {
            logger.debug("selection changed" + event);

            VirtualFile newFile = event.getNewFile();
            if (newFile != null) {
                Document document = FileDocumentManager.getInstance().getDocument(newFile);
                if (document != null)
                    lazyRender(document);
            }

        }
    }

    private class PlantUmlDocumentListener implements DocumentListener {
        public void beforeDocumentChange(DocumentEvent event) {
            // nothing

        }

        public void documentChanged(DocumentEvent event) {
            logger.debug("document changed " + event);
            lazyRender(event.getDocument());
        }
    }

    private void lazyRender(final Document document) {
        currentDocument = document;
        ApplicationManager.getApplication().runReadAction(new Runnable() {
            @Override
            public void run() {
                final String source = document.getText();
                lazyExecutor.execute(new Runnable() {
                    public void run() {
                        render(source);
                    }
                });
            }
        });
    }

    private void render(String source) {
        PlantUmlResult result = PlantUml.render(source);
        try {
            final BufferedImage image = getBufferedImage(result.getDiagramBytes());
            ApplicationManager.getApplication().invokeLater(new Runnable() {
                public void run() {
                    setDiagram(image);
                }
            });
        } catch (IOException e) {
            logger.warn("Exception occurred rendering source = " + source + ": " + e);
        }

    }

    private static BufferedImage getBufferedImage(byte[] imageBytes) throws IOException {
        ByteArrayInputStream input = new ByteArrayInputStream(imageBytes);
        BufferedImage bufferedImage;
        try {
            bufferedImage = ImageIO.read(input);
        } finally {
{'fraction_non_alphanumeric': 0.08683645289966248, 'fraction_numerical': 0.00015342129487572875, 'mean_word_length': 3.329790767186981, 'pattern_counts': {'":': 2, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1791643', 'n_tokens_mistral': 3432, 'n_tokens_neox': 3357, 'n_words': 642}