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.Project;
12 import com.intellij.openapi.roots.ModuleRootModificationUtil;
13 import com.intellij.openapi.roots.ProjectRootManager;
14 import com.intellij.openapi.util.Ref;
15 import com.intellij.openapi.util.io.FileUtil;
16 import com.intellij.openapi.vfs.LocalFileSystem;
17 import com.intellij.openapi.vfs.VirtualFile;
18 import com.intellij.psi.PsiDirectory;
19 import com.intellij.util.Function;
20 import com.jetbrains.edu.learning.StudyTaskManager;
21 import com.jetbrains.edu.learning.core.EduUtils;
22 import com.jetbrains.edu.learning.courseFormat.Course;
23 import com.jetbrains.edu.learning.courseFormat.StudyItem;
24 import com.jetbrains.edu.learning.courseFormat.Task;
25 import com.jetbrains.edu.learning.courseFormat.TaskFile;
26 import org.jetbrains.annotations.NotNull;
27 import org.jetbrains.annotations.Nullable;
30 import java.io.IOException;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.Collections;
36 public class CCUtils {
37 public static final String ANSWER_EXTENSION_DOTTED = ".answer.";
38 private static final Logger LOG = Logger.getInstance(CCUtils.class);
39 public static final String GENERATED_FILES_FOLDER = ".coursecreator";
40 public static final String COURSE_MODE = "Course Creator";
43 public static CCLanguageManager getStudyLanguageManager(@NotNull final Course course) {
44 Language language = Language.findLanguageByID(course.getLanguageID());
45 return language == null ? null : CCLanguageManager.INSTANCE.forLanguage(language);
49 * This method decreases index and updates directory names of
50 * all tasks/lessons that have higher index than specified object
52 * @param dirs directories that are used to get tasks/lessons
53 * @param getStudyItem function that is used to get task/lesson from VirtualFile. This function can return null
54 * @param threshold index is used as threshold
55 * @param prefix task or lesson directory name prefix
57 public static void updateHigherElements(VirtualFile[] dirs,
58 @NotNull final Function<VirtualFile, ? extends StudyItem> getStudyItem,
62 ArrayList<VirtualFile> dirsToRename = new ArrayList<>
63 (Collections2.filter(Arrays.asList(dirs), new Predicate<VirtualFile>() {
65 public boolean apply(VirtualFile dir) {
66 final StudyItem item = getStudyItem.fun(dir);
70 int index = item.getIndex();
71 return index > threshold;
74 Collections.sort(dirsToRename, (o1, o2) -> {
75 StudyItem item1 = getStudyItem.fun(o1);
76 StudyItem item2 = getStudyItem.fun(o2);
77 //if we delete some dir we should start increasing numbers in dir names from the end
78 return (-delta) * EduUtils.INDEX_COMPARATOR.compare(item1, item2);
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() {
89 dir.rename(this, prefix + newIndex);
91 catch (IOException e) {
99 public static boolean isLessonDir(PsiDirectory sourceDirectory) {
100 if (sourceDirectory == null) {
103 Project project = sourceDirectory.getProject();
104 Course course = StudyTaskManager.getInstance(project).getCourse();
105 if (course != null && isCourseCreator(project) && course.getLesson(sourceDirectory.getName()) != null) {
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) {
118 final Ref<VirtualFile> generatedRoot = new Ref<>();
119 ApplicationManager.getApplication().runWriteAction(new Runnable() {
123 generatedRoot.set(baseDir.createChildDirectory(this, GENERATED_FILES_FOLDER));
124 VirtualFile contentRootForFile =
125 ProjectRootManager.getInstance(module.getProject()).getFileIndex().getContentRootForFile(generatedRoot.get());
126 if (contentRootForFile == null) {
129 ModuleRootModificationUtil.updateExcludedFolders(module, contentRootForFile, Collections.emptyList(), Collections.singletonList(generatedRoot.get().getUrl()));
131 catch (IOException e) {
132 LOG.info("Failed to create folder for generated files", e);
136 return generatedRoot.get();
140 public static VirtualFile generateFolder(@NotNull Project project, @NotNull Module module, String name) {
141 VirtualFile generatedRoot = getGeneratedFilesFolder(project, module);
142 if (generatedRoot == null) {
146 final Ref<VirtualFile> folder = new Ref<>(generatedRoot.findChild(name));
147 //need to delete old folder
148 ApplicationManager.getApplication().runWriteAction(() -> {
150 if (folder.get() != null) {
151 folder.get().delete(null);
153 folder.set(generatedRoot.createChildDirectory(null, name));
155 catch (IOException e) {
156 LOG.info("Failed to generate folder " + name, e);
162 public static boolean isCourseCreator(@NotNull Project project) {
163 Course course = StudyTaskManager.getInstance(project).getCourse();
164 if (course == null) {
168 return COURSE_MODE.equals(course.getCourseMode());
171 public static boolean isTestsFile(@NotNull Project project, @NotNull VirtualFile file) {
172 Course course = StudyTaskManager.getInstance(project).getCourse();
173 if (course == null) {
176 CCLanguageManager manager = getStudyLanguageManager(course);
177 if (manager == null) {
180 return manager.isTestFile(file);
183 public static void createResourceFile(VirtualFile createdFile, Course course, VirtualFile taskVF) {
184 VirtualFile lessonVF = taskVF.getParent();
185 if (lessonVF == null) {
189 String taskResourcesPath = FileUtil.join(course.getCourseDirectory(), lessonVF.getName(), taskVF.getName());
190 File taskResourceFile = new File(taskResourcesPath);
191 if (!taskResourceFile.exists()) {
192 if (!taskResourceFile.mkdirs()) {
193 LOG.info("Failed to create resources for task " + taskResourcesPath);
197 File toFile = new File(taskResourceFile, createdFile.getName());
198 FileUtil.copy(new File(createdFile.getPath()), toFile);
200 catch (IOException e) {
201 LOG.info("Failed to copy created task file to resources " + createdFile.getPath());
206 public static void updateResources(Project project, Task task, VirtualFile taskDir) {
207 Course course = StudyTaskManager.getInstance(project).getCourse();
208 if (course == null) {
211 VirtualFile lessonVF = taskDir.getParent();
212 if (lessonVF == null) {
216 String taskResourcesPath = FileUtil.join(course.getCourseDirectory(), lessonVF.getName(), taskDir.getName());
217 File taskResourceFile = new File(taskResourcesPath);
218 if (!taskResourceFile.exists()) {
219 if (!taskResourceFile.mkdirs()) {
220 LOG.info("Failed to create resources for task " + taskResourcesPath);
223 VirtualFile studentDir = LocalFileSystem.getInstance().findFileByIoFile(taskResourceFile);
224 if (studentDir == null) {
227 for (Map.Entry<String, TaskFile> entry : task.getTaskFiles().entrySet()) {
228 String name = entry.getKey();
229 VirtualFile answerFile = taskDir.findChild(name);
230 if (answerFile == null) {
233 ApplicationManager.getApplication().runWriteAction(() -> {
234 EduUtils.createStudentFile(CCUtils.class, project, answerFile, studentDir, null, task.getActiveSubtaskIndex());
239 public static void updateActionGroup(AnActionEvent e) {
240 Presentation presentation = e.getPresentation();
241 Project project = e.getProject();
242 presentation.setEnabledAndVisible(project != null && isCourseCreator(project));