6a7bb8c1b3e64e06898320e1629af07770f36115
[idea/community.git] / python / educational-core / student / src / com / jetbrains / edu / learning / core / EduUtils.java
1 package com.jetbrains.edu.learning.core;
2
3 import com.intellij.ide.SaveAndSyncHandler;
4 import com.intellij.openapi.actionSystem.AnActionEvent;
5 import com.intellij.openapi.actionSystem.Presentation;
6 import com.intellij.openapi.application.ApplicationManager;
7 import com.intellij.openapi.application.Result;
8 import com.intellij.openapi.command.CommandProcessor;
9 import com.intellij.openapi.command.UndoConfirmationPolicy;
10 import com.intellij.openapi.command.WriteCommandAction;
11 import com.intellij.openapi.command.undo.UndoManager;
12 import com.intellij.openapi.command.undo.UndoableAction;
13 import com.intellij.openapi.diagnostic.Logger;
14 import com.intellij.openapi.editor.Document;
15 import com.intellij.openapi.fileEditor.FileDocumentManager;
16 import com.intellij.openapi.project.Project;
17 import com.intellij.openapi.util.Pair;
18 import com.intellij.openapi.util.TextRange;
19 import com.intellij.openapi.vfs.VfsUtilCore;
20 import com.intellij.openapi.vfs.VirtualFile;
21 import com.intellij.openapi.vfs.VirtualFileManager;
22 import com.intellij.psi.PsiDirectory;
23 import com.jetbrains.edu.learning.StudyUtils;
24 import com.jetbrains.edu.learning.courseFormat.*;
25 import org.jetbrains.annotations.NonNls;
26 import org.jetbrains.annotations.NotNull;
27 import org.jetbrains.annotations.Nullable;
28
29 import javax.imageio.ImageIO;
30 import java.io.FileOutputStream;
31 import java.io.IOException;
32 import java.io.PrintWriter;
33 import java.util.*;
34
35 public class EduUtils {
36   private EduUtils() {
37   }
38
39   private static final Logger LOG = Logger.getInstance(EduUtils.class.getName());
40
41   public static final Comparator<StudyItem> INDEX_COMPARATOR = (o1, o2) -> o1.getIndex() - o2.getIndex();
42
43   public static void enableAction(@NotNull final AnActionEvent event, boolean isEnable) {
44     final Presentation presentation = event.getPresentation();
45     presentation.setVisible(isEnable);
46     presentation.setEnabled(isEnable);
47   }
48
49   /**
50    * Gets number index in directory names like "task1", "lesson2"
51    *
52    * @param fullName    full name of directory
53    * @param logicalName part of name without index
54    * @return index of object
55    */
56   public static int getIndex(@NotNull final String fullName, @NotNull final String logicalName) {
57     if (!fullName.startsWith(logicalName)) {
58       return -1;
59     }
60     try {
61       return Integer.parseInt(fullName.substring(logicalName.length())) - 1;
62     }
63     catch (NumberFormatException e) {
64       return -1;
65     }
66   }
67
68   public static boolean indexIsValid(int index, Collection collection) {
69     int size = collection.size();
70     return index >= 0 && index < size;
71   }
72
73   @SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
74   @Nullable
75   public static VirtualFile flushWindows(@NotNull final TaskFile taskFile, @NotNull final VirtualFile file) {
76     final VirtualFile taskDir = file.getParent();
77     VirtualFile fileWindows = null;
78     final Document document = FileDocumentManager.getInstance().getDocument(file);
79     if (document == null) {
80       LOG.debug("Couldn't flush windows");
81       return null;
82     }
83     if (taskDir != null) {
84       final String name = file.getNameWithoutExtension() + EduNames.WINDOWS_POSTFIX;
85       deleteWindowsFile(taskDir, name);
86       PrintWriter printWriter = null;
87       try {
88         fileWindows = taskDir.createChildData(taskFile, name);
89         printWriter = new PrintWriter(new FileOutputStream(fileWindows.getPath()));
90         for (AnswerPlaceholder answerPlaceholder : taskFile.getActivePlaceholders()) {
91           int length = answerPlaceholder.getRealLength();
92           int start = answerPlaceholder.getOffset();
93           final String windowDescription = document.getText(new TextRange(start, start + length));
94           printWriter.println("#educational_plugin_window = " + windowDescription);
95         }
96         ApplicationManager.getApplication().runWriteAction(() -> FileDocumentManager.getInstance().saveDocument(document));
97       }
98       catch (IOException e) {
99         LOG.error(e);
100       }
101       finally {
102         if (printWriter != null) {
103           printWriter.close();
104         }
105         synchronize();
106       }
107     }
108     return fileWindows;
109   }
110
111   public static void synchronize() {
112     FileDocumentManager.getInstance().saveAllDocuments();
113     SaveAndSyncHandler.getInstance().refreshOpenFiles();
114     VirtualFileManager.getInstance().refreshWithoutFileWatcher(true);
115   }
116
117
118   public static VirtualFile copyFile(Object requestor, VirtualFile toDir, VirtualFile file) {
119     Document document = FileDocumentManager.getInstance().getDocument(file);
120     if (document != null) {
121       FileDocumentManager.getInstance().saveDocument(document);
122     }
123     String name = file.getName();
124     try {
125       VirtualFile userFile = toDir.findChild(name);
126       if (userFile != null) {
127         userFile.delete(requestor);
128       }
129       return VfsUtilCore.copyFile(requestor, file, toDir);
130     }
131     catch (IOException e) {
132       LOG.info("Failed to create file " + name + "  in folder " + toDir.getPath(), e);
133     }
134     return null;
135   }
136
137   @Nullable
138   public static Pair<VirtualFile, TaskFile> createStudentFile(Object requestor,
139                                                               Project project,
140                                                               VirtualFile answerFile,
141                                                               VirtualFile parentDir,
142                                                               @Nullable Task task) {
143
144     VirtualFile studentFile = copyFile(requestor, parentDir, answerFile);
145     if (studentFile == null) {
146       return null;
147     }
148     Document studentDocument = FileDocumentManager.getInstance().getDocument(studentFile);
149     if (studentDocument == null) {
150       return null;
151     }
152     if (task == null) {
153       task = StudyUtils.getTaskForFile(project, answerFile);
154       if (task == null) {
155         return null;
156       }
157       task = task.copy();
158     }
159     TaskFile taskFile = task.getTaskFile(answerFile.getName());
160     if (taskFile == null) {
161       return null;
162     }
163     EduDocumentListener listener = new EduDocumentListener(taskFile, false);
164     studentDocument.addDocumentListener(listener);
165
166     for (AnswerPlaceholder placeholder : taskFile.getActivePlaceholders()) {
167       replaceAnswerPlaceholder(project, studentDocument, placeholder, placeholder.getRealLength(), placeholder.getTaskText());
168     }
169     studentDocument.removeDocumentListener(listener);
170     return Pair.create(studentFile, taskFile);
171   }
172
173   public static void replaceAnswerPlaceholder(@NotNull final Project project,
174                                               @NotNull final Document document,
175                                               @NotNull final AnswerPlaceholder answerPlaceholder,
176                                               int length,
177                                               String replacementText) {
178     final int offset = answerPlaceholder.getOffset();
179     CommandProcessor.getInstance().executeCommand(project, () -> ApplicationManager.getApplication().runWriteAction(() -> {
180       document.replaceString(offset, offset + length, replacementText);
181       FileDocumentManager.getInstance().saveDocument(document);
182     }), "Replace Answer Placeholders", "Replace Answer Placeholders");
183   }
184
185   public static void deleteWindowDescriptions(@NotNull final Task task, @NotNull final VirtualFile taskDir) {
186     for (Map.Entry<String, TaskFile> entry : task.getTaskFiles().entrySet()) {
187       String name = entry.getKey();
188       VirtualFile virtualFile = taskDir.findChild(name);
189       if (virtualFile == null) {
190         continue;
191       }
192       String windowsFileName = virtualFile.getNameWithoutExtension() + EduNames.WINDOWS_POSTFIX;
193       deleteWindowsFile(taskDir, windowsFileName);
194     }
195   }
196
197   private static void deleteWindowsFile(@NotNull final VirtualFile taskDir, @NotNull final String name) {
198     final VirtualFile fileWindows = taskDir.findChild(name);
199     if (fileWindows != null && fileWindows.exists()) {
200       ApplicationManager.getApplication().runWriteAction(() -> {
201         try {
202           fileWindows.delete(taskDir);
203         }
204         catch (IOException e) {
205           LOG.warn("Tried to delete non existed _windows file");
206         }
207       });
208     }
209   }
210
211   @Nullable
212   public static Task getTask(@NotNull final PsiDirectory directory, @NotNull final Course course) {
213     PsiDirectory lessonDir = directory.getParent();
214     if (lessonDir == null) {
215       return null;
216     }
217     Lesson lesson = course.getLesson(lessonDir.getName());
218     if (lesson == null) {
219       return null;
220     }
221     return lesson.getTask(directory.getName());
222   }
223
224   public static boolean isImage(String fileName) {
225     final String[] readerFormatNames = ImageIO.getReaderFormatNames();
226     for (@NonNls String format : readerFormatNames) {
227       final String ext = format.toLowerCase();
228       if (fileName.endsWith(ext)) {
229         return true;
230       }
231     }
232     return false;
233   }
234
235   public static void runUndoableAction(Project project, String name, UndoableAction action) {
236     runUndoableAction(project, name, action, UndoConfirmationPolicy.DO_NOT_REQUEST_CONFIRMATION);
237   }
238
239   public static void runUndoableAction(Project project, String name, UndoableAction action, UndoConfirmationPolicy confirmationPolicy) {
240     new WriteCommandAction(project, name) {
241       protected void run(@NotNull final Result result) throws Throwable {
242         action.redo();
243         UndoManager.getInstance(project).undoableActionPerformed(action);
244       }
245
246       @Override
247       protected UndoConfirmationPolicy getUndoConfirmationPolicy() {
248         return confirmationPolicy;
249       }
250     }.execute();
251   }
252 }