GP-2796 - Refactor DockingApplicationLayout to be more generic

This commit is contained in:
dragonmacher 2022-11-18 11:57:32 -05:00
parent 3586062eb4
commit 93f9e93cd7
9 changed files with 74 additions and 217 deletions

View file

@ -27,11 +27,11 @@ import javax.swing.table.TableModel;
import db.buffers.LocalBufferFile; import db.buffers.LocalBufferFile;
import docking.framework.DockingApplicationConfiguration; import docking.framework.DockingApplicationConfiguration;
import docking.framework.DockingApplicationLayout;
import docking.widgets.combobox.GComboBox; import docking.widgets.combobox.GComboBox;
import docking.widgets.filechooser.GhidraFileChooser; import docking.widgets.filechooser.GhidraFileChooser;
import docking.widgets.label.GDLabel; import docking.widgets.label.GDLabel;
import docking.widgets.label.GLabel; import docking.widgets.label.GLabel;
import generic.application.GenericApplicationLayout;
import ghidra.app.plugin.debug.dbtable.DbLargeTableModel; import ghidra.app.plugin.debug.dbtable.DbLargeTableModel;
import ghidra.app.plugin.debug.dbtable.DbSmallTableModel; import ghidra.app.plugin.debug.dbtable.DbSmallTableModel;
import ghidra.framework.Application; import ghidra.framework.Application;
@ -268,7 +268,7 @@ public class DbViewer extends JFrame {
*/ */
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
ApplicationLayout layout = new DockingApplicationLayout("DB Viewer", "1.0"); ApplicationLayout layout = new GenericApplicationLayout("DB Viewer", "1.0");
DockingApplicationConfiguration configuration = new DockingApplicationConfiguration(); DockingApplicationConfiguration configuration = new DockingApplicationConfiguration();
configuration.setShowSplashScreen(false); configuration.setShowSplashScreen(false);

View file

@ -21,6 +21,7 @@ import javax.swing.*;
import docking.DialogComponentProvider; import docking.DialogComponentProvider;
import docking.DockingWindowManager; import docking.DockingWindowManager;
import generic.application.GenericApplicationLayout;
import generic.theme.GThemeDefaults.Colors; import generic.theme.GThemeDefaults.Colors;
import ghidra.framework.Application; import ghidra.framework.Application;
import utility.application.ApplicationLayout; import utility.application.ApplicationLayout;
@ -68,7 +69,7 @@ public class AboutDialog extends DialogComponentProvider {
} }
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
ApplicationLayout layout = new DockingApplicationLayout("About Dialog", "1.0"); ApplicationLayout layout = new GenericApplicationLayout("About Dialog", "1.0");
DockingApplicationConfiguration config = new DockingApplicationConfiguration(); DockingApplicationConfiguration config = new DockingApplicationConfiguration();
config.setShowSplashScreen(false); config.setShowSplashScreen(false);
Application.initializeApplication(layout, config); Application.initializeApplication(layout, config);

View file

