1 package com.jetbrains.edu.coursecreator;
3 import com.google.common.base.Predicate;
4 import com.google.common.collect.Collections2;
5 import com.intellij.lang.Language;
6 import com.intellij.openapi.actionSystem.AnActionEvent;
7 import com.intellij.openapi.actionSystem.Presentation;
8 import com.intellij.openapi.application.ApplicationManager;
9 import com.intellij.openapi.diagnostic.Logger;
10 import com.intellij.openapi.module.Module;
11 import com.intellij.openapi.project.DumbModePermission;
12 import com.intellij.openapi.project.DumbService;
13 import com.intellij.openapi.project.Project;
14 import com.intellij.openapi.roots.ModuleRootModificationUtil;
15 import com.intellij.openapi.roots.ProjectRootManager;
16 import com.intellij.openapi.util.Ref;
17 import com.intellij.openapi.util.io.FileUtil;
18 import com.intellij.openapi.vfs.LocalFileSystem;
19 import com.intellij.openapi.vfs.VirtualFile;
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 com.jetbrains.edu.learning.courseFormat.Task;
27 import com.jetbrains.edu.learning.courseFormat.TaskFile;
28 import org.jetbrains.annotations.NotNull;
29 import org.jetbrains.annotations.Nullable;
32 import java.io.IOException;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.Collections;
38 public class CCUtils {
39 public static final String ANSWER_EXTENSION_DOTTED = ".answer.";
40 private static final Logger LOG = Logger.getInstance(CCUtils.class);
41 public static final String GENERATED_FILES_FOLDER = ".coursecreator";
42 public static final String COURSE_MODE = "Course Creator";
45 public static CCLanguageManager getStudyLanguageManager(@NotNull final Course course) {
46 Language language = Language.findLanguageByID(course.getLanguageID());
47 return language == null ? null : CCLanguageManager.INSTANCE.forLanguage(language);
51 * This method decreases index and updates directory names of
52 * all tasks/lessons that have higher index than specified object
54 * @param dirs directories that are used to get tasks/lessons
55 * @param getStudyItem function that is used to get task/lesson from VirtualFile. This function can return null
56 * @param threshold index is used as threshold
57 * @param prefix task or lesson directory name prefix
59 public static void updateHigherElements(VirtualFile[] dirs,
60 @NotNull final Function<VirtualFile, ? extends StudyItem> getStudyItem,
64 ArrayList<VirtualFile> dirsToRename = new ArrayList<>
65 (Collections2.filter(Arrays.asList(dirs), new Predicate<VirtualFile>() {
67 public boolean apply(VirtualFile dir) {
68 final StudyItem item = getStudyItem.fun(dir);
72 int index = item.getIndex();
73 return index > threshold;
76 Collections.sort(dirsToRename, (o1, o2) -> {
77 StudyItem item1 = getStudyItem.fun(o1);
78 StudyItem item2 = getStudyItem.fun(o2);
79 //if we delete some dir we should start increasing numbers in dir names from the end
80 return (-delta) * EduUtils.INDEX_COMPARATOR.compare(item1, item2);
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() {
91 dir.rename(this, prefix + newIndex);
93 catch (IOException e) {
101 public static boolean isLessonDir(PsiDirectory sourceDirectory) {
102 if (sourceDirectory == null) {
105 Project project = sourceDirectory.getProject();
106 Course course = StudyTaskManager.getInstance(project).getCourse();
107 if (course != null && isCourseCreator(project) && course.getLesson(sourceDirectory.getName()) != null) {
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) {
120 final Ref<VirtualFile> generatedRoot = new Ref<>();
121 DumbService.allowStartingDumbModeInside(DumbModePermission.MAY_START_BACKGROUND, new Runnable() {
124 ApplicationManager.getApplication().runWriteAction(new Runnable() {
128 generatedRoot.set(baseDir.createChildDirectory(this, GENERATED_FILES_FOLDER));
129 VirtualFile contentRootForFile =
130 ProjectRootManager.getInstance(module.getProject()).getFileIndex().getContentRootForFile(generatedRoot.get());
131 if (contentRootForFile == null) {
134 ModuleRootModificationUtil.updateExcludedFolders(module, contentRootForFile, Collections.emptyList(), Collections.singletonList(generatedRoot.get().getUrl()));
136 catch (IOException e) {
137 LOG.info("Failed to create folder for generated files", e);
143 return generatedRoot.get();
147 public static VirtualFile generateFolder(@NotNull Project project, @NotNull Module module, String name) {
148 VirtualFile generatedRoot = getGeneratedFilesFolder(project, module);
149 if (generatedRoot == null) {
153 final Ref<VirtualFile> folder = new Ref<>(generatedRoot.findChild(name));
154 //need to delete old folder
155 ApplicationManager.getApplication().runWriteAction(() -> {
157 if (folder.get() != null) {
158 folder.get().delete(null);
160 folder.set(generatedRoot.createChildDirectory(null, name));
162 catch (IOException e) {
163 LOG.info("Failed to generate folder " + name, e);
169 public static boolean isCourseCreator(@NotNull Project project) {
170 Course course = StudyTaskManager.getInstance(project).getCourse();
171 if (course == null) {
175 return COURSE_MODE.equals(course.getCourseMode());
178 public static boolean isTestsFile(@NotNull Project project, @NotNull VirtualFile file) {
179 Course course = StudyTaskManager.getInstance(project).getCourse();
180 if (course == null) {
183 CCLanguageManager manager = getStudyLanguageManager(course);
184 if (manager == null) {
187 return manager.isTestFile(file);
190 public static void createResourceFile(VirtualFile createdFile, Course course, VirtualFile taskVF) {
191 VirtualFile lessonVF = taskVF.getParent();
192 if (lessonVF == null) {
196 String taskResourcesPath = FileUtil.join(course.getCourseDirectory(), lessonVF.getName(), taskVF.getName());
197 File taskResourceFile = new File(taskResourcesPath);
198 if (!taskResourceFile.exists()) {
199 if (!taskResourceFile.mkdirs()) {
200 LOG.info("Failed to create resources for task " + taskResourcesPath);
204 File toFile = new File(taskResourceFile, createdFile.getName());
205 FileUtil.copy(new File(createdFile.getPath()), toFile);
207 catch (IOException e) {
208 LOG.info("Failed to copy created task file to resources " + createdFile.getPath());
213 public static void updateResources(Project project, Task task, VirtualFile taskDir) {
214 Course course = StudyTaskManager.getInstance(project).getCourse();
215 if (course == null) {
218 VirtualFile lessonVF = taskDir.getParent();
219 if (lessonVF == null) {
223 String taskResourcesPath = FileUtil.join(course.getCourseDirectory(), lessonVF.getName(), taskDir.getName());
224 File taskResourceFile = new File(taskResourcesPath);
225 if (!taskResourceFile.exists()) {
226 if (!taskResourceFile.mkdirs()) {
227 LOG.info("Failed to create resources for task " + taskResourcesPath);
230 VirtualFile studentDir = LocalFileSystem.getInstance().findFileByIoFile(taskResourceFile);
231 if (studentDir == null) {
234 for (Map.Entry<String, TaskFile> entry : task.getTaskFiles().entrySet()) {
235 String name = entry.getKey();
236 VirtualFile answerFile = taskDir.findChild(name);
237 if (answerFile == null) {
240 ApplicationManager.getApplication().runWriteAction(() -> {
241 EduUtils.createStudentFile(CCUtils.class, project, answerFile, studentDir, null);
246 public static void updateActionGroup(AnActionEvent e) {
247 Presentation presentation = e.getPresentation();
248 Project project = e.getProject();
249 presentation.setEnabledAndVisible(project != null && isCourseCreator(project));