Skip to content

Commit

Permalink
Add package/directory export context actions
Browse files Browse the repository at this point in the history
  • Loading branch information
Col-E committed Nov 11, 2024
1 parent 702674f commit 8199604
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ public ContextMenuProvider getDirectoryContextMenuProvider(@Nonnull ContextSourc
refactor.directoryItem("menu.refactor.move", STACKED_MOVE, actions::moveDirectory);
refactor.directoryItem("menu.refactor.rename", TAG_EDIT, actions::renameDirectory);

builder.directoryItem("menu.export.directory", EXPORT, actions::exportDirectory);

// TODO: implement operations
// - Search references
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ public ContextMenuProvider getPackageContextMenuProvider(@Nonnull ContextSource
var refactor = jvmBuilder.submenu("menu.refactor", PAINT_BRUSH);
refactor.directoryItem("menu.refactor.move", STACKED_MOVE, actions::movePackage);
refactor.directoryItem("menu.refactor.rename", TAG_EDIT, actions::renamePackage);

jvmBuilder.directoryItem("menu.export.package", EXPORT, actions::exportPackage);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package software.coley.recaf.services.navigation;

import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Instance;
import jakarta.inject.Inject;
Expand All @@ -25,7 +26,14 @@
import software.coley.recaf.info.member.ClassMember;
import software.coley.recaf.info.member.FieldMember;
import software.coley.recaf.info.member.MethodMember;
import software.coley.recaf.path.*;
import software.coley.recaf.path.ClassMemberPathNode;
import software.coley.recaf.path.ClassPathNode;
import software.coley.recaf.path.DirectoryPathNode;
import software.coley.recaf.path.FilePathNode;
import software.coley.recaf.path.IncompletePathException;
import software.coley.recaf.path.LineNumberPathNode;
import software.coley.recaf.path.PathNode;
import software.coley.recaf.path.PathNodes;
import software.coley.recaf.services.Service;
import software.coley.recaf.services.cell.CellConfigurationService;
import software.coley.recaf.services.cell.icon.IconProviderService;
Expand Down Expand Up @@ -58,16 +66,45 @@
import software.coley.recaf.ui.pane.editing.media.ImageFilePane;
import software.coley.recaf.ui.pane.editing.media.VideoFilePane;
import software.coley.recaf.ui.pane.editing.text.TextFilePane;
import software.coley.recaf.ui.pane.search.*;
import software.coley.recaf.ui.pane.search.AbstractSearchPane;
import software.coley.recaf.ui.pane.search.ClassReferenceSearchPane;
import software.coley.recaf.ui.pane.search.MemberReferenceSearchPane;
import software.coley.recaf.ui.pane.search.NumberSearchPane;
import software.coley.recaf.ui.pane.search.StringSearchPane;
import software.coley.recaf.ui.window.RecafScene;
import software.coley.recaf.util.*;
import software.coley.recaf.util.visitors.*;
import software.coley.recaf.util.ClipboardUtil;
import software.coley.recaf.util.EscapeUtil;
import software.coley.recaf.util.FxThreadUtil;
import software.coley.recaf.util.Lang;
import software.coley.recaf.util.SceneUtils;
import software.coley.recaf.util.StringUtil;
import software.coley.recaf.util.visitors.ClassAnnotationRemovingVisitor;
import software.coley.recaf.util.visitors.FieldAnnotationRemovingVisitor;
import software.coley.recaf.util.visitors.FieldPredicate;
import software.coley.recaf.util.visitors.MemberCopyingVisitor;
import software.coley.recaf.util.visitors.MemberRemovingVisitor;
import software.coley.recaf.util.visitors.MemberStubAddingVisitor;
import software.coley.recaf.util.visitors.MethodAnnotationRemovingVisitor;
import software.coley.recaf.util.visitors.MethodNoopingVisitor;
import software.coley.recaf.util.visitors.MethodPredicate;
import software.coley.recaf.workspace.PathExportingManager;
import software.coley.recaf.workspace.model.BasicWorkspace;
import software.coley.recaf.workspace.model.Workspace;
import software.coley.recaf.workspace.model.bundle.*;
import software.coley.recaf.workspace.model.bundle.AndroidClassBundle;
import software.coley.recaf.workspace.model.bundle.BasicFileBundle;
import software.coley.recaf.workspace.model.bundle.BasicJvmClassBundle;
import software.coley.recaf.workspace.model.bundle.Bundle;
import software.coley.recaf.workspace.model.bundle.ClassBundle;
import software.coley.recaf.workspace.model.bundle.FileBundle;
import software.coley.recaf.workspace.model.bundle.JvmClassBundle;
import software.coley.recaf.workspace.model.resource.WorkspaceResource;
import software.coley.recaf.workspace.model.resource.WorkspaceResourceBuilder;

import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -119,9 +156,9 @@ public Actions(@Nonnull ActionsConfig config,
@Nonnull WindowFactory windowFactory,
@Nonnull TextProviderService textService,
@Nonnull IconProviderService iconService,
@Nonnull CellConfigurationService cellConfigurationService,
@Nonnull CellConfigurationService cellConfigurationService,
@Nonnull PathExportingManager pathExportingManager,
@Nonnull Instance<InheritanceGraph> graphProvider,
@Nonnull Instance<InheritanceGraph> graphProvider,
@Nonnull Instance<MappingApplier> applierProvider,
@Nonnull Instance<JvmClassPane> jvmPaneProvider,
@Nonnull Instance<AndroidClassPane> androidPaneProvider,
Expand All @@ -146,7 +183,7 @@ public Actions(@Nonnull ActionsConfig config,
this.iconService = iconService;
this.cellConfigurationService = cellConfigurationService;
this.pathExportingManager = pathExportingManager;
this.graphProvider= graphProvider;
this.graphProvider = graphProvider;
this.applierProvider = applierProvider;
this.jvmPaneProvider = jvmPaneProvider;
this.androidPaneProvider = androidPaneProvider;
Expand Down Expand Up @@ -1656,6 +1693,62 @@ public void exportClass(@Nonnull Workspace workspace,
pathExportingManager.export(info);
}

/**
* Exports a package, prompting the user to select a location to save the file to.
*
* @param workspace
* Containing workspace.
* @param resource
* Containing resource.
* @param bundle
* Containing bundle.
* @param packageName
* Name of package to export.
*/
public void exportPackage(@Nonnull Workspace workspace,
@Nonnull WorkspaceResource resource,
@Nonnull JvmClassBundle bundle,
@Nullable String packageName) {
JvmClassBundle bundleCopy = new BasicJvmClassBundle();
bundle.valuesAsCopy().forEach(cls -> {
if (packageName == null && cls.getPackageName() == null)
bundleCopy.put(cls);
else if (cls.getName().startsWith(packageName + "/"))
bundleCopy.put(cls);
});
WorkspaceResource resourceCopy = new WorkspaceResourceBuilder().withJvmClassBundle(bundleCopy).build();
Workspace workspaceCopy = new BasicWorkspace(resourceCopy);
pathExportingManager.export(workspaceCopy, "package", false);
}

/**
* Exports a directory, prompting the user to select a location to save the file to.
*
* @param workspace
* Containing workspace.
* @param resource
* Containing resource.
* @param bundle
* Containing bundle.
* @param directoryName
* Name of directory to export.
*/
public void exportDirectory(@Nonnull Workspace workspace,
@Nonnull WorkspaceResource resource,
@Nonnull FileBundle bundle,
@Nullable String directoryName) {
FileBundle bundleCopy = new BasicFileBundle();
bundle.valuesAsCopy().forEach(cls -> {
if (directoryName == null && cls.getDirectoryName() == null)
bundleCopy.put(cls);
else if (cls.getName().startsWith(directoryName + "/"))
bundleCopy.put(cls);
});
WorkspaceResource resourceCopy = new WorkspaceResourceBuilder().withFileBundle(bundleCopy).build();
Workspace workspaceCopy = new BasicWorkspace(resourceCopy);
pathExportingManager.export(workspaceCopy, "directory", false);
}

/**
* Prompts the user <i>(if configured, otherwise prompt is skipped)</i> to delete the class.
*
Expand Down Expand Up @@ -2002,7 +2095,7 @@ public void addClassMethod(@Nonnull Workspace workspace,
}

/**
* Prompts the user for method declaration info, to add it to the given class.
* Prompts the user to select a method to override.
*
* @param workspace
* Containing workspace.
Expand All @@ -2014,9 +2107,9 @@ public void addClassMethod(@Nonnull Workspace workspace,
* Class to update.
*/
public void overrideClassMethod(@Nonnull Workspace workspace,
@Nonnull WorkspaceResource resource,
@Nonnull JvmClassBundle bundle,
@Nonnull JvmClassInfo info) {
@Nonnull WorkspaceResource resource,
@Nonnull JvmClassBundle bundle,
@Nonnull JvmClassInfo info) {
InheritanceGraph graph = graphProvider.get();
if (graph == null)
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,19 @@
import software.coley.recaf.info.Info;
import software.coley.recaf.info.JvmClassInfo;
import software.coley.recaf.services.workspace.WorkspaceManager;
import software.coley.recaf.services.workspace.io.*;
import software.coley.recaf.services.workspace.io.PathWorkspaceExportConsumer;
import software.coley.recaf.services.workspace.io.WorkspaceCompressType;
import software.coley.recaf.services.workspace.io.WorkspaceExportOptions;
import software.coley.recaf.services.workspace.io.WorkspaceExporter;
import software.coley.recaf.services.workspace.io.WorkspaceOutputType;
import software.coley.recaf.ui.config.ExportConfig;
import software.coley.recaf.ui.config.RecentFilesConfig;
import software.coley.recaf.util.*;
import software.coley.recaf.util.DirectoryChooserBuilder;
import software.coley.recaf.util.ErrorDialogs;
import software.coley.recaf.util.FileChooserBuilder;
import software.coley.recaf.util.Icons;
import software.coley.recaf.util.Lang;
import software.coley.recaf.util.StringUtil;
import software.coley.recaf.workspace.model.Workspace;
import software.coley.recaf.workspace.model.resource.WorkspaceDirectoryResource;
import software.coley.recaf.workspace.model.resource.WorkspaceFileResource;
Expand Down Expand Up @@ -69,18 +78,36 @@ public void exportCurrent() {
* Workspace to export.
*/
public void export(@Nonnull Workspace workspace) {
export(workspace, "workspace", true);
}

/**
* Export the given workspace.
*
* @param workspace
* Workspace to export.
* @param contentDescription
* Description string for what is within the workspace.
* @param alertWhenEmpty
* {@code true} to warn users before exporting that no changes are present in the workspace.
* {@code false} to skip the warning.
*/
public void export(@Nonnull Workspace workspace, @Nonnull String contentDescription, boolean alertWhenEmpty) {
WorkspaceResource primaryResource = workspace.getPrimaryResource();

// Check if the user hasn't made any changes. Plenty of people have not understood that their changes weren't
// saved for one reason or another (the amount of people seeing a red flash thinking that is fine is crazy)
WorkspaceResource primaryResource = workspace.getPrimaryResource();
boolean noChangesFound = exportConfig.getWarnNoChanges().getValue() && primaryResource.bundleStreamRecursive()
.allMatch(b -> b.getDirtyKeys().isEmpty());
if (noChangesFound) {
Alert alert = new Alert(Alert.AlertType.CONFIRMATION, Lang.get("dialog.file.nochanges"), ButtonType.YES, ButtonType.NO);
alert.setTitle(Lang.get("dialog.title.nochanges"));
Stage stage = (Stage) alert.getDialogPane().getScene().getWindow();
stage.getIcons().add(Icons.getImage(Icons.LOGO));
if (alert.showAndWait().orElse(ButtonType.NO) != ButtonType.YES)
return;
if (alertWhenEmpty) {
boolean noChangesFound = exportConfig.getWarnNoChanges().getValue() && primaryResource.bundleStreamRecursive()
.allMatch(b -> b.getDirtyKeys().isEmpty());
if (noChangesFound) {
Alert alert = new Alert(Alert.AlertType.CONFIRMATION, Lang.get("dialog.file.nochanges"), ButtonType.YES, ButtonType.NO);
alert.setTitle(Lang.get("dialog.title.nochanges"));
Stage stage = (Stage) alert.getDialogPane().getScene().getWindow();
stage.getIcons().add(Icons.getImage(Icons.LOGO));
if (alert.showAndWait().orElse(ButtonType.NO) != ButtonType.YES)
return;
}
}

// Prompt a path for the user to write to.
Expand Down Expand Up @@ -117,9 +144,9 @@ public void export(@Nonnull Workspace workspace) {
WorkspaceExporter exporter = createResourceExporter(primaryResource, exportPath);
try {
exporter.export(workspace);
logger.info("Exported workspace to path '{}'", exportPath);
logger.info("Exported " + contentDescription + " to path '{}'", exportPath);
} catch (IOException ex) {
logger.error("Failed to export workspace to path '{}'", exportPath, ex);
logger.error("Failed to export " + contentDescription + " to path '{}'", exportPath, ex);
ErrorDialogs.show(
Lang.getBinding("dialog.error.exportworkspace.title"),
Lang.getBinding("dialog.error.exportworkspace.header"),
Expand Down
2 changes: 2 additions & 0 deletions recaf-ui/src/main/resources/translations/en_US.lang
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ menu.edit.changeversion.up=Upgrade
menu.edit.changeversion.down=Downgrade
menu.export.class=Export class
menu.export.file=Export file
menu.export.package=Export package
menu.export.directory=Export directory
menu.help=Help
menu.help.discord=Discord
menu.help.docs=Online user documentation
Expand Down

0 comments on commit 8199604

Please sign in to comment.