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