9f5738cbe162bdb34273546861a9487c8d1840f2
[idea/community.git] / python / educational / src / com / jetbrains / edu / EduUtils.java
1 package com.jetbrains.edu;
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.TextRange;
13 import com.intellij.openapi.util.io.FileUtilRt;
14 import com.intellij.openapi.vfs.VirtualFile;
15 import com.intellij.openapi.vfs.VirtualFileManager;
16 import com.intellij.psi.PsiDirectory;
17 import com.intellij.util.containers.HashMap;
18 import com.jetbrains.edu.courseFormat.AnswerPlaceholder;
19 import com.jetbrains.edu.courseFormat.Course;
20 import com.jetbrains.edu.courseFormat.Lesson;
21 import com.jetbrains.edu.courseFormat.StudyItem;
22 import com.jetbrains.edu.courseFormat.Task;
23 import com.jetbrains.edu.courseFormat.TaskFile;
24 import com.jetbrains.edu.oldCourseFormat.OldCourse;
25 import com.jetbrains.edu.oldCourseFormat.TaskWindow;
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.ArrayList;
35 import java.util.Collection;
36 import java.util.Comparator;
37 import java.util.Map;
38
39 public class EduUtils {
40   private EduUtils() {
41   }
42   private static final Logger LOG = Logger.getInstance(EduUtils.class.getName());
43
44   public static Comparator<StudyItem> INDEX_COMPARATOR = new Comparator<StudyItem>() {
45     @Override
46     public int compare(StudyItem o1, StudyItem o2) {
47       return o1.getIndex() - o2.getIndex();
48     }
49   };
50
51   public static void enableAction(@NotNull final AnActionEvent event, boolean isEnable) {
52     final Presentation presentation = event.getPresentation();
53     presentation.setVisible(isEnable);
54     presentation.setEnabled(isEnable);
55   }
56
57   /**
58    * Gets number index in directory names like "task1", "lesson2"
59    *
60    * @param fullName    full name of directory
61    * @param logicalName part of name without index
62    * @return index of object
63    */
64   public static int getIndex(@NotNull final String fullName, @NotNull final String logicalName) {
65     if (!fullName.startsWith(logicalName)) {
66       return -1;
67     }
68     try {
69       return Integer.parseInt(fullName.substring(logicalName.length())) - 1;
70     } catch(NumberFormatException e) {
71       return -1;
72     }
73   }
74
75   public static boolean indexIsValid(int index, Collection collection) {
76     int size = collection.size();
77     return index >= 0 && index < size;
78   }
79
80   @SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
81   @Nullable
82   public static VirtualFile flushWindows(@NotNull final TaskFile taskFile, @NotNull final VirtualFile file,
83                                          boolean useLength) {
84     final VirtualFile taskDir = file.getParent();
85     VirtualFile fileWindows = null;
86     final Document document = FileDocumentManager.getInstance().getDocument(file);
87     if (document == null) {
88       LOG.debug("Couldn't flush windows");
89       return null;
90     }
91     if (taskDir != null) {
92       final String name = file.getNameWithoutExtension() + EduNames.WINDOWS_POSTFIX;
93       deleteWindowsFile(taskDir, name);
94       PrintWriter printWriter = null;
95       try {
96         fileWindows = taskDir.createChildData(taskFile, name);
97         printWriter = new PrintWriter(new FileOutputStream(fileWindows.getPath()));
98         for (AnswerPlaceholder answerPlaceholder : taskFile.getAnswerPlaceholders()) {
99           int length = useLength ? answerPlaceholder.getLength() : answerPlaceholder.getPossibleAnswerLength();
100           if (!answerPlaceholder.isValid(document, length)) {
101             printWriter.println("#educational_plugin_window = ");
102             continue;
103           }
104           int start = answerPlaceholder.getRealStartOffset(document);
105           final String windowDescription = document.getText(new TextRange(start, start + length));
106           printWriter.println("#educational_plugin_window = " + windowDescription);
107         }
108         ApplicationManager.getApplication().runWriteAction(new Runnable() {
109           @Override
110           public void run() {
111             FileDocumentManager.getInstance().saveDocument(document);
112           }
113         });
114       }
115       catch (IOException e) {
116         LOG.error(e);
117       }
118       finally {
119         if (printWriter != null) {
120           printWriter.close();
121         }
122         synchronize();
123       }
124     }
125     return fileWindows;
126   }
127
128   public static void synchronize() {
129     FileDocumentManager.getInstance().saveAllDocuments();
130     SaveAndSyncHandler.getInstance().refreshOpenFiles();
131     VirtualFileManager.getInstance().refreshWithoutFileWatcher(true);
132   }
133
134   public static void createStudentFileFromAnswer(@NotNull final Project project,
135                                                  @NotNull final VirtualFile userFileDir,
136                                                  @NotNull final VirtualFile answerFileDir,
137                                                  @NotNull final String taskFileName, @NotNull final TaskFile taskFile) {
138     VirtualFile file = userFileDir.findChild(taskFileName);
139     if (file != null) {
140       try {
141         file.delete(project);
142       }
143       catch (IOException e) {
144         LOG.error(e);
145       }
146     }
147
148     if (taskFile.getAnswerPlaceholders().isEmpty()) {
149       String extension = FileUtilRt.getExtension(taskFileName);
150       String nameWithoutExtension = FileUtilRt.getNameWithoutExtension(taskFileName);
151       VirtualFile answerFile = answerFileDir.findChild(nameWithoutExtension + ".answer." + extension);
152       if (answerFile != null) {
153         try {
154           answerFile.copy(answerFileDir, userFileDir, taskFileName);
155         }
156         catch (IOException e) {
157           LOG.error(e);
158         }
159       }
160       return;
161     }
162     try {
163       userFileDir.createChildData(project, taskFileName);
164     }
165     catch (IOException e) {
166       LOG.error(e);
167     }
168
169     file = userFileDir.findChild(taskFileName);
170     assert file != null;
171     String answerFileName = file.getNameWithoutExtension() + ".answer." + file.getExtension();
172     VirtualFile answerFile = answerFileDir.findChild(answerFileName);
173     if (answerFile == null) {
174       return;
175     }
176     final Document answerDocument = FileDocumentManager.getInstance().getDocument(answerFile);
177     if (answerDocument == null) {
178       return;
179     }
180     final Document document = FileDocumentManager.getInstance().getDocument(file);
181     if (document == null) return;
182
183     CommandProcessor.getInstance().executeCommand(project, new Runnable() {
184       @Override
185       public void run() {
186         ApplicationManager.getApplication().runWriteAction(new Runnable() {
187           @Override
188           public void run() {
189             document.replaceString(0, document.getTextLength(), answerDocument.getCharsSequence());
190             FileDocumentManager.getInstance().saveDocument(document);
191           }
192         });
193       }
194     }, "Create Student File", "Create Student File");
195     EduDocumentListener listener = new EduDocumentListener(taskFile, false);
196     document.addDocumentListener(listener);
197     taskFile.sortAnswerPlaceholders();
198     for (int i = taskFile.getAnswerPlaceholders().size() - 1; i >= 0; i--) {
199       final AnswerPlaceholder answerPlaceholder = taskFile.getAnswerPlaceholders().get(i);
200       if (answerPlaceholder.getRealStartOffset(document) > document.getTextLength() || answerPlaceholder.getRealStartOffset(document) + answerPlaceholder.getPossibleAnswerLength() > document.getTextLength()) {
201         LOG.error("Wrong startOffset: " + answerPlaceholder.getRealStartOffset(document) + "; document: " + file.getPath());
202         return;
203       }
204       replaceAnswerPlaceholder(project, document, answerPlaceholder);
205     }
206     CommandProcessor.getInstance().executeCommand(project, new Runnable() {
207       @Override
208       public void run() {
209         ApplicationManager.getApplication().runWriteAction(new Runnable() {
210           @Override
211           public void run() {
212             FileDocumentManager.getInstance().saveDocument(document);
213           }
214         });
215       }
216     }, "Create Student File", "Create Student File");
217     document.removeDocumentListener(listener);
218   }
219
220   private static void replaceAnswerPlaceholder(@NotNull final Project project,
221                                                @NotNull final Document document,
222                                                @NotNull final AnswerPlaceholder answerPlaceholder) {
223     final String taskText = answerPlaceholder.getTaskText();
224     final int offset = answerPlaceholder.getRealStartOffset(document);
225     CommandProcessor.getInstance().executeCommand(project, new Runnable() {
226       @Override
227       public void run() {
228         ApplicationManager.getApplication().runWriteAction(new Runnable() {
229           @Override
230           public void run() {
231             document.replaceString(offset, offset + answerPlaceholder.getPossibleAnswerLength(), taskText);
232             FileDocumentManager.getInstance().saveDocument(document);
233           }
234         });
235       }
236     }, "Replace Answer Placeholders", "Replace Answer Placeholders");
237   }
238
239   public static void deleteWindowDescriptions(@NotNull final Task task, @NotNull final VirtualFile taskDir) {
240     for (Map.Entry<String, TaskFile> entry : task.getTaskFiles().entrySet()) {
241       String name = entry.getKey();
242       VirtualFile virtualFile = taskDir.findChild(name);
243       if (virtualFile == null) {
244         continue;
245       }
246       String windowsFileName = virtualFile.getNameWithoutExtension() + EduNames.WINDOWS_POSTFIX;
247       deleteWindowsFile(taskDir, windowsFileName);
248     }
249   }
250
251   private static void deleteWindowsFile(@NotNull final VirtualFile taskDir, @NotNull final String name) {
252     final VirtualFile fileWindows = taskDir.findChild(name);
253     if (fileWindows != null && fileWindows.exists()) {
254       ApplicationManager.getApplication().runWriteAction(new Runnable() {
255         @Override
256         public void run() {
257           try {
258             fileWindows.delete(taskDir);
259           }
260           catch (IOException e) {
261             LOG.warn("Tried to delete non existed _windows file");
262           }
263         }
264       });
265     }
266   }
267
268   @Nullable
269   public static Task getTask(@NotNull final PsiDirectory directory, @NotNull final Course course) {
270     PsiDirectory lessonDir = directory.getParent();
271     if (lessonDir == null) {
272       return null;
273     }
274     Lesson lesson = course.getLesson(lessonDir.getName());
275     if (lesson == null) {
276       return null;
277     }
278     return lesson.getTask(directory.getName());
279   }
280
281   @NotNull
282   public static Course transformOldCourse(@NotNull final OldCourse oldCourse) {
283     Course course = new Course();
284     course.setDescription(oldCourse.description);
285     course.setName(oldCourse.name);
286     course.setAuthors(new String[]{oldCourse.author});
287
288     final ArrayList<Lesson> lessons = new ArrayList<Lesson>();
289     for (com.jetbrains.edu.oldCourseFormat.Lesson oldLesson : oldCourse.lessons) {
290       final Lesson lesson = new Lesson();
291       lesson.setName(oldLesson.name);
292       lesson.setIndex(oldLesson.myIndex);
293
294       final ArrayList<Task> tasks = new ArrayList<Task>();
295       for (com.jetbrains.edu.oldCourseFormat.Task oldTask : oldLesson.taskList) {
296         final Task task = new Task();
297         task.setIndex(oldTask.myIndex);
298         task.setName(oldTask.name);
299         task.setLesson(lesson);
300         final HashMap<String, TaskFile> taskFiles = new HashMap<String, TaskFile>();
301         for (Map.Entry<String, com.jetbrains.edu.oldCourseFormat.TaskFile> entry : oldTask.taskFiles.entrySet()) {
302           final TaskFile taskFile = new TaskFile();
303           final com.jetbrains.edu.oldCourseFormat.TaskFile oldTaskFile = entry.getValue();
304           taskFile.setIndex(oldTaskFile.myIndex);
305           taskFile.name = entry.getKey();
306
307           final ArrayList<AnswerPlaceholder> placeholders = new ArrayList<AnswerPlaceholder>();
308           for (TaskWindow window : oldTaskFile.taskWindows) {
309             final AnswerPlaceholder placeholder = new AnswerPlaceholder();
310
311             placeholder.setIndex(window.myIndex);
312             placeholder.setHint(window.hint);
313             placeholder.setLength(window.length);
314             placeholder.setLine(window.line);
315             placeholder.setPossibleAnswer(window.possibleAnswer);
316             placeholder.setStart(window.start);
317
318             placeholders.add(placeholder);
319           }
320
321           taskFile.setAnswerPlaceholders(placeholders);
322           taskFiles.put(entry.getKey(), taskFile);
323         }
324         task.taskFiles = taskFiles;
325         tasks.add(task);
326       }
327
328       lesson.taskList = tasks;
329
330       lessons.add(lesson);
331     }
332     course.setLessons(lessons);
333     course.initCourse(false);
334     return course;
335   }
336
337   public static boolean isImage(String fileName) {
338     final String[] readerFormatNames = ImageIO.getReaderFormatNames();
339     for (@NonNls String format : readerFormatNames) {
340       final String ext = format.toLowerCase();
341       if (fileName.endsWith(ext)) {
342         return true;
343       }
344     }
345     return false;
346   }
347 }