diff --git a/resources/META-INF/plugin.xml b/resources/META-INF/plugin.xml
index bbe53ee..85e7999 100644
--- a/resources/META-INF/plugin.xml
+++ b/resources/META-INF/plugin.xml
@@ -48,6 +48,20 @@
description="Configure JHelper"
icon="/name/admitriev/jhelper/icons/settings.png"
/>
+
+
+
+
+
+
pathToFile;
+ final String curDate;
+ int archivedCount = 0;
+
+ public RecursiveArchiver(@NotNull Project project, @NotNull VirtualFile projectBaseDir, @NotNull String archiveDirectoryRelativePath, @NotNull VirtualFile selectedFile) {
+ this.project = project;
+ this.projectBaseDir = projectBaseDir;
+ this.archiveDirectory = archiveDirectoryRelativePath;
+ this.selectedFile = selectedFile;
+ this.pathToFile = new ArrayList<>();
+ this.curDate = DateTimeFormatter.ofPattern("dd_MM_yyyy_HH:mm:ss").format(LocalDateTime.now());
+ }
+
+ @Override
+ public void run() {
+ try {
+ VfsUtil.createDirectoryIfMissing(archiveDirectory);
+ } catch (IOException ex) {
+ throw new NotificationException("Unable to create archive directory", "Root of archive directory does not exist and failed to be created");
+ }
+ VfsUtilCore.visitChildrenRecursively(selectedFile, new VirtualFileVisitor<>() {
+ @Override
+ public boolean visitFile(@NotNull VirtualFile file) {
+ if (file.isDirectory()) {
+ pathToFile.add(file.getName());
+ return true;
+ }
+ handleSourceFile(file);
+ return false;
+ }
+
+ @Override
+ public void afterChildrenVisited(@NotNull VirtualFile file) {
+ pathToFile.remove(pathToFile.size() - 1);
+ }
+ });
+ if (archivedCount > 0) {
+ LocalFileSystem.getInstance().refresh(true); // to notify Vfs about new files created in archive
+ Notificator.showNotification(
+ "All tasks in " + selectedFile.getName() + " were successfully archived",
+ NotificationType.INFORMATION
+ );
+ DeleteTaskAction.selectSomeTaskConfiguration(RunManagerEx.getInstanceEx(project));
+ }
+ }
+
+ private void handleSourceFile(@NotNull VirtualFile file) {
+ RunnerAndConfigurationSettings taskConfigurationAndSettings = findJHelperRCForFile(file, project);
+ if (taskConfigurationAndSettings == null) {
+ return;
+ }
+ TaskConfiguration taskConfiguration = ((TaskConfiguration) taskConfigurationAndSettings.getConfiguration());
+ VirtualFile directory = getFinalArchiveDirectoryOfFile(file);
+ saveCodeToArchive(file, directory, taskConfiguration.getClassName() + "_" + curDate + ".cpp");
+ saveRCToArchive(taskConfiguration, directory, taskConfiguration.getClassName() + "_" + curDate + ".xml");
+ deleteFileWithCode(file);
+ deleteRC(taskConfigurationAndSettings);
+ ++archivedCount;
+ }
+
+ private void deleteRC(RunnerAndConfigurationSettings taskConfigurationAndSettings) {
+ RunManagerEx runManager = RunManagerEx.getInstanceEx(project);
+ runManager.removeConfiguration(taskConfigurationAndSettings);
+ }
+
+ private void deleteFileWithCode(@NotNull VirtualFile file) {
+ try {
+ file.delete(this);
+ } catch (IOException e) {
+ throw new NotificationException("Archiving of the task failed", "Unable to delete sources of the task, caused by " + e.getMessage());
+ }
+ }
+
+ private void saveCodeToArchive(@NotNull VirtualFile fileWithCode, VirtualFile directoryInArchiveForTheTask, String archiveTaskFileName) {
+ try {
+ VfsUtil.copyFile(this, fileWithCode, directoryInArchiveForTheTask, archiveTaskFileName);
+ } catch (IOException e) {
+ throw new NotificationException("Archiving of the task failed", "Archiving of the code of the solution failed, caused by " + e.getMessage());
+ }
+ }
+
+ private void saveRCToArchive(TaskConfiguration taskConfiguration, VirtualFile directoryInArchiveForTheTask, String archiveRCFileName) {
+ try {
+ String rcArchiveFqn = directoryInArchiveForTheTask.getPath() + "/" + archiveRCFileName;
+ Element rc = new Element("RC");
+ Document doc = new Document(rc);
+ taskConfiguration.writeExternal(rc);
+ XMLOutputter xmlOutputter = new XMLOutputter(Format.getPrettyFormat());
+ xmlOutputter.output(doc, new FileOutputStream(rcArchiveFqn));
+ } catch (IOException e) {
+ throw new NotificationException("Archiving of the task failed", "Unable to archive the task, caused by " + e.getMessage());
+ }
+ }
+
+ @NotNull
+ private VirtualFile getFinalArchiveDirectoryOfFile(@NotNull VirtualFile file) {
+ String relativePathToParentInArchive;
+ if (pathToFile.isEmpty()) {
+ relativePathToParentInArchive = archiveDirectory + "/" + file.getParent().getName();
+ } else {
+ relativePathToParentInArchive = archiveDirectory + "/" + String.join("/", pathToFile);
+ }
+ VirtualFile parent;
+ try {
+ parent = VfsUtil.createDirectoryIfMissing(Paths.get(projectBaseDir.getPath(), relativePathToParentInArchive).toString());
+ } catch (IOException e) {
+ throw new NotificationException("Archiving of the task failed", "Unable to create directory in archive for the task, cased by " + e.getMessage());
+ }
+ return parent == null ? Objects.requireNonNull(projectBaseDir.findFileByRelativePath(relativePathToParentInArchive)) : parent;
+ }
+ }
+
+ private static @Nullable RunnerAndConfigurationSettings findJHelperRCForFile(@NotNull VirtualFile file, @NotNull Project project) {
+ RunManagerImpl runManager = RunManagerImpl.getInstanceImpl(project);
+ for (RunnerAndConfigurationSettings configuration : runManager.getAllSettings()) {
+ RunConfiguration rc = configuration.getConfiguration();
+ if (rc instanceof TaskConfiguration) {
+ TaskConfiguration task = (TaskConfiguration) rc;
+ String pathToClassFile = task.getCppPath();
+ VirtualFile expectedFie = FileUtilities.getFile(project, pathToClassFile);
+ if (file.equals(expectedFie)) {
+ return configuration;
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/name/admitriev/jhelper/actions/DeleteTaskAction.java b/src/name/admitriev/jhelper/actions/DeleteTaskAction.java
index c7a096d..ea4d26d 100644
--- a/src/name/admitriev/jhelper/actions/DeleteTaskAction.java
+++ b/src/name/admitriev/jhelper/actions/DeleteTaskAction.java
@@ -66,7 +66,7 @@ public void run() {
);
}
- private static void selectSomeTaskConfiguration(RunManagerEx runManager) {
+ public static void selectSomeTaskConfiguration(RunManagerEx runManager) {
for (RunnerAndConfigurationSettings settings : runManager.getAllSettings()) {
if (settings.getConfiguration() instanceof TaskConfiguration) {
runManager.setSelectedConfiguration(settings);
diff --git a/src/name/admitriev/jhelper/actions/UnarchiveTaskAction.java b/src/name/admitriev/jhelper/actions/UnarchiveTaskAction.java
new file mode 100644
index 0000000..48635ff
--- /dev/null
+++ b/src/name/admitriev/jhelper/actions/UnarchiveTaskAction.java
@@ -0,0 +1,158 @@
+package name.admitriev.jhelper.actions;
+
+import com.intellij.execution.RunManager;
+import com.intellij.execution.RunnerAndConfigurationSettings;
+import com.intellij.execution.configurations.ConfigurationFactory;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectUtil;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileVisitor;
+import com.intellij.psi.PsiManager;
+import com.jetbrains.cidr.lang.psi.OCFile;
+import name.admitriev.jhelper.IDEUtils;
+import name.admitriev.jhelper.configuration.TaskConfiguration;
+import name.admitriev.jhelper.configuration.TaskConfigurationType;
+import name.admitriev.jhelper.exceptions.NotificationException;
+import name.admitriev.jhelper.ui.UIUtils;
+import org.jdom.Document;
+import org.jdom.JDOMException;
+import org.jdom.input.SAXBuilder;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.Objects;
+
+public class UnarchiveTaskAction extends BaseAction {
+ @Override
+ protected void performAction(AnActionEvent e) {
+ Project project = e.getProject();
+ if (project == null) {
+ throw new NotificationException("No project found", "Are you in any project?");
+ }
+ VirtualFile file = e.getDataContext().getData(CommonDataKeys.VIRTUAL_FILE);
+ if (file == null) {
+ throw new NotificationException("No task or directory with tasks selected", "To unarchive a solution or a directory you should select it first");
+ }
+ VirtualFile projectBaseDir = ProjectUtil.guessProjectDir(project);
+ if (projectBaseDir == null) {
+ throw new NotificationException("Unable to find base directory of project", "If you are in default project then switch to the normal one");
+ }
+ ApplicationManager.getApplication().runWriteAction(new RecursiveUnarchiver(project, projectBaseDir, file));
+ }
+
+ private static final class RecursiveUnarchiver implements Runnable {
+ final Project project;
+ final VirtualFile projectBaseDir;
+ final VirtualFile selectedFile;
+ VirtualFile lastGeneratedFile;
+
+ public RecursiveUnarchiver(@NotNull Project project, @NotNull VirtualFile projectBaseDirPath, @NotNull VirtualFile selectedFile) {
+ this.project = project;
+ this.projectBaseDir = projectBaseDirPath;
+ this.selectedFile = selectedFile;
+ }
+
+ @Override
+ public void run() {
+ VfsUtilCore.visitChildrenRecursively(selectedFile, new VirtualFileVisitor<>() {
+ @Override
+ public boolean visitFile(@NotNull VirtualFile file) {
+ if (file.isDirectory()) {
+ return true;
+ }
+ handleSourceFile(file);
+ return false;
+ }
+ });
+ UIUtils.openMethodInEditor(project, (OCFile) Objects.requireNonNull(PsiManager.getInstance(project).findFile(lastGeneratedFile)), "solve");
+ IDEUtils.reloadProject(project);
+ }
+
+ private void handleSourceFile(@NotNull VirtualFile file) {
+ Pair xmlAndCppToUnarchive = getXmlAndCppToUnarchive(file);
+ if (xmlAndCppToUnarchive == null) {
+ return;
+ }
+ VirtualFile xmlRC = xmlAndCppToUnarchive.first;
+ VirtualFile cppFile = xmlAndCppToUnarchive.second;
+ RunnerAndConfigurationSettings configuration = restoreTaskSettings(file, xmlRC);
+ TaskConfiguration taskConfiguration = ((TaskConfiguration) configuration.getConfiguration());
+
+ VirtualFile directory = getUnarchivedDirectoryOfTask(taskConfiguration.getCppPath());
+ try {
+ lastGeneratedFile = VfsUtil.copyFile(this, cppFile, directory, Paths.get(taskConfiguration.getCppPath()).getFileName().toString());
+ } catch (IOException e) {
+ throw new NotificationException("Restoring of task " + file.getNameWithoutExtension() + " failed", "Unable to restore source of the solution, caused by " + e.getMessage());
+ }
+ configuration.storeInDotIdeaFolder();
+ RunManager manager = RunManager.getInstance(project);
+ manager.addConfiguration(configuration);
+
+ try {
+ xmlRC.delete(this);
+ cppFile.delete(this);
+ } catch (IOException e) {
+ throw new NotificationException("Restoring of task " + file.getNameWithoutExtension() + " failed",
+ "Unable to delete archived rc and solution, caused by " + e.getMessage());
+ }
+ }
+
+ private RunnerAndConfigurationSettings restoreTaskSettings(@NotNull VirtualFile file, VirtualFile xmlRC) {
+ TaskConfigurationType taskConfigurationType = new TaskConfigurationType();
+ TaskConfiguration taskConfiguration = ((TaskConfiguration) taskConfigurationType.createTemplateConfiguration(project));
+ SAXBuilder saxBuilder = new SAXBuilder();
+ File inputFile = new File(xmlRC.getPath());
+ Document document;
+ try {
+ document = saxBuilder.build(inputFile);
+ } catch (JDOMException | IOException e) {
+ throw new NotificationException("Restoring of task " + file.getNameWithoutExtension() + " failed", "Unable to restore RC, caused by " + e.getMessage());
+ }
+ RunManager manager = RunManager.getInstance(project);
+ taskConfiguration.readExternal(document.getRootElement());
+ ConfigurationFactory factory = taskConfigurationType.getConfigurationFactories()[0];
+ return manager.createConfiguration(taskConfiguration, factory);
+ }
+
+ private VirtualFile getUnarchivedDirectoryOfTask(String relativeCppPath) {
+ VirtualFile directory;
+ try {
+ directory = VfsUtil.createDirectoryIfMissing(Paths.get(projectBaseDir.getPath(), relativeCppPath).getParent().toString());
+ } catch (IOException e) {
+ throw new NotificationException("Unarchive of the task failed", "Unable to create directory for the task, cased by " + e.getMessage());
+ }
+ return directory == null ? Objects.requireNonNull(projectBaseDir.findFileByRelativePath(Paths.get(relativeCppPath).getParent().toString())) : directory;
+ }
+
+ /* file can be either xml with RC or cpp with solution */
+ private static @Nullable Pair getXmlAndCppToUnarchive(@NotNull VirtualFile file) {
+ String prefix = file.getNameWithoutExtension();
+ VirtualFile xml = null;
+ VirtualFile cpp = null;
+ for (VirtualFile otherFile : file.getParent().getChildren()) {
+ if (otherFile.getNameWithoutExtension().equals(prefix)) {
+ String extension = otherFile.getExtension();
+ if (extension != null && extension.equals("cpp")) {
+ cpp = otherFile;
+ }
+ if (extension != null && extension.equals("xml")) {
+ xml = otherFile;
+ }
+ }
+ }
+ if (xml == null || cpp == null) {
+ return null;
+ }
+ return new Pair<>(xml, cpp);
+ }
+ }
+}
diff --git a/src/name/admitriev/jhelper/components/Configurator.java b/src/name/admitriev/jhelper/components/Configurator.java
index dfeb412..b72a25c 100644
--- a/src/name/admitriev/jhelper/components/Configurator.java
+++ b/src/name/admitriev/jhelper/components/Configurator.java
@@ -32,6 +32,7 @@ public static class State {
private String tasksDirectory;
private String outputFile;
private String runFile;
+ private String archiveDirectory;
private boolean codeEliminationOn;
private boolean codeReformattingOn;
@@ -40,6 +41,7 @@ public State(
String tasksDirectory,
String outputFile,
String runFile,
+ String archiveDirectory,
boolean codeEliminationOn,
boolean codeReformattingOn
) {
@@ -47,12 +49,13 @@ public State(
this.tasksDirectory = tasksDirectory;
this.outputFile = outputFile;
this.runFile = runFile;
+ this.archiveDirectory = archiveDirectory;
this.codeEliminationOn = codeEliminationOn;
this.codeReformattingOn = codeReformattingOn;
}
public State() {
- this("", "tasks", "output/main.cpp", "testrunner/main.cpp", false, false);
+ this("", "tasks", "output/main.cpp", "testrunner/main.cpp", "archive", false, false);
}
public String getAuthor() {
@@ -71,6 +74,10 @@ public String getRunFile() {
return runFile;
}
+ public String getArchiveDirectory() {
+ return archiveDirectory;
+ }
+
public boolean isCodeEliminationOn() {
return codeEliminationOn;
}
diff --git a/src/name/admitriev/jhelper/configuration/TaskConfiguration.java b/src/name/admitriev/jhelper/configuration/TaskConfiguration.java
index 0c6e0a2..28e0e10 100644
--- a/src/name/admitriev/jhelper/configuration/TaskConfiguration.java
+++ b/src/name/admitriev/jhelper/configuration/TaskConfiguration.java
@@ -134,6 +134,7 @@ private static StreamConfiguration readStreamConfiguration(
@Override
public void readExternal(Element element) {
super.readExternal(element);
+ setName(element.getAttributeValue("name",""));
className = element.getAttributeValue("className", "");
cppPath = element.getAttributeValue("cppPath", "");
input = readStreamConfiguration(element, "inputPath", "inputFile");
@@ -166,6 +167,7 @@ private static Test readTest(Element element) {
@Override
public void writeExternal(Element element) {
+ element.setAttribute("name", getName());
element.setAttribute("className", className);
element.setAttribute("cppPath", cppPath);
element.setAttribute("inputType", String.valueOf(input.type.name()));
diff --git a/src/name/admitriev/jhelper/ui/ConfigurationDialog.java b/src/name/admitriev/jhelper/ui/ConfigurationDialog.java
index 1dd9572..5691115 100644
--- a/src/name/admitriev/jhelper/ui/ConfigurationDialog.java
+++ b/src/name/admitriev/jhelper/ui/ConfigurationDialog.java
@@ -16,6 +16,7 @@ public class ConfigurationDialog extends DialogWrapper {
private FileSelector tasksDirectory;
private FileSelector outputFile;
private FileSelector runFile;
+ private FileSelector archiveDirectory;
private JCheckBox codeEliminationOn;
private JCheckBox codeReformattingOn;
@@ -40,6 +41,11 @@ public ConfigurationDialog(@NotNull Project project, Configurator.State configur
configuration.getRunFile(),
RelativeFileChooserDescriptor.fileChooser(project.getBaseDir())
);
+ archiveDirectory = new FileSelector(
+ project,
+ configuration.getArchiveDirectory(),
+ RelativeFileChooserDescriptor.fileChooser(project.getBaseDir())
+ );
codeEliminationOn = new JCheckBox("Eliminate code?", configuration.isCodeEliminationOn());
codeReformattingOn = new JCheckBox("Reformat code?", configuration.isCodeReformattingOn());
@@ -49,6 +55,7 @@ public ConfigurationDialog(@NotNull Project project, Configurator.State configur
panel.add(LabeledComponent.create(tasksDirectory, "Tasks directory"));
panel.add(LabeledComponent.create(outputFile, "Output file"));
panel.add(LabeledComponent.create(runFile, "Run File"));
+ panel.add(LabeledComponent.create(archiveDirectory, "Archive Directory"));
panel.add(codeEliminationOn);
panel.add(codeReformattingOn);
@@ -69,6 +76,7 @@ public Configurator.State getConfiguration() {
tasksDirectory.getText(),
outputFile.getText(),
runFile.getText(),
+ archiveDirectory.getText(),
codeEliminationOn.isSelected(),
codeReformattingOn.isSelected()
);