create course archive 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.jetbrains.edu.learning.StudySubtaskUtils;
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                                                               int toSubtaskIndex) {
145
146     VirtualFile studentFile = copyFile(requestor, parentDir, answerFile);
147     if (studentFile == null) {
148       return null;
149     }
150     Document studentDocument = FileDocumentManager.getInstance().getDocument(studentFile);
151     if (studentDocument == null) {
152       return null;
153     }
154     if (task == null) {
155       task = StudyUtils.getTaskForFile(project, answerFile);
156       if (task == null) {
157         return null;
158       }
159       task = task.copy();
160     }
161     TaskFile taskFile = task.getTaskFile(answerFile.getName());
162     if (taskFile == null) {
163       return null;
164     }
165     EduDocumentListener listener = new EduDocumentListener(taskFile, false);
166     studentDocument.addDocumentListener(listener);
167     StudySubtaskUtils.updatePlaceholderTexts(project, studentDocument, taskFile, task.getActiveSubtaskIndex(), toSubtaskIndex);
168     studentDocument.removeDocumentListener(listener);
169     return Pair.create(studentFile, taskFile);
170   }
171
172   public static void replaceAnswerPlaceholder(@NotNull final Project project,
173                                               @NotNull final Document document,
174                                               @NotNull final AnswerPlaceholder answerPlaceholder,
175                                               int length,
176                                               String replacementText) {
177     final int offset = answerPlaceholder.getOffset();
178     CommandProcessor.getInstance().executeCommand(project, () -> ApplicationManager.getApplication().runWriteAction(() -> {
179       document.replaceString(offset, offset + length, replacementText);
180       FileDocumentManager.getInstance().saveDocument(document);
181     }), "Replace Answer Placeholders", "Replace Answer Placeholders");
182   }
183
184   public static void deleteWindowDescriptions(@NotNull final Task task, @NotNull final VirtualFile taskDir) {
185     for (Map.Entry<String, TaskFile> entry : task.getTaskFiles().entrySet()) {
186       String name = entry.getKey();
187       VirtualFile virtualFile = taskDir.findChild(name);
188       if (virtualFile == null) {
189         continue;
190       }
191       String windowsFileName = virtualFile.getNameWithoutExtension() + EduNames.WINDOWS_POSTFIX;
192       deleteWindowsFile(taskDir, windowsFileName);
193     }
194   }
195
196   private static void deleteWindowsFile(@NotNull final VirtualFile taskDir, @NotNull final String name) {
197     final VirtualFile fileWindows = taskDir.findChild(name);
198     if (fileWindows != null && fileWindows.exists()) {
199       ApplicationManager.getApplication().runWriteAction(() -> {
200         try {
201           fileWindows.delete(taskDir);
202         }
203         catch (IOException e) {
204           LOG.warn("Tried to delete non existed _windows file");
205         }
206       });
207     }
208   }
209
210   @Nullable
211   public static Task getTask(@NotNull final PsiDirectory directory, @NotNull final Course course) {
212     PsiDirectory lessonDir = directory.getParent();
213     if (lessonDir == null) {
214       return null;
215     }
216     Lesson lesson = course.getLesson(lessonDir.getName());
217     if (lesson == null) {
218       return null;
219     }
220     return lesson.getTask(directory.getName());
221   }
222
223   public static boolean isImage(String fileName) {
224     final String[] readerFormatNames = ImageIO.getReaderFormatNames();
225     for (@NonNls String format : readerFormatNames) {
226       final String ext = format.toLowerCase();
227       if (fileName.endsWith(ext)) {
228         return true;
229       }
230     }
231     return false;
232   }
233
234   public static void runUndoableAction(Project project, String name, UndoableAction action) {
235     runUndoableAction(project, name, action, UndoConfirmationPolicy.DO_NOT_REQUEST_CONFIRMATION);
236   }
237
238   public static void runUndoableAction(Project project, String name, UndoableAction action, UndoConfirmationPolicy confirmationPolicy) {
239     new WriteCommandAction(project, name) {
240       protected void run(@NotNull final Result result) throws Throwable {
241         action.redo();
242         UndoManager.getInstance(project).undoableActionPerformed(action);
243       }
244
245       @Override
246       protected UndoConfirmationPolicy getUndoConfirmationPolicy() {
247         return confirmationPolicy;
248       }
249     }.execute();
250   }
251 }