1 package com.jetbrains.edu.coursecreator;
3 import com.google.common.base.Predicate;
4 import com.google.common.collect.Collections2;
5 import com.intellij.ide.IdeView;
6 import com.intellij.ide.fileTemplates.FileTemplate;
7 import com.intellij.ide.fileTemplates.FileTemplateManager;
8 import com.intellij.ide.fileTemplates.FileTemplateUtil;
9 import com.intellij.ide.util.EditorHelper;
10 import com.intellij.lang.Language;
11 import com.intellij.openapi.actionSystem.AnActionEvent;
12 import com.intellij.openapi.actionSystem.Presentation;
13 import com.intellij.openapi.application.ApplicationManager;
14 import com.intellij.openapi.diagnostic.Logger;
15 import com.intellij.openapi.module.Module;
16 import com.intellij.openapi.project.Project;
17 import com.intellij.openapi.roots.ModuleRootModificationUtil;
18 import com.intellij.openapi.roots.ProjectRootManager;
19 import com.intellij.openapi.util.Ref;
20 import com.intellij.openapi.util.io.FileUtil;
21 import com.intellij.openapi.vfs.LocalFileSystem;
22 import com.intellij.openapi.vfs.VirtualFile;
23 import com.intellij.psi.PsiDirectory;
24 import com.intellij.psi.PsiElement;
25 import com.intellij.util.Function;
26 import com.jetbrains.edu.coursecreator.settings.CCSettings;
27 import com.jetbrains.edu.learning.StudyTaskManager;
28 import com.jetbrains.edu.learning.StudyUtils;
29 import com.jetbrains.edu.learning.core.EduUtils;
30 import com.jetbrains.edu.learning.courseFormat.Course;
31 import com.jetbrains.edu.learning.courseFormat.StudyItem;
32 import com.jetbrains.edu.learning.courseFormat.Task;
33 import com.jetbrains.edu.learning.courseFormat.TaskFile;
34 import org.jetbrains.annotations.NotNull;
35 import org.jetbrains.annotations.Nullable;
38 import java.io.IOException;
39 import java.util.ArrayList;
40 import java.util.Arrays;
41 import java.util.Collections;
44 public class CCUtils {
45 public static final String ANSWER_EXTENSION_DOTTED = ".answer.";
46 private static final Logger LOG = Logger.getInstance(CCUtils.class);
47 public static final String GENERATED_FILES_FOLDER = ".coursecreator";
48 public static final String COURSE_MODE = "Course Creator";
51 public static CCLanguageManager getStudyLanguageManager(@NotNull final Course course) {
52 Language language = Language.findLanguageByID(course.getLanguageID());
53 return language == null ? null : CCLanguageManager.INSTANCE.forLanguage(language);
57 * This method decreases index and updates directory names of
58 * all tasks/lessons that have higher index than specified object
60 * @param dirs directories that are used to get tasks/lessons
61 * @param getStudyItem function that is used to get task/lesson from VirtualFile. This function can return null
62 * @param threshold index is used as threshold
63 * @param prefix task or lesson directory name prefix
65 public static void updateHigherElements(VirtualFile[] dirs,
66 @NotNull final Function<VirtualFile, ? extends StudyItem> getStudyItem,
70 ArrayList<VirtualFile> dirsToRename = new ArrayList<>
71 (Collections2.filter(Arrays.asList(dirs), new Predicate<VirtualFile>() {
73 public boolean apply(VirtualFile dir) {
74 final StudyItem item = getStudyItem.fun(dir);
78 int index = item.getIndex();
79 return index > threshold;
82 Collections.sort(dirsToRename, (o1, o2) -> {
83 StudyItem item1 = getStudyItem.fun(o1);
84 StudyItem item2 = getStudyItem.fun(o2);
85 //if we delete some dir we should start increasing numbers in dir names from the end
86 return (-delta) * EduUtils.INDEX_COMPARATOR.compare(item1, item2);
89 for (final VirtualFile dir : dirsToRename) {
90 final StudyItem item = getStudyItem.fun(dir);
91 final int newIndex = item.getIndex() + delta;
92 item.setIndex(newIndex);
93 ApplicationManager.getApplication().runWriteAction(new Runnable() {
97 dir.rename(this, prefix + newIndex);
99 catch (IOException e) {
107 public static boolean isLessonDir(PsiDirectory sourceDirectory) {
108 if (sourceDirectory == null) {
111 Project project = sourceDirectory.getProject();
112 Course course = StudyTaskManager.getInstance(project).getCourse();
113 if (course != null && isCourseCreator(project) && course.getLesson(sourceDirectory.getName()) != null) {
120 public static VirtualFile getGeneratedFilesFolder(@NotNull Project project, @NotNull Module module) {
121 VirtualFile baseDir = project.getBaseDir();
122 VirtualFile folder = baseDir.findChild(GENERATED_FILES_FOLDER);
123 if (folder != null) {
126 final Ref<VirtualFile> generatedRoot = new Ref<>();
127 ApplicationManager.getApplication().runWriteAction(new Runnable() {
131 generatedRoot.set(baseDir.createChildDirectory(this, GENERATED_FILES_FOLDER));
132 VirtualFile contentRootForFile =
133 ProjectRootManager.getInstance(module.getProject()).getFileIndex().getContentRootForFile(generatedRoot.get());
134 if (contentRootForFile == null) {
137 ModuleRootModificationUtil.updateExcludedFolders(module, contentRootForFile, Collections.emptyList(), Collections.singletonList(generatedRoot.get().getUrl()));
139 catch (IOException e) {
140 LOG.info("Failed to create folder for generated files", e);
144 return generatedRoot.get();
148 public static VirtualFile generateFolder(@NotNull Project project, @NotNull Module module, String name) {
149 VirtualFile generatedRoot = getGeneratedFilesFolder(project, module);
150 if (generatedRoot == null) {
154 final Ref<VirtualFile> folder = new Ref<>(generatedRoot.findChild(name));
155 //need to delete old folder
156 ApplicationManager.getApplication().runWriteAction(() -> {
158 if (folder.get() != null) {
159 folder.get().delete(null);
161 folder.set(generatedRoot.createChildDirectory(null, name));
163 catch (IOException e) {
164 LOG.info("Failed to generate folder " + name, e);
170 public static boolean isCourseCreator(@NotNull Project project) {
171 Course course = StudyTaskManager.getInstance(project).getCourse();
172 if (course == null) {
176 return COURSE_MODE.equals(course.getCourseMode());
179 public static boolean isTestsFile(@NotNull Project project, @NotNull VirtualFile file) {
180 Course course = StudyTaskManager.getInstance(project).getCourse();
181 if (course == null) {
184 CCLanguageManager manager = getStudyLanguageManager(course);
185 if (manager == null) {
188 return manager.isTestFile(file);
191 public static void createResourceFile(VirtualFile createdFile, Course course, VirtualFile taskVF) {
192 VirtualFile lessonVF = taskVF.getParent();
193 if (lessonVF == null) {
197 String taskResourcesPath = FileUtil.join(course.getCourseDirectory(), lessonVF.getName(), taskVF.getName());
198 File taskResourceFile = new File(taskResourcesPath);
199 if (!taskResourceFile.exists()) {
200 if (!taskResourceFile.mkdirs()) {
201 LOG.info("Failed to create resources for task " + taskResourcesPath);
205 File toFile = new File(taskResourceFile, createdFile.getName());
206 FileUtil.copy(new File(createdFile.getPath()), toFile);
208 catch (IOException e) {
209 LOG.info("Failed to copy created task file to resources " + createdFile.getPath());
214 public static void updateResources(Project project, Task task, VirtualFile taskDir) {
215 Course course = StudyTaskManager.getInstance(project).getCourse();
216 if (course == null) {
219 VirtualFile lessonVF = taskDir.getParent();
220 if (lessonVF == null) {
224 String taskResourcesPath = FileUtil.join(course.getCourseDirectory(), lessonVF.getName(), taskDir.getName());
225 File taskResourceFile = new File(taskResourcesPath);
226 if (!taskResourceFile.exists()) {
227 if (!taskResourceFile.mkdirs()) {
228 LOG.info("Failed to create resources for task " + taskResourcesPath);
231 VirtualFile studentDir = LocalFileSystem.getInstance().findFileByIoFile(taskResourceFile);
232 if (studentDir == null) {
235 for (Map.Entry<String, TaskFile> entry : task.getTaskFiles().entrySet()) {
236 String name = entry.getKey();
237 VirtualFile answerFile = taskDir.findChild(name);
238 if (answerFile == null) {
241 ApplicationManager.getApplication().runWriteAction(() -> {
242 EduUtils.createStudentFile(CCUtils.class, project, answerFile, studentDir, null, task.getActiveSubtaskIndex());
247 public static void updateActionGroup(AnActionEvent e) {
248 Presentation presentation = e.getPresentation();
249 Project project = e.getProject();
250 presentation.setEnabledAndVisible(project != null && isCourseCreator(project));
253 private static void createFromTemplate(@NotNull final PsiDirectory taskDirectory,
254 @Nullable final FileTemplate template,
255 @Nullable IdeView view, boolean open) {
256 if (template == null) {
260 final PsiElement file = FileTemplateUtil.createFromTemplate(template, template.getName(), null, taskDirectory);
261 if (view != null && open) {
262 EditorHelper.openInEditor(file, false);
263 view.selectElement(file);
266 catch (Exception e) {
271 public static void createTaskContent(@NotNull Project project,
272 @Nullable IdeView view,
273 @NotNull Course course,
274 PsiDirectory taskDirectory) {
275 CCLanguageManager manager = getStudyLanguageManager(course);
276 if (manager == null) {
279 createFromTemplate(taskDirectory, manager.getTestsTemplate(project), view, false);
280 createFromTemplate(taskDirectory, FileTemplateManager.getInstance(project)
281 .getInternalTemplate(StudyUtils.getTaskDescriptionFileName(CCSettings.getInstance().useHtmlAsDefaultTaskFormat())), view, false);
282 String defaultExtension = manager.getDefaultTaskFileExtension();
283 if (defaultExtension != null) {
284 FileTemplate taskFileTemplate = manager.getTaskFileTemplateForExtension(project, defaultExtension);
285 createFromTemplate(taskDirectory, taskFileTemplate, view, true);