diff --git a/NEWS b/NEWS index 95c0f3365..33ec62e6f 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,9 @@ Please see the file `README' for a description of how to report bugs. ** Changes between version 7.1.0 and X.X.X * USE now supports data types +* [USE] The plugin and OCL extension directories can now be configured at + startup with the -pluginDir and -extensionsDir arguments, instead of being + fixed to /lib/plugins and /oclextensions (#20) ** Changes between version 5.2.0 and 6.0.0 * New OCL complexity plugin diff --git a/use-core/src/main/java/org/tzi/use/config/Options.java b/use-core/src/main/java/org/tzi/use/config/Options.java index 9064cdb5e..b5f0b484c 100644 --- a/use-core/src/main/java/org/tzi/use/config/Options.java +++ b/use-core/src/main/java/org/tzi/use/config/Options.java @@ -184,6 +184,11 @@ public static WarningType getType(String type) { */ public static Path pluginDir = null; + /** + * Directory containing the OCL extension definitions. + */ + public static Path oclExtensionsDir = null; + /** * Preferred size of the diagram views. */ @@ -246,6 +251,12 @@ private static void printHelp() { System.out.println(" -noplugins do not use plugins"); System.out.println(" -h print help"); System.out.println(" -H=path home of use installation"); + System.out.println(" -pluginDir=path"); + System.out.println(" directory to load plugins from"); + System.out.println(" (default: /lib/plugins)"); + System.out.println(" -extensionsDir=path"); + System.out.println(" directory to load OCL extensions from"); + System.out.println(" (default: /oclextensions)"); System.out.println(" -nr suppress warnings about missing readline library"); System.out.println(" -q reads spec_file, executes cmd_file, and checks constraints"); System.out.println(" exit code is 1 if constraints fail, otherwise 0"); @@ -319,6 +330,7 @@ public static void resetOptions() { checkWarningsUnrelatedTypes = WarningType.WARN; doPLUGIN = true; pluginDir = null; + oclExtensionsDir = null; fDiagramDimension = new Dimension( 600, 600 ); props = null; specFilename = null; @@ -349,14 +361,30 @@ public static void processArgs(String[] args) { Options.doGUI = false; } else if (arg.equals("noplugins")) { Options.doPLUGIN = false; - } else if (arg.startsWith("H=")) { + } else if (arg.startsWith("H=")) { try { homeDir = Paths.get(arg.substring(2)); } catch (InvalidPathException e) { System.err.println("Invalid path " + StringUtil.inQuotes(arg.substring(2)) + " for home directory specified."); System.exit(1); } - } else if (arg.equals("nr")) { + } else if (arg.startsWith("pluginDir=")) { + String value = arg.substring("pluginDir=".length()); + try { + pluginDir = Paths.get(value); + } catch (InvalidPathException e) { + System.err.println("Invalid path " + StringUtil.inQuotes(value) + " for plugin directory specified."); + System.exit(1); + } + } else if (arg.startsWith("extensionsDir=")) { + String value = arg.substring("extensionsDir=".length()); + try { + oclExtensionsDir = Paths.get(value); + } catch (InvalidPathException e) { + System.err.println("Invalid path " + StringUtil.inQuotes(value) + " for OCL extensions directory specified."); + System.exit(1); + } + } else if (arg.equals("nr")) { suppressWarningsAboutMissingReadlineLibrary = true; } else if (arg.equals("q")) { Options.quiet = true; @@ -441,7 +469,14 @@ public static void processArgs(String[] args) { } setLastDirectory(homeDir); - pluginDir = homeDir.resolve("lib").resolve("plugins"); + // Both directories default to a location below the home directory, but + // can be overridden independently via -pluginDir / -extensionsDir. + if (pluginDir == null) { + pluginDir = homeDir.resolve("lib").resolve("plugins"); + } + if (oclExtensionsDir == null) { + oclExtensionsDir = homeDir.resolve("oclextensions"); + } if (quiet && (specFilename == null || cmdFilename == null) ) { System.err diff --git a/use-core/src/test/java/org/tzi/use/config/OptionsDirectoryArgsTest.java b/use-core/src/test/java/org/tzi/use/config/OptionsDirectoryArgsTest.java new file mode 100644 index 000000000..b5ac5a94d --- /dev/null +++ b/use-core/src/test/java/org/tzi/use/config/OptionsDirectoryArgsTest.java @@ -0,0 +1,118 @@ +/* + * USE - UML based specification environment + * Copyright (C) 1999-2026 University of Bremen & University of Applied Sciences Hamburg + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package org.tzi.use.config; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Properties; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Tests for the configurable directories for plugins and OCL extensions + * (issue #20). + * + *

By default both directories are derived from the USE home directory + * ({@code -H}): plugins live in {@code /lib/plugins} and OCL extensions + * in {@code /oclextensions}. These tests verify that both defaults are + * applied and that each directory can be overridden independently with a + * startup argument ({@code -pluginDir=} and {@code -extensionsDir=}).

+ * + * @author Cansin Yildiz + * @author Claude + */ +public class OptionsDirectoryArgsTest { + + private Properties savedSystemProperties; + + @BeforeEach + public void setUp() { + // processArgs() augments the global system properties; remember them + // so we can restore them and keep the test isolated. + savedSystemProperties = (Properties) System.getProperties().clone(); + Options.resetOptions(); + } + + @AfterEach + public void tearDown() { + Options.resetOptions(); + System.setProperties(savedSystemProperties); + } + + private Path home() { + return Paths.get(System.getProperty("java.io.tmpdir"), "use-home-under-test"); + } + + @Test + public void pluginDirDefaultsToHomeLibPlugins() { + Path home = home(); + Options.processArgs(new String[] { "-H=" + home, "-c" }); + assertEquals(home.resolve("lib").resolve("plugins"), Options.pluginDir, + "plugin directory must default to /lib/plugins"); + } + + @Test + public void extensionsDirDefaultsToHomeOclextensions() { + Path home = home(); + Options.processArgs(new String[] { "-H=" + home, "-c" }); + assertEquals(home.resolve("oclextensions"), Options.oclExtensionsDir, + "OCL extensions directory must default to /oclextensions"); + } + + @Test + public void pluginDirCanBeOverridden() { + Path home = home(); + Path custom = Paths.get(System.getProperty("java.io.tmpdir"), "custom-plugins"); + Options.processArgs(new String[] { "-H=" + home, "-pluginDir=" + custom, "-c" }); + assertEquals(custom, Options.pluginDir, + "-pluginDir must override the plugin directory"); + assertEquals(home.resolve("oclextensions"), Options.oclExtensionsDir, + "overriding the plugin directory must not affect the extensions directory"); + } + + @Test + public void extensionsDirCanBeOverridden() { + Path home = home(); + Path custom = Paths.get(System.getProperty("java.io.tmpdir"), "custom-extensions"); + Options.processArgs(new String[] { "-H=" + home, "-extensionsDir=" + custom, "-c" }); + assertEquals(custom, Options.oclExtensionsDir, + "-extensionsDir must override the OCL extensions directory"); + assertEquals(home.resolve("lib").resolve("plugins"), Options.pluginDir, + "overriding the extensions directory must not affect the plugin directory"); + } + + @Test + public void bothDirectoriesCanBeOverriddenIndependentlyOfHome() { + Path home = home(); + Path customPlugins = Paths.get(System.getProperty("java.io.tmpdir"), "p"); + Path customExtensions = Paths.get(System.getProperty("java.io.tmpdir"), "e"); + Options.processArgs(new String[] { + "-H=" + home, + "-pluginDir=" + customPlugins, + "-extensionsDir=" + customExtensions, + "-c" }); + assertEquals(customPlugins, Options.pluginDir); + assertEquals(customExtensions, Options.oclExtensionsDir); + } +} diff --git a/use-gui/src/main/java/org/tzi/use/main/gui/fx/JavaFXAppLauncher.java b/use-gui/src/main/java/org/tzi/use/main/gui/fx/JavaFXAppLauncher.java index 0de0b4026..eda4516ca 100644 --- a/use-gui/src/main/java/org/tzi/use/main/gui/fx/JavaFXAppLauncher.java +++ b/use-gui/src/main/java/org/tzi/use/main/gui/fx/JavaFXAppLauncher.java @@ -61,7 +61,7 @@ public void start(Stage primaryStage) throws Exception { private void initExtensions() { if (!Options.disableExtensions) { - ExtensionManager.EXTENSIONS_FOLDER = Options.homeDir + Options.FILE_SEPARATOR + "oclextensions"; + ExtensionManager.EXTENSIONS_FOLDER = Options.oclExtensionsDir.toString(); ExtensionManager.getInstance().loadExtensions(); } } diff --git a/use-gui/src/main/java/org/tzi/use/main/gui/swing/MainSwing.java b/use-gui/src/main/java/org/tzi/use/main/gui/swing/MainSwing.java index 0723eaa45..f535da01a 100644 --- a/use-gui/src/main/java/org/tzi/use/main/gui/swing/MainSwing.java +++ b/use-gui/src/main/java/org/tzi/use/main/gui/swing/MainSwing.java @@ -42,7 +42,7 @@ public void launch(String[] args) { MSystem system = null; if (!Options.disableExtensions) { - ExtensionManager.EXTENSIONS_FOLDER = Options.homeDir + Options.FILE_SEPARATOR + "oclextensions"; + ExtensionManager.EXTENSIONS_FOLDER = Options.oclExtensionsDir.toString(); ExtensionManager.getInstance().loadExtensions(); }