From 09d7fde99b1c282952a8a0f526ddda6b9a08e04b Mon Sep 17 00:00:00 2001 From: Kirill Antonov Date: Mon, 6 Sep 2021 23:04:05 +0300 Subject: [PATCH 1/5] add name of the task to external element --- src/name/admitriev/jhelper/configuration/TaskConfiguration.java | 2 ++ 1 file changed, 2 insertions(+) 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())); From 64ddd87b6dffef2f6b49432f40c96cf5b3e5a8f9 Mon Sep 17 00:00:00 2001 From: Kirill Antonov Date: Mon, 6 Sep 2021 23:04:38 +0300 Subject: [PATCH 2/5] refactoring - change select configuration method to public --- src/name/admitriev/jhelper/actions/DeleteTaskAction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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); From 5b1e7356ef6714dfba92f9ca27fefb3c8767b518 Mon Sep 17 00:00:00 2001 From: Kirill Antonov Date: Tue, 7 Sep 2021 19:59:20 +0300 Subject: [PATCH 3/5] add archive directory setting to configs --- src/name/admitriev/jhelper/components/Configurator.java | 9 ++++++++- src/name/admitriev/jhelper/ui/ConfigurationDialog.java | 8 ++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) 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/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() ); From fa957436b65bfd0904e6c6b903b880ea91aafdfa Mon Sep 17 00:00:00 2001 From: Kirill Antonov Date: Tue, 7 Sep 2021 20:00:09 +0300 Subject: [PATCH 4/5] add icons for archiving --- .../name/admitriev/jhelper/icons/archive.png | Bin 0 -> 646 bytes .../name/admitriev/jhelper/icons/unarchive.png | Bin 0 -> 650 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 resources/name/admitriev/jhelper/icons/archive.png create mode 100644 resources/name/admitriev/jhelper/icons/unarchive.png diff --git a/resources/name/admitriev/jhelper/icons/archive.png b/resources/name/admitriev/jhelper/icons/archive.png new file mode 100644 index 0000000000000000000000000000000000000000..6a12420a54eb0705aceb727ba47330a1ab911175 GIT binary patch literal 646 zcmV;10(t$3P))W|Q+1w3vWzdhhXlAHMJL6zaue=nh7_qpj8JrczI; z)dc{4zs6`RLCLQ0W5;YHYYb@fcQBTm#IOn&d&|IfU7gwp&?JI{PNWF>;xvh1LkGw9 zns_z51dJcl=X=gzmJ58@_;ytGb?Nf*#6J#nUBCYz`Di?eYJ6c~;r=lPx~_K#Ay)f)M<~e>*FIk5dtsSVt#JU>3tJ4d46!(wK~w~< z-@K$)-0hy8o?bg}Fx$}{LkK~8pp9qeUeMLt!%B9ImFybn<{qA&eNH?SMbmtAc6MP| z)+~Uc>v~uSVfOcqAcWxe#vf9tI9bc!()$aEdTR&qGRyD9}54RTLCO zsa~ZvB&Ec4U0mrR-CBNFyO^fAquI6{K0P{yq9`bE2&wzGz^1*4Eo{<(H1iMV!&)wv z1K{nu52!*#sA?6a)L=*6Wq2sf-1=RJ!rb6J48uS(3OaXb$+fdE?4|t&r(^QcWb-wv93~Gh;fA6LuU2$8io1C8fmk gJUq{9fC6CWFUS4*oKn&t!2kdN07*qoM6N<$f*4&i3;+NC literal 0 HcmV?d00001 diff --git a/resources/name/admitriev/jhelper/icons/unarchive.png b/resources/name/admitriev/jhelper/icons/unarchive.png new file mode 100644 index 0000000000000000000000000000000000000000..9dbd211fa43ff82ee4a29e725101cbdc95624c0a GIT binary patch literal 650 zcmV;50(Jd~P)cMfG!W`R z(C$IfI>^b=oaW6Be~k0I@7>p8oWVhh4qP|~&i8YEUBO>0{c6-E@EwF)MK#<{-u_ZJ zE>9dQUK+D8`;L^P0E}S;85>L9aE?HZ0MhSo+L*wRN~X>Xbpw$3x`7GJ3ep-&UVl>u zk#*wbaT}u@Al2HHAlB4?)nL)ul>kU#w3A*Qw-0Nqy7GbVAS!L=(4*auz~0^-JLO&0cD@sukX$ZT0W`;(n7DWAzeZRXdRcJ{Xsr=5Gc)-h2<#vT k2!f!tC}T|R3jeUeACVs#^9PuA*Z=?k07*qoM6N<$f-R*k-2eap literal 0 HcmV?d00001 From 146f24300cb7bbea6b9d37a3e2fb5fb0fe423ce3 Mon Sep 17 00:00:00 2001 From: Kirill Antonov Date: Tue, 7 Sep 2021 20:14:49 +0300 Subject: [PATCH 5/5] add actions for archiving --- resources/META-INF/plugin.xml | 14 ++ .../jhelper/actions/ArchiveTaskAction.java | 186 ++++++++++++++++++ .../jhelper/actions/UnarchiveTaskAction.java | 158 +++++++++++++++ 3 files changed, 358 insertions(+) create mode 100644 src/name/admitriev/jhelper/actions/ArchiveTaskAction.java create mode 100644 src/name/admitriev/jhelper/actions/UnarchiveTaskAction.java 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/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); + } + } +}