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