diff --git a/cli/pom.xml b/cli/pom.xml
index 113a30ccb..654ca1e75 100644
--- a/cli/pom.xml
+++ b/cli/pom.xml
@@ -16,7 +16,6 @@
com.devonfw.tools.ide.cli.Ideasy
ideasy
${project.artifactId}-${os.detected.classifier}-${os.detected.arch}
- 17
0.9.28
3.24.1
2.4.0
diff --git a/cli/src/main/java/com/devonfw/tools/ide/completion/CompletionCandidateCollector.java b/cli/src/main/java/com/devonfw/tools/ide/completion/CompletionCandidateCollector.java
index 9d1c64f1f..27fd2e709 100644
--- a/cli/src/main/java/com/devonfw/tools/ide/completion/CompletionCandidateCollector.java
+++ b/cli/src/main/java/com/devonfw/tools/ide/completion/CompletionCandidateCollector.java
@@ -57,7 +57,7 @@ default int addAllMatches(String text, String[] sortedCandidates, Property> pr
index++;
count++;
} else {
- index = -index;
+ index = -index - 1;
}
while ((index >= 0) && (index < sortedCandidates.length)) {
if (sortedCandidates[index].startsWith(text)) {
diff --git a/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java b/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java
index e7e4c1bc9..9ce391150 100644
--- a/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java
+++ b/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java
@@ -40,7 +40,8 @@
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
-import java.net.InetAddress;
+import java.net.URL;
+import java.net.URLConnection;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
@@ -598,7 +599,13 @@ public boolean isOnline() {
boolean online = false;
try {
int timeout = 1000;
- online = InetAddress.getByName("github.com").isReachable(timeout);
+ //open a connection to github.com and try to retrieve data
+ //getContent fails if there is no connection
+ URLConnection connection = new URL("https://www.github.com").openConnection();
+ connection.setConnectTimeout(timeout);
+ connection.getContent();
+ online = true;
+
} catch (Exception ignored) {
}
diff --git a/cli/src/main/java/com/devonfw/tools/ide/context/IdeContext.java b/cli/src/main/java/com/devonfw/tools/ide/context/IdeContext.java
index 0a87eb864..361e3a0b2 100644
--- a/cli/src/main/java/com/devonfw/tools/ide/context/IdeContext.java
+++ b/cli/src/main/java/com/devonfw/tools/ide/context/IdeContext.java
@@ -445,6 +445,19 @@ default String getMavenArgs() {
}
+ /**
+ * @return the String value for the variable M2_REPO, or null if called outside an IDEasy installation.
+ */
+ default String getMavenRepoEnvVariable() {
+
+ if (getIdeHome() != null) {
+ Path m2Repo = getConfPath().resolve(Mvn.M2_CONFIG_FOLDER).resolve("repository");
+ return m2Repo.toString();
+ }
+ return null;
+
+ }
+
/**
* Updates the current working directory (CWD) and configures the environment paths according to the specified parameters. This method is central to changing
* the IDE's notion of where it operates, affecting where configurations, workspaces, settings, and other resources are located or loaded from.
diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/mvn/Mvn.java b/cli/src/main/java/com/devonfw/tools/ide/tool/mvn/Mvn.java
index 66132d649..607b513fb 100644
--- a/cli/src/main/java/com/devonfw/tools/ide/tool/mvn/Mvn.java
+++ b/cli/src/main/java/com/devonfw/tools/ide/tool/mvn/Mvn.java
@@ -33,7 +33,8 @@ public class Mvn extends PluginBasedCommandlet {
/** The name of the settings.xml */
public static final String SETTINGS_FILE = "settings.xml";
- private static final String M2_CONFIG_FOLDER = ".m2";
+ /** The name of the m2 repository */
+ public static final String M2_CONFIG_FOLDER = ".m2";
private static final String SETTINGS_SECURITY_FILE = "settings-security.xml";
diff --git a/cli/src/main/java/com/devonfw/tools/ide/variable/IdeVariables.java b/cli/src/main/java/com/devonfw/tools/ide/variable/IdeVariables.java
index 396939a53..059f8b9fd 100644
--- a/cli/src/main/java/com/devonfw/tools/ide/variable/IdeVariables.java
+++ b/cli/src/main/java/com/devonfw/tools/ide/variable/IdeVariables.java
@@ -41,9 +41,12 @@ public interface IdeVariables {
/** {@link VariableDefinition} for version of maven (mvn). */
VariableDefinitionVersion MVN_VERSION = new VariableDefinitionVersion("MVN_VERSION", "MAVEN_VERSION");
- /** {@link VariableDefinition} arguments for maven to set the m2 repo location. */
+ /** {@link VariableDefinition} arguments for maven to locate the settings file. */
VariableDefinitionString MAVEN_ARGS = new VariableDefinitionString("MAVEN_ARGS", null, c -> c.getMavenArgs(), false, true);
+ /** {@link VariableDefinition} arguments for maven to set the m2 repo location. */
+ VariableDefinitionString M2_REPO = new VariableDefinitionString("M2_REPO", null, c -> c.getMavenRepoEnvVariable(), false, true);
+
/** {@link VariableDefinition} for {@link com.devonfw.tools.ide.context.IdeContext#getWorkspaceName() WORKSPACE}. */
VariableDefinitionString DOCKER_EDITION = new VariableDefinitionString("DOCKER_EDITION", null, c -> "rancher");
@@ -58,7 +61,7 @@ public interface IdeVariables {
/** A {@link Collection} with all pre-defined {@link VariableDefinition}s. */
Collection> VARIABLES = List.of(PATH, HOME, WORKSPACE_PATH, IDE_HOME, IDE_ROOT, WORKSPACE, IDE_TOOLS, CREATE_START_SCRIPTS,
- IDE_MIN_VERSION, MVN_VERSION, DOCKER_EDITION, JASYPT_OPTS, MAVEN_ARGS, PROJECT_NAME);
+ IDE_MIN_VERSION, MVN_VERSION, M2_REPO, DOCKER_EDITION, JASYPT_OPTS, MAVEN_ARGS, PROJECT_NAME);
/**
* @param name the name of the requested {@link VariableDefinition}.
diff --git a/cli/src/main/java/com/devonfw/tools/ide/version/VersionSegment.java b/cli/src/main/java/com/devonfw/tools/ide/version/VersionSegment.java
index fb47384f5..8f067891b 100644
--- a/cli/src/main/java/com/devonfw/tools/ide/version/VersionSegment.java
+++ b/cli/src/main/java/com/devonfw/tools/ide/version/VersionSegment.java
@@ -222,6 +222,12 @@ public VersionComparisonResult compareVersion(VersionSegment other) {
if (!lettersResult.isEqual()) {
return lettersResult;
}
+ if (!"_".equals(this.separator) && "_".equals(other.separator)) {
+ return VersionComparisonResult.GREATER;
+ } else if ("_".equals(this.separator) && !"_".equals(other.separator)) {
+ return VersionComparisonResult.LESS;
+ }
+
if (this.number < other.number) {
return VersionComparisonResult.LESS;
} else if (this.number > other.number) {
diff --git a/cli/src/test/java/com/devonfw/tools/ide/completion/CompletionCandidateCollectorDefaultTest.java b/cli/src/test/java/com/devonfw/tools/ide/completion/CompletionCandidateCollectorDefaultTest.java
new file mode 100644
index 000000000..d0ed48464
--- /dev/null
+++ b/cli/src/test/java/com/devonfw/tools/ide/completion/CompletionCandidateCollectorDefaultTest.java
@@ -0,0 +1,34 @@
+package com.devonfw.tools.ide.completion;
+
+import com.devonfw.tools.ide.commandlet.Commandlet;
+import com.devonfw.tools.ide.commandlet.VersionCommandlet;
+import com.devonfw.tools.ide.context.AbstractIdeContextTest;
+import com.devonfw.tools.ide.context.IdeContext;
+import com.devonfw.tools.ide.context.IdeTestContextMock;
+import com.devonfw.tools.ide.property.Property;
+import com.devonfw.tools.ide.property.VersionProperty;
+import org.junit.jupiter.api.Test;
+
+class CompletionCandidateCollectorDefaultTest extends AbstractIdeContextTest {
+
+ /**
+ * Test of {@link CompletionCandidateCollectorDefault#addAllMatches(String, String[], Property, Commandlet)}
+ */
+ @Test
+ public void testAddAllMatches() {
+
+ String[] sortedCandidates = { "1", "2.0", "2.1", "3" };
+ String input = "2";
+ String[] expectedCandidates = { "2.0", "2.1" };
+
+ VersionProperty versionProperty = new VersionProperty("", false, "version");
+ IdeContext context = IdeTestContextMock.get();
+ CompletionCandidateCollector collector = new CompletionCandidateCollectorDefault(context);
+
+ int matches = collector.addAllMatches(input, sortedCandidates, versionProperty, new VersionCommandlet(context));
+
+ assertThat(matches).isEqualTo(2);
+ assertThat(collector.getCandidates().stream().map(CompletionCandidate::text)).containsExactly(expectedCandidates);
+
+ }
+}
\ No newline at end of file
diff --git a/cli/src/test/java/com/devonfw/tools/ide/version/VersionIdentifierTest.java b/cli/src/test/java/com/devonfw/tools/ide/version/VersionIdentifierTest.java
index 20fa0ed8d..d25df1c58 100644
--- a/cli/src/test/java/com/devonfw/tools/ide/version/VersionIdentifierTest.java
+++ b/cli/src/test/java/com/devonfw/tools/ide/version/VersionIdentifierTest.java
@@ -249,4 +249,15 @@ public void testMatchAny() {
assertThat(pattern.matches(VersionIdentifier.of("17.0-SNAPSHOT"))).isTrue();
}
+ @Test
+ public void testCompareJavaVersions() {
+
+ VersionIdentifier v21_35 = VersionIdentifier.of("21_35");
+ VersionIdentifier v21_0_2_13 = VersionIdentifier.of("21.0.2_13");
+ VersionIdentifier v21_0_3_9 = VersionIdentifier.of("21.0.3_9");
+ assertThat(v21_35).isLessThan(v21_0_2_13);
+ assertThat(v21_0_2_13).isLessThan(v21_0_3_9);
+ assertThat(v21_0_3_9).isGreaterThan(v21_35);
+ }
+
}
diff --git a/gui/pom.xml b/gui/pom.xml
new file mode 100644
index 000000000..6cd3b2e15
--- /dev/null
+++ b/gui/pom.xml
@@ -0,0 +1,82 @@
+
+
+ 4.0.0
+
+ com.devonfw.tools.IDEasy.dev
+ ide
+ dev-SNAPSHOT
+ ../pom.xml
+
+ com.devonfw.tools.IDEasy
+ gui
+ ${revision}
+ gui
+
+
+ com.devonfw.ide.gui.AppLauncher
+ 21
+ 4.0.18
+ 0.0.8
+
+
+
+
+ ${project.groupId}
+ ide-cli
+ ${project.version}
+
+
+ org.openjfx
+ javafx-controls
+ ${javafx.version}
+
+
+ org.openjfx
+ javafx-fxml
+ ${javafx.version}
+
+
+ org.testfx
+ testfx-core
+ ${testfx.version}
+ test
+
+
+ org.testfx
+ testfx-junit5
+ ${testfx.version}
+ test
+
+
+ org.testfx
+ openjfx-monocle
+ 17.0.10
+ test
+
+
+
+
+
+
+ org.openjfx
+ javafx-maven-plugin
+ ${javafx.maven.plugin.version}
+
+
+
+ ${mainClass}
+ app
+ app
+ app
+ true
+ true
+ true
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gui/src/main/java/com/devonfw/ide/gui/App.java b/gui/src/main/java/com/devonfw/ide/gui/App.java
new file mode 100644
index 000000000..7e875256e
--- /dev/null
+++ b/gui/src/main/java/com/devonfw/ide/gui/App.java
@@ -0,0 +1,38 @@
+package com.devonfw.ide.gui;
+
+import com.devonfw.tools.ide.version.IdeVersion;
+import javafx.application.Application;
+import javafx.fxml.FXMLLoader;
+import javafx.geometry.Rectangle2D;
+import javafx.scene.Parent;
+import javafx.scene.Scene;
+import javafx.stage.Screen;
+import javafx.stage.Stage;
+
+import java.io.IOException;
+
+/**
+ * GUI Application for IDEasy
+ */
+public class App extends Application {
+
+ Parent root;
+
+ @Override
+ public void start(Stage primaryStage) throws IOException {
+
+ root = FXMLLoader.load(App.class.getResource("main-view.fxml"));
+
+ Rectangle2D bounds = Screen.getPrimary().getVisualBounds();
+ Scene scene = new Scene(root, bounds.getWidth() / 2, bounds.getHeight() / 2);
+
+ primaryStage.setTitle("IDEasy - version " + IdeVersion.get());
+ primaryStage.setScene(scene);
+ primaryStage.show();
+ }
+
+ public static void main(String[] args) {
+
+ launch(args);
+ }
+}
diff --git a/gui/src/main/java/com/devonfw/ide/gui/AppLauncher.java b/gui/src/main/java/com/devonfw/ide/gui/AppLauncher.java
new file mode 100644
index 000000000..3a3a78d6c
--- /dev/null
+++ b/gui/src/main/java/com/devonfw/ide/gui/AppLauncher.java
@@ -0,0 +1,15 @@
+package com.devonfw.ide.gui;
+
+/**
+ * Launcher class for the App.
+ * Workaround for "Error: JavaFX runtime components are missing, and are required to run this application."
+ * Inspired by
+ * StackOverflow
+ */
+public class AppLauncher {
+ public static void main(final String[] args) {
+
+ App.main(args);
+ }
+
+}
diff --git a/gui/src/main/resources/com/devonfw/ide/gui/main-view.fxml b/gui/src/main/resources/com/devonfw/ide/gui/main-view.fxml
new file mode 100644
index 000000000..ab0800c57
--- /dev/null
+++ b/gui/src/main/resources/com/devonfw/ide/gui/main-view.fxml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/gui/src/test/java/com/devonfw/ide/gui/AppBaseTest.java b/gui/src/test/java/com/devonfw/ide/gui/AppBaseTest.java
new file mode 100644
index 000000000..1de30246f
--- /dev/null
+++ b/gui/src/test/java/com/devonfw/ide/gui/AppBaseTest.java
@@ -0,0 +1,54 @@
+package com.devonfw.ide.gui;
+
+import javafx.stage.Stage;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.testfx.framework.junit5.ApplicationTest;
+import org.testfx.matcher.base.NodeMatchers;
+
+import java.io.IOException;
+
+import static org.testfx.api.FxAssert.verifyThat;
+
+/**
+ * Base Test
+ */
+public class AppBaseTest extends ApplicationTest {
+
+ @Override
+ public void start(Stage stage) throws IOException {
+
+ new App().start(stage);
+ }
+
+ /**
+ * Set up headless testing
+ *
+ * @throws IOException
+ */
+ @BeforeAll
+ public static void setupHeadlessMode() {
+
+ //Enable headless testing. Should be moved as a system property "-Dheadless=true" into workflows to only affect CI
+ System.setProperty("headless", "true");
+
+ if (Boolean.getBoolean("headless")) {
+ System.setProperty("testfx.robot", "glass");
+ System.setProperty("glass.platform", "Monocle");
+ System.setProperty("testfx.headless", "true");
+ System.setProperty("prism.order", "sw");
+ System.setProperty("java.awt.headless", "true");
+ }
+
+ }
+
+ /**
+ * Test if welcome message is shown when GUI is started
+ */
+ @Test
+ public void ensureHelloMessageIsShownOnStartUp() {
+
+ verifyThat("#hellomessage", NodeMatchers.isNotNull());
+ }
+
+}
diff --git a/pom.xml b/pom.xml
index cf21be915..217d4a590 100644
--- a/pom.xml
+++ b/pom.xml
@@ -19,6 +19,7 @@
IDEasy
${revision}
+ 17
@@ -51,6 +52,7 @@
documentation
cli
+ gui