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