@ -24,6 +24,7 @@ import javax.swing.border.BevelBorder;
import docking.*; import docking.*;
import docking.widgets.label.GDLabel; import docking.widgets.label.GDLabel;
import generic.application.GenericApplicationLayout;
import generic.theme.GColor; import generic.theme.GColor;
import generic.theme.Gui; import generic.theme.Gui;
import generic.util.WindowUtilities; import generic.util.WindowUtilities;
@ -317,7 +318,7 @@ public class SplashScreen extends JWindow {
} }
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
ApplicationLayout layout = new DockingApplicationLayout("Splash Screen Main", "1.0"); ApplicationLayout layout = new GenericApplicationLayout("Splash Screen Main", "1.0");
DockingApplicationConfiguration config = new DockingApplicationConfiguration(); DockingApplicationConfiguration config = new DockingApplicationConfiguration();
config.setShowSplashScreen(false); config.setShowSplashScreen(false);

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package docking.framework; package generic.application;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
@ -33,10 +33,11 @@ import utility.module.ClasspathFilter;
import utility.module.ModuleUtilities; import utility.module.ModuleUtilities;
/** /**
* The docking application layout defines the customizable elements of a docking application's * A low-level implementation of {@link ApplicationLayout} that is suitable for basic applications.
* directory structure. * This class makes use of the {@link GModule Module} system to find application components at
* runtime.
*/ */
public class DockingApplicationLayout extends ApplicationLayout { public class GenericApplicationLayout extends ApplicationLayout {
private static final String NO_RELEASE_NAME = "NO_RELEASE"; private static final String NO_RELEASE_NAME = "NO_RELEASE";
@ -45,30 +46,30 @@ public class DockingApplicationLayout extends ApplicationLayout {
Pattern.compile(".*/(\\w+)/bin/main"); Pattern.compile(".*/(\\w+)/bin/main");
/** /**
* Constructs a new docking application layout object with the given name and version. * Constructs a new application layout object with the given name and version.
* *
* @param name The name of the application. * @param name The name of the application.
* @param version The version of the application. * @param version The version of the application.
* @throws FileNotFoundException if there was a problem getting a user directory. * @throws FileNotFoundException if there was a problem getting a user directory.
*/ */
public DockingApplicationLayout(String name, String version) throws FileNotFoundException { public GenericApplicationLayout(String name, String version) throws FileNotFoundException {
this(new ApplicationProperties(name, version, NO_RELEASE_NAME)); this(new ApplicationProperties(name, version, NO_RELEASE_NAME));
} }
/** /**
* Constructs a new docking application layout object with the given set of application * Constructs a new application layout object with the given set of application
* properties. The default Ghidra application root directory(s) will be used. * properties. The default Ghidra application root directory(s) will be used.
* *
* @param applicationProperties The properties object that will be read system properties. * @param applicationProperties The properties object that will be read system properties.
* @throws FileNotFoundException if there was a problem getting a user directory. * @throws FileNotFoundException if there was a problem getting a user directory.
*/ */
public DockingApplicationLayout(ApplicationProperties applicationProperties) public GenericApplicationLayout(ApplicationProperties applicationProperties)
throws FileNotFoundException { throws FileNotFoundException {
this(getDefaultApplicationRootDirs(), applicationProperties); this(getDefaultApplicationRootDirs(), applicationProperties);
} }
/** /**
* Constructs a new docking application layout object with the given set of application * Constructs a new application layout object with the given set of application
* properties. * properties.
* *
* @param applicationRootDirs list of application root directories which should be * @param applicationRootDirs list of application root directories which should be
@ -77,7 +78,7 @@ public class DockingApplicationLayout extends ApplicationLayout {
* @param applicationProperties The properties object that will be read system properties. * @param applicationProperties The properties object that will be read system properties.
* @throws FileNotFoundException if there was a problem getting a user directory. * @throws FileNotFoundException if there was a problem getting a user directory.
*/ */
public DockingApplicationLayout(Collection<ResourceFile> applicationRootDirs, public GenericApplicationLayout(Collection<ResourceFile> applicationRootDirs,
ApplicationProperties applicationProperties) throws FileNotFoundException { ApplicationProperties applicationProperties) throws FileNotFoundException {
this.applicationProperties = Objects.requireNonNull(applicationProperties); this.applicationProperties = Objects.requireNonNull(applicationProperties);

View file

@ -328,8 +328,7 @@ public class Application {
return null; return null;
} }
private List<ResourceFile> findFilesByExtensionInModule(String moduleName, String extension) private List<ResourceFile> findFilesByExtensionInModule(String moduleName, String extension) {
throws IllegalArgumentException {
extension = verifyExtension(extension); extension = verifyExtension(extension);
List<ResourceFile> list = new ArrayList<>(); List<ResourceFile> list = new ArrayList<>();
GModule gModule = app.layout.getModules().get(moduleName); GModule gModule = app.layout.getModules().get(moduleName);
@ -672,7 +671,7 @@ public class Application {
* Returns the installation directory. In an installation, there is only one application root * Returns the installation directory. In an installation, there is only one application root
* and its parent is the installation directory. If not an installation, then this call doesn't * and its parent is the installation directory. If not an installation, then this call doesn't
* really make sense, but it will return the parent of the first installation root. * really make sense, but it will return the parent of the first installation root.
* @return * @return the directory
*/ */
public static ResourceFile getInstallationDirectory() { public static ResourceFile getInstallationDirectory() {
checkAppInitialized(); checkAppInitialized();
@ -697,8 +696,8 @@ public class Application {
*/ */
public static boolean isTestBuild() { public static boolean isTestBuild() {
checkAppInitialized(); checkAppInitialized();
String value = app.layout.getApplicationProperties().getProperty( String value = app.layout.getApplicationProperties()
ApplicationProperties.TEST_RELEASE_PROPERTY); .getProperty(ApplicationProperties.TEST_RELEASE_PROPERTY);
if (value == null) { if (value == null) {
return false; return false;
} }
@ -818,8 +817,7 @@ public class Application {
* @param extension the filename extension for which to find file.s * @param extension the filename extension for which to find file.s
* @return a list of all files with the given extension that are located in the named module. * @return a list of all files with the given extension that are located in the named module.
*/ */
public static List<ResourceFile> findFilesByExtension(String moduleName, String extension) public static List<ResourceFile> findFilesByExtension(String moduleName, String extension) {
throws IllegalArgumentException {
checkAppInitialized(); checkAppInitialized();
return app.findFilesByExtensionInModule(moduleName, extension); return app.findFilesByExtensionInModule(moduleName, extension);
} }
@ -839,6 +837,7 @@ public class Application {
/** /**
* Returns the directory relative to the calling class's module's data directory. * Returns the directory relative to the calling class's module's data directory.
* @param relativePath the path relative the module's data directory * @param relativePath the path relative the module's data directory
* @return the directory
* @throws FileNotFoundException if the directory does not exist. * @throws FileNotFoundException if the directory does not exist.
* @throws IOException if an error occurred trying to access the directory. * @throws IOException if an error occurred trying to access the directory.
*/ */
@ -859,6 +858,7 @@ public class Application {
* be prepended to the given path) * be prepended to the given path)
* @param moduleName the name of the module. * @param moduleName the name of the module.
* @param relativePath the path relative to the module's data directory. * @param relativePath the path relative to the module's data directory.
* @return @return the directory
* @throws FileNotFoundException if the directory does not exist * @throws FileNotFoundException if the directory does not exist
* @throws IOException if an error occurred trying to access the directory. * @throws IOException if an error occurred trying to access the directory.
*/ */
@ -872,6 +872,7 @@ public class Application {
* Return the directory relative the the name module's directory. * Return the directory relative the the name module's directory.
* @param moduleName the name of the module. * @param moduleName the name of the module.
* @param relativePath the path relative to the module's root directory. * @param relativePath the path relative to the module's root directory.
* @return the directory
* @throws FileNotFoundException if the directory does not exist * @throws FileNotFoundException if the directory does not exist
* @throws IOException if an error occurred trying to access the directory. * @throws IOException if an error occurred trying to access the directory.
*/ */
@ -884,6 +885,7 @@ public class Application {
/** /**
* Returns the file relative to the calling class's module's data directory * Returns the file relative to the calling class's module's data directory
* @param relativeDataPath the path relative the to module's data directory * @param relativeDataPath the path relative the to module's data directory
* @return the file
* @throws FileNotFoundException if the file or module does not exist. * @throws FileNotFoundException if the file or module does not exist.
*/ */
public static ResourceFile getModuleDataFile(String relativeDataPath) public static ResourceFile getModuleDataFile(String relativeDataPath)
@ -902,6 +904,7 @@ public class Application {
* be prepended to the give path) * be prepended to the give path)
* @param moduleName the name of the module. * @param moduleName the name of the module.
* @param relativeDataPath the path relative to the module's data directory. * @param relativeDataPath the path relative to the module's data directory.
* @return the file
* @throws FileNotFoundException if the file does not exist. * @throws FileNotFoundException if the file does not exist.
*/ */
public static ResourceFile getModuleDataFile(String moduleName, String relativeDataPath) public static ResourceFile getModuleDataFile(String moduleName, String relativeDataPath)
@ -914,6 +917,7 @@ public class Application {
* Returns the file relative to the named module's directory. * Returns the file relative to the named module's directory.
* @param moduleName the name of the module. * @param moduleName the name of the module.
* @param relativePath the path relative to the module's data directory. * @param relativePath the path relative to the module's data directory.
* @return the file
* @throws FileNotFoundException if the file does not exist. * @throws FileNotFoundException if the file does not exist.
*/ */
public static ResourceFile getModuleFile(String moduleName, String relativePath) public static ResourceFile getModuleFile(String moduleName, String relativePath)

View file

@ -21,6 +21,7 @@ import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.*; import java.util.*;
import generic.application.GenericApplicationLayout;
import generic.theme.ApplicationThemeManager; import generic.theme.ApplicationThemeManager;
import ghidra.framework.Application; import ghidra.framework.Application;
import ghidra.framework.ApplicationConfiguration; import ghidra.framework.ApplicationConfiguration;
@ -77,7 +78,8 @@ public class GHelpBuilder {
return false; return false;
} }
}; };
Application.initializeApplication(new HelpApplicationLayout("Help Builder", "0.1"), config); Application.initializeApplication(new GenericApplicationLayout("Help Builder", "0.1"),
config);
builder.build(args); builder.build(args);
} }

View file

@ -1,154 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package help;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.*;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import generic.jar.ResourceFile;
import ghidra.framework.ApplicationProperties;
import ghidra.framework.GModule;
import ghidra.util.SystemUtilities;
import util.CollectionUtils;
import utility.application.ApplicationLayout;
import utility.application.ApplicationUtilities;
import utility.module.ClasspathFilter;
import utility.module.ModuleUtilities;
//
// TODO this class should be deleted when the GP-1981 branch is merged into master. The
// DockingApplicationLayout is not accessible by help, due to Docking depending on Help.
// Much of the application layout code should live in Utility so that it is reachable by more
// modules. Docking and Ghidra application layout classes should also have their names
// changed.
//
// Perhaps: ModuleAppliactionLayout and ClasspathApplicationLayout.
//
public class HelpApplicationLayout extends ApplicationLayout {
private static final String NO_RELEASE_NAME = "NO_RELEASE";
/** Dev mode main source bin dir pattern */
private static final Pattern CLASS_PATH_MODULE_NAME_PATTERN =
Pattern.compile(".*/(\\w+)/bin/main");
/**
* Constructs a new docking application layout object with the given name and version.
*
* @param name The name of the application.
* @param version The version of the application.
* @throws FileNotFoundException if there was a problem getting a user directory.
*/
public HelpApplicationLayout(String name, String version) throws FileNotFoundException {
this.applicationProperties =
Objects.requireNonNull(new ApplicationProperties(name, version, NO_RELEASE_NAME));
this.applicationRootDirs = getDefaultApplicationRootDirs();
applicationRootDirs.addAll(getAdditionalApplicationRootDirs(applicationRootDirs));
// Application installation directory
applicationInstallationDir = applicationRootDirs.iterator().next().getParentFile();
if (SystemUtilities.isInDevelopmentMode()) {
applicationInstallationDir = applicationInstallationDir.getParentFile();
}
// Modules
if (SystemUtilities.isInDevelopmentMode()) {
// In development mode we rely on the IDE's classpath to determine which modules to
// include, as opposed to scanning the filesystem. This prevents unrelated modules
// from being used.
modules = ModuleUtilities.findModules(applicationRootDirs,
ModuleUtilities.findModuleRootDirectories(applicationRootDirs),
new ClasspathFilter());
}
else {
modules = ModuleUtilities.findModules(applicationRootDirs, applicationRootDirs);
}
// User directories
userTempDir = ApplicationUtilities.getDefaultUserTempDir(applicationProperties);
userSettingsDir = ApplicationUtilities.getDefaultUserSettingsDir(applicationProperties,
applicationInstallationDir);
}
protected Collection<ResourceFile> getAdditionalApplicationRootDirs(
Collection<ResourceFile> roots) {
return Collections.emptyList();
}
protected Map<String, GModule> findModules() {
if (!SystemUtilities.isInDevelopmentMode()) {
// in release mode we only have one application root, so no need to find all others
return ModuleUtilities.findModules(applicationRootDirs, applicationRootDirs);
}
// In development mode we may have multiple module root directories under which modules may
// be found. Search all roots for modules.
Collection<ResourceFile> roots =
ModuleUtilities.findModuleRootDirectories(applicationRootDirs, new ArrayList<>());
Map<String, GModule> allModules = ModuleUtilities.findModules(applicationRootDirs, roots);
// Filter any modules found to ensure that we only include those that are listed on the
// classpath. (Due to the nature of how the development classpath is created, not all
// found modules may match the classpath entries.)
Set<String> cpNames = getClassPathModuleNames();
Map<String, GModule> filteredModules = new HashMap<>();
Set<Entry<String, GModule>> entrySet = allModules.entrySet();
for (Entry<String, GModule> entry : entrySet) {
GModule module = entry.getValue();
if (cpNames.contains(module.getName())) {
filteredModules.put(entry.getKey(), module);
}
}
return filteredModules;
}
private Set<String> getClassPathModuleNames() {
String cp = System.getProperty("java.class.path");
String[] pathParts = cp.split(File.pathSeparator);
Set<String> paths = new HashSet<>(Arrays.asList(pathParts));
Set<String> cpNames = new HashSet<>();
for (String cpEntry : paths) {
Matcher matcher = CLASS_PATH_MODULE_NAME_PATTERN.matcher(cpEntry);
if (matcher.matches()) {
cpNames.add(matcher.group(1));
}
}
return cpNames;
}
/**
* Get the default list of Application directories. In repo-based
* development mode this includes the root Ghidra directory within each repo.
* When not in development mode, the requirement is that the current working
* directory correspond to the installation root. The first entry will be
* the primary root in both cases.
* @return root directories
*/
public static Collection<ResourceFile> getDefaultApplicationRootDirs() {
if (SystemUtilities.isInDevelopmentMode()) {
return ApplicationUtilities.findDefaultApplicationRootDirs();
}
return CollectionUtils.asList(new ResourceFile(System.getProperty("user.dir")));
}
}

View file

@ -24,8 +24,8 @@ import javax.swing.text.html.HTMLEditorKit;
import docking.DialogComponentProvider; import docking.DialogComponentProvider;
import docking.DockingWindowManager; import docking.DockingWindowManager;
import docking.framework.DockingApplicationConfiguration; import docking.framework.DockingApplicationConfiguration;
import docking.framework.DockingApplicationLayout;
import docking.widgets.label.GDLabel; import docking.widgets.label.GDLabel;
import generic.application.GenericApplicationLayout;
import generic.theme.Gui; import generic.theme.Gui;
import ghidra.framework.Application; import ghidra.framework.Application;
import ghidra.util.HTMLUtilities; import ghidra.util.HTMLUtilities;
@ -113,7 +113,7 @@ public class UserAgreementDialog extends DialogComponentProvider {
} }
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
ApplicationLayout layout = new DockingApplicationLayout("User Agreement Main", "1.0"); ApplicationLayout layout = new GenericApplicationLayout("User Agreement Main", "1.0");
DockingApplicationConfiguration config = new DockingApplicationConfiguration(); DockingApplicationConfiguration config = new DockingApplicationConfiguration();
Application.initializeApplication(layout, config); Application.initializeApplication(layout, config);
UserAgreementDialog dialog = new UserAgreementDialog(true, true); UserAgreementDialog dialog = new UserAgreementDialog(true, true);

View file

@ -27,7 +27,9 @@ import org.jdom.Element;
import org.jdom.input.SAXBuilder; import org.jdom.input.SAXBuilder;
import org.jdom.output.XMLOutputter; import org.jdom.output.XMLOutputter;
import docking.framework.*; import docking.framework.ApplicationInformationDisplayFactory;
import docking.framework.DockingApplicationConfiguration;
import generic.application.GenericApplicationLayout;
import ghidra.framework.*; import ghidra.framework.*;
import ghidra.framework.model.ToolServices; import ghidra.framework.model.ToolServices;
import ghidra.util.Msg; import ghidra.util.Msg;
@ -61,7 +63,7 @@ public abstract class StandAloneApplication implements GenericStandAloneApplicat
* @throws IOException error causing application initialization failure * @throws IOException error causing application initialization failure
*/ */
public StandAloneApplication(String propertiesFilename) throws IOException { public StandAloneApplication(String propertiesFilename) throws IOException {
this(new DockingApplicationLayout(readApplicationProperties(propertiesFilename))); this(new GenericApplicationLayout(readApplicationProperties(propertiesFilename)));
} }
/** /**
@ -72,7 +74,7 @@ public abstract class StandAloneApplication implements GenericStandAloneApplicat
* @throws IOException error causing application initialization failure * @throws IOException error causing application initialization failure
*/ */
public StandAloneApplication(String name, String version) throws IOException { public StandAloneApplication(String name, String version) throws IOException {
this(new DockingApplicationLayout(name, version)); this(new GenericApplicationLayout(name, version));
} }
/** /**