78ad05d7aaa086e42f7d285ce8a0aea706126569
[idea/community.git] / python / educational-core / course-creator / src / com / jetbrains / edu / coursecreator / CCUtils.java
1 package com.jetbrains.edu.coursecreator;
2
3 import com.google.common.base.Predicate;
4 import com.google.common.collect.Collections2;
5 import com.intellij.ide.projectView.actions.MarkRootActionBase;
6 import com.intellij.lang.Language;
7 import com.intellij.openapi.application.ApplicationManager;
8 import com.intellij.openapi.diagnostic.Logger;
9 import com.intellij.openapi.module.Module;
10 import com.intellij.openapi.project.DumbModePermission;
11 import com.intellij.openapi.project.DumbService;
12 import com.intellij.openapi.project.Project;
13 import com.intellij.openapi.roots.ContentEntry;
14 import com.intellij.openapi.roots.ModifiableRootModel;
15 import com.intellij.openapi.roots.ModuleRootManager;
16 import com.intellij.openapi.util.Ref;
17 import com.intellij.openapi.util.io.FileUtil;
18 import com.intellij.openapi.vfs.VirtualFile;
19 import com.intellij.openapi.vfs.VirtualFileEvent;
20 import com.intellij.psi.PsiDirectory;
21 import com.intellij.util.Function;
22 import com.jetbrains.edu.learning.StudyTaskManager;
23 import com.jetbrains.edu.learning.core.EduUtils;
24 import com.jetbrains.edu.learning.courseFormat.Course;
25 import com.jetbrains.edu.learning.courseFormat.StudyItem;
26 import org.jetbrains.annotations.NotNull;
27 import org.jetbrains.annotations.Nullable;
28
29 import java.io.File;
30 import java.io.IOException;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.Collections;
34 import java.util.Comparator;
35
36 public class CCUtils {
37   private static final Logger LOG = Logger.getInstance(CCUtils.class);
38   public static final String GENERATED_FILES_FOLDER = ".coursecreator";
39   public static final String COURSE_MODE = "Course Creator";
40
41   @Nullable
42   public static CCLanguageManager getStudyLanguageManager(@NotNull final Course course) {
43     Language language = Language.findLanguageByID(course.getLanguage());
44     return language == null ? null : CCLanguageManager.INSTANCE.forLanguage(language);
45   }
46
47   /**
48    * This method decreases index and updates directory names of
49    * all tasks/lessons that have higher index than specified object
50    *
51    * @param dirs         directories that are used to get tasks/lessons
52    * @param getStudyItem function that is used to get task/lesson from VirtualFile. This function can return null
53    * @param threshold    index is used as threshold
54    * @param prefix       task or lesson directory name prefix
55    */
56   public static void updateHigherElements(VirtualFile[] dirs,
57                                           @NotNull final Function<VirtualFile, ? extends StudyItem> getStudyItem,
58                                           final int threshold,
59                                           final String prefix,
60                                           final int delta) {
61     ArrayList<VirtualFile> dirsToRename = new ArrayList<VirtualFile>
62       (Collections2.filter(Arrays.asList(dirs), new Predicate<VirtualFile>() {
63         @Override
64         public boolean apply(VirtualFile dir) {
65           final StudyItem item = getStudyItem.fun(dir);
66           if (item == null) {
67             return false;
68           }
69           int index = item.getIndex();
70           return index > threshold;
71         }
72       }));
73     Collections.sort(dirsToRename, new Comparator<VirtualFile>() {
74       @Override
75       public int compare(VirtualFile o1, VirtualFile o2) {
76         StudyItem item1 = getStudyItem.fun(o1);
77         StudyItem item2 = getStudyItem.fun(o2);
78         //if we delete some dir we should start increasing numbers in dir names from the end
79         return (-delta) * EduUtils.INDEX_COMPARATOR.compare(item1, item2);
80       }
81     });
82
83     for (final VirtualFile dir : dirsToRename) {
84       final StudyItem item = getStudyItem.fun(dir);
85       final int newIndex = item.getIndex() + delta;
86       item.setIndex(newIndex);
87       ApplicationManager.getApplication().runWriteAction(new Runnable() {
88         @Override
89         public void run() {
90           try {
91             dir.rename(this, prefix + newIndex);
92           }
93           catch (IOException e) {
94             LOG.error(e);
95           }
96         }
97       });
98     }
99   }
100
101   public static boolean isLessonDir(PsiDirectory sourceDirectory) {
102     if (sourceDirectory == null) {
103       return false;
104     }
105     Project project = sourceDirectory.getProject();
106     Course course = StudyTaskManager.getInstance(project).getCourse();
107     if (course != null && isCourseCreator(project) && course.getLesson(sourceDirectory.getName()) != null) {
108       return true;
109     }
110     return false;
111   }
112
113
114   public static VirtualFile getGeneratedFilesFolder(@NotNull Project project, @NotNull Module module) {
115     VirtualFile baseDir = project.getBaseDir();
116     VirtualFile folder = baseDir.findChild(GENERATED_FILES_FOLDER);
117     if (folder != null) {
118       return folder;
119     }
120     final Ref<VirtualFile> generatedRoot = new Ref<>();
121     DumbService.allowStartingDumbModeInside(DumbModePermission.MAY_START_BACKGROUND, new Runnable() {
122       @Override
123       public void run() {
124         ApplicationManager.getApplication().runWriteAction(new Runnable() {
125           @Override
126           public void run() {
127             try {
128               generatedRoot.set(baseDir.createChildDirectory(this, GENERATED_FILES_FOLDER));
129               final ModifiableRootModel model = ModuleRootManager.getInstance(module).getModifiableModel();
130               ContentEntry entry = MarkRootActionBase.findContentEntry(model, generatedRoot.get());
131               if (entry == null) {
132                 LOG.info("Failed to find contentEntry for archive folder");
133                 return;
134               }
135               entry.addExcludeFolder(generatedRoot.get());
136               model.commit();
137               module.getProject().save();
138             }
139             catch (IOException e) {
140               LOG.info("Failed to create folder for generated files", e);
141             }
142           }
143         });
144       }
145     });
146     return generatedRoot.get();
147   }
148
149   /**
150    * @param requestor {@link VirtualFileEvent#getRequestor}
151    */
152   @Nullable
153   public static VirtualFile generateFolder(@NotNull Project project, @NotNull Module module, @Nullable Object requestor, String name) {
154     VirtualFile generatedRoot = getGeneratedFilesFolder(project, module);
155     if (generatedRoot == null) {
156       return null;
157     }
158
159     final Ref<VirtualFile> folder = new Ref<>(generatedRoot.findChild(name));
160     //need to delete old folder
161     ApplicationManager.getApplication().runWriteAction(() -> {
162       try {
163         if (folder.get() != null) {
164           folder.get().delete(requestor);
165         }
166         folder.set(generatedRoot.createChildDirectory(requestor, name));
167       }
168       catch (IOException e) {
169         LOG.info("Failed to generate folder " + name, e);
170       }
171     });
172     return folder.get();
173   }
174
175   public static boolean isCourseCreator(@NotNull Project project) {
176     Course course = StudyTaskManager.getInstance(project).getCourse();
177     if (course == null) {
178       return false;
179     }
180
181     return COURSE_MODE.equals(course.getCourseMode());
182   }
183
184   public static boolean isTestsFile(@NotNull Project project, @NotNull VirtualFile file) {
185     Course course = StudyTaskManager.getInstance(project).getCourse();
186     if (course == null) {
187       return false;
188     }
189     CCLanguageManager manager = getStudyLanguageManager(course);
190     if (manager == null) {
191       return false;
192     }
193     return manager.isTestFile(file);
194   }
195
196   public static void createResourceFile(VirtualFile createdFile, Course course, VirtualFile taskVF) {
197     VirtualFile lessonVF = taskVF.getParent();
198     if (lessonVF == null) {
199       return;
200     }
201
202     String taskResourcesPath = FileUtil.join(course.getCourseDirectory(), lessonVF.getName(), taskVF.getName());
203     File taskResourceFile = new File(taskResourcesPath);
204     if (!taskResourceFile.exists()) {
205       if (!taskResourceFile.mkdirs()) {
206         LOG.info("Failed to create resources for task " + taskResourcesPath);
207       }
208     }
209     try {
210       File toFile = new File(taskResourceFile, createdFile.getName());
211       FileUtil.copy(new File(createdFile.getPath()), toFile);
212     }
213     catch (IOException e) {
214       LOG.info("Failed to copy created task file to resources " + createdFile.getPath());
215     }
216   }
217 }