From 6954a34c9a2701e9583b5640f95487d170b737af Mon Sep 17 00:00:00 2001 From: jan-vcapgemini <59438728+jan-vcapgemini@users.noreply.github.com> Date: Thu, 11 Jan 2024 11:23:36 +0100 Subject: [PATCH 1/5] #175: fixed NPE for missing version (#176) --- .../java/com/devonfw/tools/ide/url/model/UrlMetadata.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/model/UrlMetadata.java b/cli/src/main/java/com/devonfw/tools/ide/url/model/UrlMetadata.java index 183cf300e..1597c7ec2 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/url/model/UrlMetadata.java +++ b/cli/src/main/java/com/devonfw/tools/ide/url/model/UrlMetadata.java @@ -115,7 +115,10 @@ public UrlVersion getVersionFolder(String tool, String edition, VersionIdentifie VersionIdentifier resolvedVersion = getVersion(tool, edition, version); UrlVersion urlVersion = getEdition(tool, edition).getChild(resolvedVersion.toString()); - Objects.requireNonNull(urlVersion); + if (urlVersion == null) { + throw new IllegalArgumentException( + "Version " + version + " for tool " + tool + " does not exist in edition " + edition + "."); + } return urlVersion; } From 79f4e767f33bbb952f506b1daa31d7aacbb793f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Hohwiller?= Date: Tue, 16 Jan 2024 12:36:59 +0100 Subject: [PATCH 2/5] #158: improved VersionRange (#170) --- .../tools/ide/version/BoundaryType.java | 64 +++++- .../tools/ide/version/VersionRange.java | 187 ++++++------------ .../tools/ide/version/VersionRangeTest.java | 144 +++++++------- 3 files changed, 201 insertions(+), 194 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/version/BoundaryType.java b/cli/src/main/java/com/devonfw/tools/ide/version/BoundaryType.java index c8abed59a..359391632 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/version/BoundaryType.java +++ b/cli/src/main/java/com/devonfw/tools/ide/version/BoundaryType.java @@ -15,5 +15,67 @@ public enum BoundaryType { LEFT_OPEN, /** Right open interval - includes the lower bound but excludes the upper bound. */ - RIGHT_OPEN + RIGHT_OPEN; + + static final String START_EXCLUDING_PREFIX = "("; + + static final String START_INCLUDING_PREFIX = "["; + + static final String END_EXCLUDING_SUFFIX = ")"; + + static final String END_INCLUDING_SUFFIX = "]"; + + /** + * @return {@code true} if left exclusive, {@code false} otherwise (left inclusive). + */ + public boolean isLeftExclusive() { + + return (this == LEFT_OPEN) || (this == OPEN); + } + + /** + * @return {@code true} if right exclusive, {@code false} otherwise (right inclusive). + */ + public boolean isRightExclusive() { + + return (this == RIGHT_OPEN) || (this == OPEN); + } + + /** + * @return the prefix (left parenthesis or bracket for {@link #isLeftExclusive()}). + */ + public String getPrefix() { + + return isLeftExclusive() ? START_EXCLUDING_PREFIX : START_INCLUDING_PREFIX; + } + + /** + * @return the suffix (right parenthesis or bracket for {@link #isRightExclusive()}). + */ + public String getSuffix() { + + return isRightExclusive() ? END_EXCLUDING_SUFFIX : END_INCLUDING_SUFFIX; + } + + /** + * @param leftExclusive the {@link #isLeftExclusive() left exclusive flag}. + * @param rightExclusive the {@link #isRightExclusive() right exclusive flag}. + * @return the {@link BoundaryType} with the specified values. + */ + public static BoundaryType of(boolean leftExclusive, boolean rightExclusive) { + + if (leftExclusive) { + if (rightExclusive) { + return OPEN; + } else { + return LEFT_OPEN; + } + } else { + if (rightExclusive) { + return RIGHT_OPEN; + } else { + return CLOSED; + } + } + } } diff --git a/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java b/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java index df27e3612..cf086f55b 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java +++ b/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java @@ -1,5 +1,7 @@ package com.devonfw.tools.ide.version; +import java.util.Objects; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; @@ -14,44 +16,9 @@ public final class VersionRange implements Comparable { private final VersionIdentifier max; - private final boolean leftIsExclusive; - - private final boolean rightIsExclusive; - - private static final String VERSION_SEPARATOR = ">"; - - private static final String START_EXCLUDING_PREFIX = "("; - - private static final String START_INCLUDING_PREFIX = "["; - - private static final String END_EXCLUDING_SUFFIX = ")"; - - private static final String END_INCLUDING_SUFFIX = "]"; - - public static String getVersionSeparator() { - - return VERSION_SEPARATOR; - } - - public static String getStartExcludingPrefix() { - - return START_EXCLUDING_PREFIX; - } - - public static String getStartIncludingPrefix() { - - return START_INCLUDING_PREFIX; - } - - public static String getEndExcludingSuffix() { - - return END_EXCLUDING_SUFFIX; - } - - public static String getEndIncludingSuffix() { + private final BoundaryType boundaryType; - return END_INCLUDING_SUFFIX; - } + private static final String VERSION_SEPARATOR = ","; /** * The constructor. @@ -61,11 +28,7 @@ public static String getEndIncludingSuffix() { */ public VersionRange(VersionIdentifier min, VersionIdentifier max) { - super(); - this.min = min; - this.max = max; - this.leftIsExclusive = false; - this.rightIsExclusive = false; + this(min, max, BoundaryType.CLOSED); } /** @@ -79,27 +42,18 @@ public VersionRange(VersionIdentifier min, VersionIdentifier max) { public VersionRange(VersionIdentifier min, VersionIdentifier max, BoundaryType boundaryType) { super(); + Objects.requireNonNull(boundaryType); this.min = min; this.max = max; - this.leftIsExclusive = BoundaryType.LEFT_OPEN.equals(boundaryType) || BoundaryType.OPEN.equals(boundaryType); - this.rightIsExclusive = BoundaryType.RIGHT_OPEN.equals(boundaryType) || BoundaryType.OPEN.equals(boundaryType); - } - - /** - * The constructor. - * - * @param min the {@link #getMin() minimum}. - * @param max the {@link #getMax() maximum}. - * @param leftIsExclusive - {@code true} if the {@link #getMin() minimum} is exclusive, {@code false} otherwise. - * @param rightIsExclusive - {@code true} if the {@link #getMax() maximum} is exclusive, {@code false} otherwise. - */ - public VersionRange(VersionIdentifier min, VersionIdentifier max, boolean leftIsExclusive, boolean rightIsExclusive) { + this.boundaryType = boundaryType; + if ((min != null) && (max != null) && min.isGreater(max)) { + throw new IllegalArgumentException(toString()); + } else if ((min == null) && !boundaryType.isLeftExclusive()) { + throw new IllegalArgumentException(toString()); + } else if ((max == null) && !boundaryType.isRightExclusive()) { + throw new IllegalArgumentException(toString()); + } - super(); - this.min = min; - this.max = max; - this.leftIsExclusive = leftIsExclusive; - this.rightIsExclusive = rightIsExclusive; } /** @@ -118,36 +72,12 @@ public VersionIdentifier getMax() { return this.max; } - /** - * @return {@code true} if the {@link #getMin() minimum} is exclusive, {@code false} otherwise. - */ - public boolean isLeftExclusive() { - - return this.leftIsExclusive; - } - - /** - * @return {@code true} if the {@link #getMax() maximum} is exclusive, {@code false} otherwise. - */ - public boolean isRightExclusive() { - - return this.rightIsExclusive; - } - /** * @return the {@link BoundaryType} defining whether the boundaries of the range are inclusive or exclusive. */ public BoundaryType getBoundaryType() { - if (this.leftIsExclusive && this.rightIsExclusive) { - return BoundaryType.OPEN; - } else if (this.leftIsExclusive) { - return BoundaryType.LEFT_OPEN; - } else if (this.rightIsExclusive) { - return BoundaryType.RIGHT_OPEN; - } else { - return BoundaryType.CLOSED; - } + return this.boundaryType; } /** @@ -161,7 +91,7 @@ public boolean contains(VersionIdentifier version) { VersionComparisonResult compareMin = version.compareVersion(this.min); if (compareMin.isLess()) { return false; - } else if (compareMin.isEqual() && this.leftIsExclusive) { + } else if (compareMin.isEqual() && this.boundaryType.isLeftExclusive()) { return false; } } @@ -169,7 +99,7 @@ public boolean contains(VersionIdentifier version) { VersionComparisonResult compareMax = version.compareVersion(this.max); if (compareMax.isGreater()) { return false; - } else if (compareMax.isEqual() && this.rightIsExclusive) { + } else if (compareMax.isEqual() && this.boundaryType.isRightExclusive()) { return false; } } @@ -189,7 +119,8 @@ public int compareTo(VersionRange o) { } int compareMins = this.min.compareTo(o.min); if (compareMins == 0) { - return this.leftIsExclusive == o.leftIsExclusive ? 0 : this.leftIsExclusive ? 1 : -1; + return this.boundaryType.isLeftExclusive() == o.boundaryType.isLeftExclusive() ? 0 + : this.boundaryType.isLeftExclusive() ? 1 : -1; } else { return compareMins; } @@ -204,15 +135,14 @@ public boolean equals(Object obj) { return false; } VersionRange o = (VersionRange) obj; - if (this.min == null && this.max == null) { - return o.min == null && o.max == null; - } else if (this.min == null) { - return o.min == null && this.max.equals(o.max) && this.rightIsExclusive == o.rightIsExclusive; - } else if (this.max == null) { - return this.min.equals(o.min) && o.max == null && this.leftIsExclusive == o.leftIsExclusive; + if (this.boundaryType != o.boundaryType) { + return false; + } else if (!Objects.equals(this.min, o.min)) { + return false; + } else if (!Objects.equals(this.max, o.max)) { + return false; } - return this.min.equals(o.min) && this.leftIsExclusive == o.leftIsExclusive && this.max.equals(o.max) - && this.rightIsExclusive == o.rightIsExclusive; + return true; } @Override @@ -220,7 +150,7 @@ public boolean equals(Object obj) { public String toString() { StringBuilder sb = new StringBuilder(); - sb.append(this.leftIsExclusive ? START_EXCLUDING_PREFIX : START_INCLUDING_PREFIX); + sb.append(this.boundaryType.getPrefix()); if (this.min != null) { sb.append(this.min); } @@ -228,7 +158,7 @@ public String toString() { if (this.max != null) { sb.append(this.max); } - sb.append(this.rightIsExclusive ? END_EXCLUDING_SUFFIX : END_INCLUDING_SUFFIX); + sb.append(this.boundaryType.getSuffix()); return sb.toString(); } @@ -239,40 +169,45 @@ public String toString() { @JsonCreator public static VersionRange of(String value) { - boolean leftIsExclusive = value.startsWith(START_EXCLUDING_PREFIX); - boolean rightIsExclusive = value.endsWith(END_EXCLUDING_SUFFIX); - value = removeAffixes(value); - - int index = value.indexOf(VERSION_SEPARATOR); - if (index == -1) { - return null; // log warning? + Boolean isleftExclusive = null; + Boolean isRightExclusive = null; + if (value.startsWith(BoundaryType.START_EXCLUDING_PREFIX)) { + isleftExclusive = Boolean.TRUE; + value = value.substring(BoundaryType.START_EXCLUDING_PREFIX.length()); + } else if (value.startsWith(BoundaryType.START_INCLUDING_PREFIX)) { + isleftExclusive = Boolean.FALSE; + value = value.substring(BoundaryType.START_INCLUDING_PREFIX.length()); } - - VersionIdentifier min = null; - if (index > 0) { - min = VersionIdentifier.of(value.substring(0, index)); + if (value.endsWith(BoundaryType.END_EXCLUDING_SUFFIX)) { + isRightExclusive = Boolean.TRUE; + value = value.substring(0, value.length() - BoundaryType.END_EXCLUDING_SUFFIX.length()); + } else if (value.endsWith(BoundaryType.END_INCLUDING_SUFFIX)) { + isRightExclusive = Boolean.FALSE; + value = value.substring(0, value.length() - BoundaryType.END_INCLUDING_SUFFIX.length()); } + VersionIdentifier min = null; VersionIdentifier max = null; - String maxString = value.substring(index + 1); - if (!maxString.isEmpty()) { - max = VersionIdentifier.of(maxString); + int index = value.indexOf(VERSION_SEPARATOR); + if (index < 0) { + min = VersionIdentifier.of(value); + max = min; + } else { + String minString = value.substring(0, index); + if (!minString.isEmpty()) { + min = VersionIdentifier.of(minString); + } + String maxString = value.substring(index + 1); + if (!maxString.isEmpty()) { + max = VersionIdentifier.of(maxString); + } } - return new VersionRange(min, max, leftIsExclusive, rightIsExclusive); - } - - private static String removeAffixes(String value) { - - if (value.startsWith(START_EXCLUDING_PREFIX)) { - value = value.substring(START_EXCLUDING_PREFIX.length()); - } else if (value.startsWith(START_INCLUDING_PREFIX)) { - value = value.substring(START_INCLUDING_PREFIX.length()); + if (isleftExclusive == null) { + isleftExclusive = Boolean.valueOf(min == null); } - if (value.endsWith(END_EXCLUDING_SUFFIX)) { - value = value.substring(0, value.length() - END_EXCLUDING_SUFFIX.length()); - } else if (value.endsWith(END_INCLUDING_SUFFIX)) { - value = value.substring(0, value.length() - END_EXCLUDING_SUFFIX.length()); + if (isRightExclusive == null) { + isRightExclusive = Boolean.valueOf(max == null); } - return value; + return new VersionRange(min, max, BoundaryType.of(isleftExclusive.booleanValue(), isRightExclusive.booleanValue())); } } diff --git a/cli/src/test/java/com/devonfw/tools/ide/version/VersionRangeTest.java b/cli/src/test/java/com/devonfw/tools/ide/version/VersionRangeTest.java index 0a305f8a6..8d8ffe05c 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/version/VersionRangeTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/version/VersionRangeTest.java @@ -8,16 +8,14 @@ */ public class VersionRangeTest extends Assertions { - /** - * Test of {@link VersionRange#of(String)}. - */ + /** Test of {@link VersionRange#of(String)}. */ @Test public void testOf() { // arrange - String v1String = "1.2>3"; - String v2String = "1>)"; - String v3String = "(1.2>3.4]"; + String v1String = "1.2,3"; + String v2String = "1,)"; + String v3String = "(1.2,3.4]"; // act VersionRange v1 = VersionRange.of(v1String); @@ -28,60 +26,56 @@ public void testOf() { // v1 assertThat(v1.getMin()).isEqualTo(VersionIdentifier.of("1.2")); assertThat(v1.getMax()).isEqualTo(VersionIdentifier.of("3")); - assertThat(v1.isLeftExclusive()).isFalse(); - assertThat(v1.isRightExclusive()).isFalse(); + assertThat(v1.getBoundaryType().isLeftExclusive()).isFalse(); + assertThat(v1.getBoundaryType().isRightExclusive()).isFalse(); // v2 assertThat(v2.getMin()).isEqualTo(VersionIdentifier.of("1")); assertThat(v2.getMax()).isEqualTo(null); - assertThat(v2.isLeftExclusive()).isFalse(); - assertThat(v2.isRightExclusive()).isTrue(); + assertThat(v2.getBoundaryType().isLeftExclusive()).isFalse(); + assertThat(v2.getBoundaryType().isRightExclusive()).isTrue(); // v3 assertThat(v3.getMin()).isEqualTo(VersionIdentifier.of("1.2")); assertThat(v3.getMax()).isEqualTo(VersionIdentifier.of("3.4")); - assertThat(v3.isLeftExclusive()).isTrue(); - assertThat(v3.isRightExclusive()).isFalse(); + assertThat(v3.getBoundaryType().isLeftExclusive()).isTrue(); + assertThat(v3.getBoundaryType().isRightExclusive()).isFalse(); } - /** - * Test of {@link VersionRange#toString()}. - */ + /** Test of {@link VersionRange#toString()}. */ @Test public void testToString() { - assertThat(VersionRange.of("1.2>3").toString()).isEqualTo("[1.2>3]"); - assertThat(VersionRange.of("1>)").toString()).isEqualTo("[1>)"); - assertThat(VersionRange.of("(1.2>3.4]").toString()).isEqualTo("(1.2>3.4]"); + assertThat(VersionRange.of("1.2,3").toString()).isEqualTo("[1.2,3]"); + assertThat(VersionRange.of("1,)").toString()).isEqualTo("[1,)"); + assertThat(VersionRange.of("(1.2,3.4]").toString()).isEqualTo("(1.2,3.4]"); + assertThat(VersionRange.of(",").toString()).isEqualTo("(,)"); } - /** - * Test of {@link VersionRange#equals(Object)}. - */ + /** Test of {@link VersionRange#equals(Object)}. */ @Test public void testEquals() { // assert // equals - assertThat(VersionRange.of("1.2>")).isEqualTo(VersionRange.of("1.2>")); - assertThat(VersionRange.of("(1.2>")).isEqualTo(VersionRange.of("(1.2>)")); - assertThat(VersionRange.of("1.2>3")).isEqualTo(VersionRange.of("1.2>3")); - assertThat(VersionRange.of("[1.2>3")).isEqualTo(VersionRange.of("1.2>3]")); - assertThat(VersionRange.of(">3)")).isEqualTo(VersionRange.of(">3)")); - assertThat(VersionRange.of(">")).isEqualTo(VersionRange.of(">")); - assertThat(VersionRange.of("[>)")).isEqualTo(VersionRange.of("(>]")); - assertThat(VersionRange.of("8u302b08>11.0.14_9")).isEqualTo(VersionRange.of("8u302b08>11.0.14_9")); + assertThat(VersionRange.of("1.2,")).isEqualTo(VersionRange.of("1.2,")); + assertThat(VersionRange.of("(1.2,")).isEqualTo(VersionRange.of("(1.2,)")); + assertThat(VersionRange.of("1.2,3")).isEqualTo(VersionRange.of("1.2,3")); + assertThat(VersionRange.of("[1.2,3")).isEqualTo(VersionRange.of("1.2,3]")); + assertThat(VersionRange.of(",3)")).isEqualTo(VersionRange.of(",3)")); + assertThat(VersionRange.of(",")).isEqualTo(VersionRange.of("(,)")); + assertThat(VersionRange.of("8u302b08,11.0.14_9")).isEqualTo(VersionRange.of("8u302b08,11.0.14_9")); // not equals - assertThat(VersionRange.of("1>")).isNotEqualTo(null); - assertThat(VersionRange.of("1.2>")).isNotEqualTo(VersionRange.of("1>")); - assertThat(VersionRange.of("1.2>3")).isNotEqualTo(VersionRange.of("1.2>")); - assertThat(VersionRange.of("(1.2>3")).isNotEqualTo(VersionRange.of("1.2.3>")); - assertThat(VersionRange.of("1.2>3")).isNotEqualTo(VersionRange.of(">3")); - assertThat(VersionRange.of("[1.2>")).isNotEqualTo(VersionRange.of("[1.2>3")); - assertThat(VersionRange.of(">3")).isNotEqualTo(VersionRange.of("1.2>3")); - assertThat(VersionRange.of(">3")).isNotEqualTo(VersionRange.of(">")); - assertThat(VersionRange.of(">")).isNotEqualTo(VersionRange.of(">3")); - assertThat(VersionRange.of("8u302b08>11.0.14_9")).isNotEqualTo(VersionRange.of("(8u302b08>11.0.14_9)")); - assertThat(VersionRange.of("8u302b08>11.0.14_9")).isNotEqualTo(VersionRange.of("8u302b08>11.0.15_9")); - assertThat(VersionRange.of("8u302b08>11.0.14_9")).isNotEqualTo(VersionRange.of("8u302b08>11.0.14_0")); + assertThat(VersionRange.of("1,")).isNotEqualTo(null); + assertThat(VersionRange.of("1.2,")).isNotEqualTo(VersionRange.of("1,")); + assertThat(VersionRange.of("1.2,3")).isNotEqualTo(VersionRange.of("1.2,")); + assertThat(VersionRange.of("(1.2,3")).isNotEqualTo(VersionRange.of("1.2.3,")); + assertThat(VersionRange.of("1.2,3")).isNotEqualTo(VersionRange.of(",3")); + assertThat(VersionRange.of("[1.2,")).isNotEqualTo(VersionRange.of("[1.2,3")); + assertThat(VersionRange.of(",3")).isNotEqualTo(VersionRange.of("1.2,3")); + assertThat(VersionRange.of(",3")).isNotEqualTo(VersionRange.of(",")); + assertThat(VersionRange.of(",")).isNotEqualTo(VersionRange.of(",3")); + assertThat(VersionRange.of("8u302b08,11.0.14_9")).isNotEqualTo(VersionRange.of("(8u302b08,11.0.14_9)")); + assertThat(VersionRange.of("8u302b08,11.0.14_9")).isNotEqualTo(VersionRange.of("8u302b08,11.0.15_9")); + assertThat(VersionRange.of("8u302b08,11.0.14_9")).isNotEqualTo(VersionRange.of("8u302b08,11.0.14_0")); } /** @@ -92,13 +86,13 @@ public void testEquals() { public void testContains() { // assert - assertThat(VersionRange.of("1.2>3.4").contains(VersionIdentifier.of("1.2"))).isTrue(); - assertThat(VersionRange.of("1.2>3.4").contains(VersionIdentifier.of("2"))).isTrue(); - assertThat(VersionRange.of("1.2>3.4").contains(VersionIdentifier.of("3.4"))).isTrue(); + assertThat(VersionRange.of("1.2,3.4").contains(VersionIdentifier.of("1.2"))).isTrue(); + assertThat(VersionRange.of("1.2,3.4").contains(VersionIdentifier.of("2"))).isTrue(); + assertThat(VersionRange.of("1.2,3.4").contains(VersionIdentifier.of("3.4"))).isTrue(); - assertThat(VersionRange.of("(1.2>3.4)").contains(VersionIdentifier.of("1.2.1"))).isTrue(); - assertThat(VersionRange.of("(1.2>3.4)").contains(VersionIdentifier.of("2"))).isTrue(); - assertThat(VersionRange.of("(1.2>3.4)").contains(VersionIdentifier.of("3.3.9"))).isTrue(); + assertThat(VersionRange.of("(1.2,3.4)").contains(VersionIdentifier.of("1.2.1"))).isTrue(); + assertThat(VersionRange.of("(1.2,3.4)").contains(VersionIdentifier.of("2"))).isTrue(); + assertThat(VersionRange.of("(1.2,3.4)").contains(VersionIdentifier.of("3.3.9"))).isTrue(); } /** @@ -109,44 +103,60 @@ public void testContains() { public void testNotContains() { // assert - assertThat(VersionRange.of("1.2>3.4").contains(VersionIdentifier.of("1.1"))).isFalse(); - assertThat(VersionRange.of("1.2>3.4").contains(VersionIdentifier.of("3.4.1"))).isFalse(); + assertThat(VersionRange.of("1.2,3.4").contains(VersionIdentifier.of("1.1"))).isFalse(); + assertThat(VersionRange.of("1.2,3.4").contains(VersionIdentifier.of("3.4.1"))).isFalse(); - assertThat(VersionRange.of("(1.2>3.4)").contains(VersionIdentifier.of("1.2"))).isFalse(); - assertThat(VersionRange.of("(1.2>3.4)").contains(VersionIdentifier.of("3.4"))).isFalse(); + assertThat(VersionRange.of("(1.2,3.4)").contains(VersionIdentifier.of("1.2"))).isFalse(); + assertThat(VersionRange.of("(1.2,3.4)").contains(VersionIdentifier.of("3.4"))).isFalse(); } - /** - * Test of {@link VersionRange#compareTo(VersionRange)} and testing if versions are compared to be the same. - */ + /** Test of {@link VersionRange#compareTo(VersionRange)} and testing if versions are compared to be the same. */ @Test public void testCompareToIsSame() { // assert - assertThat(VersionRange.of("1.2>3").compareTo(VersionRange.of("1.2>3"))).isEqualTo(0); - assertThat(VersionRange.of("(1.2>3").compareTo(VersionRange.of("(1.2>3"))).isEqualTo(0); - assertThat(VersionRange.of("[1.2>3]").compareTo(VersionRange.of("[1.2>4)"))).isEqualTo(0); + assertThat(VersionRange.of("1.2,3").compareTo(VersionRange.of("1.2,3"))).isEqualTo(0); + assertThat(VersionRange.of("(1.2,3").compareTo(VersionRange.of("(1.2,3"))).isEqualTo(0); + assertThat(VersionRange.of("[1.2,3]").compareTo(VersionRange.of("[1.2,4)"))).isEqualTo(0); } - /** - * Test of {@link VersionRange#compareTo(VersionRange)} and testing if first version is smaller than second. - */ + /** Test of {@link VersionRange#compareTo(VersionRange)} and testing if first version is smaller than second. */ @Test public void testCompareToIsSmaller() { // assert - assertThat(VersionRange.of("1.1.2>3").compareTo(VersionRange.of("1.2>3"))).isEqualTo(-1); - assertThat(VersionRange.of("[1.2>3").compareTo(VersionRange.of("(1.2>4"))).isEqualTo(-1); + assertThat(VersionRange.of("1.1.2,3").compareTo(VersionRange.of("1.2,3"))).isEqualTo(-1); + assertThat(VersionRange.of("[1.2,3").compareTo(VersionRange.of("(1.2,4"))).isEqualTo(-1); } - /** - * Test of {@link VersionRange#compareTo(VersionRange)} and testing if first version is larger than second. - */ + /** Test of {@link VersionRange#compareTo(VersionRange)} and testing if first version is larger than second. */ @Test public void testCompareToIsLarger() { // assert - assertThat(VersionRange.of("1.2.1>3").compareTo(VersionRange.of("1.2>3"))).isEqualTo(1); - assertThat(VersionRange.of("(1.2>3").compareTo(VersionRange.of("1.2>4"))).isEqualTo(1); + assertThat(VersionRange.of("1.2.1,3").compareTo(VersionRange.of("1.2,3"))).isEqualTo(1); + assertThat(VersionRange.of("(1.2,3").compareTo(VersionRange.of("1.2,4"))).isEqualTo(1); + } + + /** Test of {@link VersionRange#of(String)} with illegal syntax. */ + @Test + public void testIllegalSyntax() { + + checkIllegalRange("[,)"); + checkIllegalRange("(,]"); + checkIllegalRange("[,]"); + checkIllegalRange("[,1.0)"); + checkIllegalRange("(1.0,]"); + checkIllegalRange("(1.1,1.0)"); + } + + private void checkIllegalRange(String range) { + + try { + VersionRange.of(range); + failBecauseExceptionWasNotThrown(IllegalArgumentException.class); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage()).isEqualTo(range); + } } } From 48e9dada9c4e49f322078002238b628caa57fffb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Hohwiller?= Date: Tue, 16 Jan 2024 12:43:37 +0100 Subject: [PATCH 3/5] #6: fix git hangs via non interactive invocation (#174) --- .../tools/ide/context/AbstractIdeContext.java | 2 +- .../tools/ide/process/ProcessContext.java | 13 +++++++++++++ .../tools/ide/process/ProcessContextImpl.java | 19 +++++++++++++------ .../tools/ide/version/VersionSegment.java | 1 - 4 files changed, 27 insertions(+), 8 deletions(-) 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 8bda33623..d9945a70a 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 @@ -601,7 +601,7 @@ public void gitPullOrClone(Path target, String gitRepoUrl) { if (!gitRepoUrl.startsWith("http")) { throw new IllegalArgumentException("Invalid git URL '" + gitRepoUrl + "'!"); } - ProcessContext pc = newProcess().directory(target).executable("git"); + ProcessContext pc = newProcess().directory(target).executable("git").withEnvVar("GIT_TERMINAL_PROMPT", "0"); if (Files.isDirectory(target.resolve(".git"))) { ProcessResult result = pc.addArg("remote").run(true); List remotes = result.getOut(); diff --git a/cli/src/main/java/com/devonfw/tools/ide/process/ProcessContext.java b/cli/src/main/java/com/devonfw/tools/ide/process/ProcessContext.java index 85eb99632..bcab1cfb9 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/process/ProcessContext.java +++ b/cli/src/main/java/com/devonfw/tools/ide/process/ProcessContext.java @@ -111,6 +111,19 @@ default ProcessContext addArgs(List... args) { return this; } + /** + * Sets or overrides the specified environment variable only for the planned {@link #run() process execution}. Please + * note that the environment variables are initialized when the {@link ProcessContext} is created. This method + * explicitly set an additional or overrides an existing environment and will have effect for each {@link #run() + * process execution} invoked from this {@link ProcessContext} instance. Be aware of such side-effects when reusing + * the same {@link ProcessContext} to {@link #run() run} multiple commands. + * + * @param key the name of the environment variable (E.g. "PATH"). + * @param value the value of the environment variable. + * @return this {@link ProcessContext} for fluent API calls. + */ + ProcessContext withEnvVar(String key, String value); + /** * Runs the previously configured {@link #executable(Path) command} with the configured {@link #addArgs(String...) * arguments}. Will reset the {@link #addArgs(String...) arguments} but not the {@link #executable(Path) command} for diff --git a/cli/src/main/java/com/devonfw/tools/ide/process/ProcessContextImpl.java b/cli/src/main/java/com/devonfw/tools/ide/process/ProcessContextImpl.java index 596f9c316..21f6a9a0e 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/process/ProcessContextImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/process/ProcessContextImpl.java @@ -1,9 +1,9 @@ package com.devonfw.tools.ide.process; import java.io.BufferedReader; +import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.io.IOException; import java.lang.ProcessBuilder.Redirect; import java.nio.file.Files; import java.nio.file.Path; @@ -89,6 +89,13 @@ public ProcessContext addArg(String arg) { return this; } + @Override + public ProcessContext withEnvVar(String key, String value) { + + this.processBuilder.environment().put(key, value); + return this; + } + @Override public ProcessResult run(boolean capture) { @@ -195,7 +202,7 @@ private String createCommandMessage(String suffix) { String message = sb.toString(); return message; } - + private boolean hasSheBang(Path file) { try (InputStream in = Files.newInputStream(file)) { @@ -211,6 +218,7 @@ private boolean hasSheBang(Path file) { } private String findBashOnWindows() { + // Check if Git Bash exists in the default location Path defaultPath = Paths.get("C:\\Program Files\\Git\\bin\\bash.exe"); if (Files.exists(defaultPath)) { @@ -218,8 +226,8 @@ private String findBashOnWindows() { } // If not found in the default location, try the registry query - String[] bashVariants = {"GitForWindows", "Cygwin\\setup"}; - String[] registryKeys = {"HKEY_LOCAL_MACHINE","HKEY_CURRENT_USER"}; + String[] bashVariants = { "GitForWindows", "Cygwin\\setup" }; + String[] registryKeys = { "HKEY_LOCAL_MACHINE", "HKEY_CURRENT_USER" }; String regQueryResult; for (String bashVariant : bashVariants) { for (String registryKey : registryKeys) { @@ -256,9 +264,8 @@ private String findBashOnWindows() { } } } - //no bash found + // no bash found throw new IllegalStateException("Could not find Bash. Please install Git for Windows and rerun."); } - } 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 1bb3d01f0..0c84b68e2 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 @@ -33,7 +33,6 @@ public class VersionSegment implements VersionObject { * @param separator the {@link #getSeparator() separator}. * @param letters the {@link #getLettersString() letters}. * @param digits the {@link #getDigits() digits}. - * @param pattern the {@link #getPattern() pattern}. */ VersionSegment(String separator, String letters, String digits) { From f75bfa6e38ae77ba02238703754dae974e86cca2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Hohwiller?= Date: Tue, 16 Jan 2024 13:33:33 +0100 Subject: [PATCH 4/5] #7: add Tag class with predefined tags (#166) --- .../com/devonfw/tools/ide/common/Tag.java | 564 ++++++++++++++++++ .../com/devonfw/tools/ide/common/Tags.java | 59 +- .../tools/ide/tool/GlobalToolCommandlet.java | 3 +- .../tools/ide/tool/LocalToolCommandlet.java | 16 +- .../tools/ide/tool/ToolCommandlet.java | 7 +- .../com/devonfw/tools/ide/tool/az/Azure.java | 5 +- .../tools/ide/tool/eclipse/Eclipse.java | 3 +- .../com/devonfw/tools/ide/tool/gh/Gh.java | 3 +- .../devonfw/tools/ide/tool/gradle/Gradle.java | 3 +- .../com/devonfw/tools/ide/tool/helm/Helm.java | 7 +- .../tools/ide/tool/ide/IdeToolCommandlet.java | 15 +- .../ide/tool/ide/PluginDescriptorImpl.java | 15 +- .../tools/ide/tool/intellij/Intellij.java | 3 +- .../com/devonfw/tools/ide/tool/java/Java.java | 3 +- .../tools/ide/tool/kotlinc/Kotlinc.java | 6 +- .../tools/ide/tool/kotlinc/KotlincNative.java | 6 +- .../com/devonfw/tools/ide/tool/mvn/Mvn.java | 3 +- .../com/devonfw/tools/ide/tool/node/Node.java | 3 +- .../com/devonfw/tools/ide/tool/oc/Oc.java | 7 +- .../tools/ide/tool/quarkus/Quarkus.java | 7 +- .../tools/ide/tool/terraform/Terraform.java | 3 +- .../devonfw/tools/ide/tool/vscode/Vscode.java | 5 +- .../com/devonfw/tools/ide/common/TagTest.java | 151 +++++ 23 files changed, 793 insertions(+), 104 deletions(-) create mode 100644 cli/src/main/java/com/devonfw/tools/ide/common/Tag.java create mode 100644 cli/src/test/java/com/devonfw/tools/ide/common/TagTest.java diff --git a/cli/src/main/java/com/devonfw/tools/ide/common/Tag.java b/cli/src/main/java/com/devonfw/tools/ide/common/Tag.java new file mode 100644 index 000000000..d7ef85640 --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/common/Tag.java @@ -0,0 +1,564 @@ +package com.devonfw.tools.ide.common; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +/** + * A {@link Tag} represents a classifier or category. A tool and plugin can be associated with {@link Tag}s allowing end + * users to find them. + */ +public final class Tag { + + private static final Tag[] NO_TAGS = new Tag[0]; + + private static final Map TAG_MAP = new HashMap<>(128); + + private static final Collection ALL_TAGS = Collections.unmodifiableCollection(TAG_MAP.values()); + + /** The root {@link Tag}. */ + public static final Tag ROOT = new Tag(); + + static { + TAG_MAP.put(ROOT.id, ROOT); + } + + /** {@link #getParent() Parent} for miscellaneous (undefined) tags. */ + public static final Tag MISC = create("miscellaneous", ROOT, true, "misc"); + + /** {@link #getParent() Parent} for programming-languages. */ + public static final Tag LANGUAGE = create("language", ROOT, true, "programming"); + + /** {@link Tag} for JVM (Java Virtual Machine). */ + public static final Tag JVM = create("java-virtual-machine", LANGUAGE, false, "jvm"); + + /** {@link Tag} for Java. */ + public static final Tag JAVA = create("java", JVM); + + /** {@link Tag} for Kotlin. */ + public static final Tag KOTLIN = create("kotlin", JVM); + + /** {@link Tag} for Scala. */ + public static final Tag SCALA = create("scala", JVM); + + /** {@link Tag} for DotNet (.NET). */ + public static final Tag DOTNET = create("dotnet", LANGUAGE, false, "net"); + + /** {@link Tag} for C#. */ + public static final Tag CS = create("c#", DOTNET, false, "cs", "csharp"); + + /** {@link Tag} for C. */ + public static final Tag C = create("c", LANGUAGE); + + /** {@link Tag} for Rust. */ + public static final Tag RUST = create("rust", LANGUAGE, false, "rs"); + + /** {@link Tag} for C++. */ + public static final Tag CPP = create("c++", LANGUAGE, false, "cpp"); + + /** {@link Tag} for Python. */ + public static final Tag PYTHON = create("python", LANGUAGE); + + /** {@link Tag} for Ruby. */ + public static final Tag RUBY = create("ruby", LANGUAGE); + + /** {@link Tag} for Perl. */ + public static final Tag PERL = create("perl", LANGUAGE); + + /** {@link Tag} for Shell scripting. */ + public static final Tag SHELL = create("shell", JVM, false, "script"); + + /** {@link Tag} for Bash. */ + public static final Tag BASH = create("bash", SHELL, false, "terminal"); + + /** {@link Tag} for TypeScript. */ + public static final Tag JAVA_SCRIPT = create("javascript", LANGUAGE, false, "js"); + + /** {@link Tag} for TypeScript. */ + public static final Tag TYPE_SCRIPT = create("typescript", JAVA_SCRIPT, false, "ts"); + + /** {@link #getParent() Parent} for programming-languages. */ + public static final Tag IDE = create("ide", ROOT); + + /** {@link Tag} for Eclipse. */ + public static final Tag ECLIPSE = create("eclipse", IDE); + + /** {@link Tag} for IDEA (JetBrains IDE Platform). */ + public static final Tag IDEA = create("idea", IDE); + + /** {@link Tag} for IntelliJ. */ + public static final Tag INTELLIJ = create("intellij", IDEA); + + /** {@link Tag} for IntelliJ. */ + public static final Tag ANDROID_STUDIO = create("android-studio", IDEA); + + /** {@link Tag} for VS-Code. */ + public static final Tag VS_CODE = create("vscode", IDE, false, "visualstudiocode"); + + /** {@link Tag} for (code-)generators (including template-engines, etc.). */ + public static final Tag GENERATOR = create("generator", ROOT); + + /** {@link #getParent() Parent} for frameworks. */ + public static final Tag FRAMEWORK = create("framework", ROOT, true); + + /** {@link Tag} for Spring(framework). */ + public static final Tag SPRING = create("spring", FRAMEWORK, false, new String[] { "springframework", "springboot" }, + JAVA); + + /** {@link Tag} for Quarkus. */ + public static final Tag QUARKUS = create("quarkus", FRAMEWORK, false, null, JAVA); + + /** {@link Tag} for Micronaut. */ + public static final Tag MICRONAUT = create("micronaut", FRAMEWORK, false, null, JAVA); + + /** {@link Tag} for Angular. */ + public static final Tag ANGULAR = create("angular", FRAMEWORK, false, new String[] { "ng", "angularjs" }, + TYPE_SCRIPT); + + /** {@link Tag} for React. */ + public static final Tag REACT = create("react", FRAMEWORK, false, null, TYPE_SCRIPT); + + /** {@link Tag} for Vue. */ + public static final Tag VUE = create("vue", FRAMEWORK, false, null, TYPE_SCRIPT); + + /** {@link Tag} for Cordova. */ + public static final Tag CORDOVA = create("cordova", FRAMEWORK, false, null, JAVA_SCRIPT); + + /** {@link Tag} for Ionic. */ + public static final Tag IONIC = create("ionic", CORDOVA, false); + + /** {@link #getParent() Parent} for quality-assurance. */ + public static final Tag QA = create("quality-assurance", ROOT, false, "qa", "quality"); + + /** {@link Tag} for everything related to testing. */ + public static final Tag TEST = create("testing", QA, false, "test"); + + /** {@link Tag} for everything related to testing. */ + public static final Tag MOCK = create("mocking", TEST, false, "mock"); + + /** {@link Tag} for everything related to testing. */ + public static final Tag CODE_QA = create("static-code-analysis", QA, false, "codeqa"); + + /** {@link #getParent() Parent} for linters. */ + public static final Tag LINTING = create("linter", QA, false, "lint", "linting"); + + /** {@link Tag} for everything related to documentation. */ + public static final Tag DOCUMENTATION = create("documentation", ROOT, false, "doc"); + + /** {@link #getParent() Parent} for file formats. */ + public static final Tag FORMAT = create("format", ROOT, true); + + /** {@link Tag} for JSON. */ + public static final Tag JSON = create("json", FORMAT); + + /** {@link Tag} for YAML. */ + public static final Tag YAML = create("yaml", FORMAT, false, "yml"); + + /** {@link Tag} for CSS. */ + public static final Tag CSS = create("css", FORMAT); + + /** {@link Tag} for Properties. */ + public static final Tag PROPERTIES = create("properties", FORMAT); + + /** {@link Tag} for AsciiDoc. */ + public static final Tag ASCII_DOC = create("ascii-doc", FORMAT, false, new String[] { "adoc" }, DOCUMENTATION); + + /** {@link Tag} for MarkDown. */ + public static final Tag MARK_DOWN = create("markdown", DOCUMENTATION, false, new String[] { "md" }, DOCUMENTATION); + + /** {@link Tag} for YAML. */ + public static final Tag PDF = create("pdf", FORMAT, false, null, DOCUMENTATION); + + /** {@link Tag} for HTML. */ + public static final Tag HTML = create("html", FORMAT, false, null, DOCUMENTATION); + + /** {@link Tag} for machine-learning. */ + public static final Tag MACHINE_LEARNING = create("machine-learning", ROOT, false, "ml"); + + /** {@link Tag} for artificial-intelligence. */ + public static final Tag ARTIFICIAL_INTELLIGENCE = create("artificial-intelligence", MACHINE_LEARNING, false, "ai"); + + /** {@link Tag} for data-science. */ + public static final Tag DATA_SCIENCE = create("data-science", ROOT); + + /** {@link Tag} for business-intelligence. */ + public static final Tag BUSINESS_INTELLIGENCE = create("business-intelligence", ROOT, false, "bi", "datawarehouse", + "dwh"); + + /** {@link #Tag} for productivity. */ + public static final Tag ARCHITECTURE = create("architecture", ROOT); + + /** {@link Tag} for AsciiDoc. */ + public static final Tag UML = create("uml", ARCHITECTURE, false, null, DOCUMENTATION); + + /** {@link #Tag} for security. */ + public static final Tag SECURITY = create("security", ROOT, false, "cve"); + + /** {@link #Tag} for collaboration. */ + public static final Tag COLLABORATION = create("collaboration", ROOT, false, "collab"); + + /** {@link #Tag} for virtualization. */ + public static final Tag VIRTUALIZATION = create("virtualization", ROOT, false, "vm"); + + /** {@link #Tag} for docker. */ + public static final Tag DOCKER = create("docker", VIRTUALIZATION); + + /** {@link #Tag} for docker. */ + public static final Tag KUBERNETES = create("kubernetes", DOCKER, false, "k8s"); + + /** {@link #Tag} for WSL. */ + public static final Tag WSL = create("wsl", VIRTUALIZATION); + + /** {@link #Tag} for network. */ + public static final Tag NETWORK = create("network", ROOT, false, "remote"); + + /** {@link #Tag} for HTTP. */ + public static final Tag HTTP = create("http", NETWORK); + + /** {@link #Tag} for REST. */ + public static final Tag REST = create("rest", HTTP); + + /** {@link #Tag} for secure-shell. */ + public static final Tag SSH = create("secure-shell", NETWORK, false, "ssh", "scp"); + + /** {@link #Tag} for capture. */ + public static final Tag CAPTURE = create("capture", ROOT, false, "capturing"); + + /** {@link #Tag} for capture. */ + public static final Tag SCREENSHOT = create("screenshot", CAPTURE); + + /** {@link #Tag} for capture. */ + public static final Tag SCREEN_RECORDING = create("screenrecording", CAPTURE, false, "videocapture"); + + /** {@link #Tag} for productivity. */ + public static final Tag PRODUCTIVITY = create("productivity", ROOT); + + /** {@link #Tag} for regular-expression. */ + public static final Tag REGEX = create("regular-expression", PRODUCTIVITY, false, "regex", "regexp"); + + /** {@link #Tag} for search. */ + public static final Tag SEARCH = create("search", PRODUCTIVITY, false, "find"); + + /** {@link #Tag} for spellchecker. */ + public static final Tag SPELLCHECKER = create("spellchecker", PRODUCTIVITY, false, "spellcheck", "spellchecking"); + + /** {@link #Tag} for analyse. */ + public static final Tag ANALYSE = create("analyse", ROOT, false, "analyze", "analysis"); + + /** {@link #Tag} for monitoring. */ + public static final Tag MONITORING = create("monitoring", ANALYSE, false, "monitor"); + + /** {@link #Tag} for formatter. */ + public static final Tag FORMATTER = create("formatter", ROOT, false, "codeformat", "codeformatter"); + + /** {@link #Tag} for user-experience. */ + public static final Tag UX = create("user-experience", PRODUCTIVITY, false, "ux"); + + /** {@link #Tag} for style. */ + public static final Tag STYLE = create("style", UX, false, "theme", "icon", "skin"); + + /** {@link #Tag} for style. */ + public static final Tag KEYBINDING = create("keybinding", UX, false, "keybindings", "keymap"); + + /** {@link #Tag} for draw(ing). */ + public static final Tag DRAW = create("draw", UX, false, "diagram", "paint"); + + /** {@link #Tag} for cloud. */ + public static final Tag CLOUD = create("cloud", ROOT); + + /** {@link #Tag} for infrastructure-as-code. */ + public static final Tag IAC = create("infrastructure-as-code", CLOUD, false, "iac"); + + /** {@link #Tag} for software-configuration-management. */ + public static final Tag CONFIG_MANAGEMENT = create("software-configuration-management", ROOT, false, + "configmanagement", "configurationmanagement"); + + /** {@link #Tag} for build-management. */ + public static final Tag BUILD = create("build-management", CONFIG_MANAGEMENT, false, "build"); + + /** {@link #Tag} for version-control. */ + public static final Tag VCS = create("version-control", CONFIG_MANAGEMENT, false, "vcs", "versioncontrolsystem"); + + /** {@link #Tag} for issue-management. */ + public static final Tag ISSUE = create("issue-management", CONFIG_MANAGEMENT, false, "issue"); + + /** {@link #Tag} for git. */ + public static final Tag GIT = create("git", VCS); + + /** {@link #Tag} for github. */ + public static final Tag GITHUB = create("github", GIT); + + /** {@link #Tag} for diff (tools that compare files and determine the difference). */ + public static final Tag DIFF = create("diff", CONFIG_MANAGEMENT, false, "patch"); + + /** {@link #Tag} for diff (tools that compare files and determine the difference). */ + public static final Tag RUNTIME = create("runtime", ROOT); + + /** {@link #getParent() Parent} for operating-system. */ + public static final Tag OS = create("operating-system", ROOT, true, "os"); + + /** {@link #Tag} for Windows. */ + public static final Tag WINDOWS = create("windows", OS); + + /** {@link #Tag} for Mac. */ + public static final Tag MAC = create("mac", OS, false, "macos", "osx"); + + /** {@link #Tag} for Linux. */ + public static final Tag LINUX = create("linux", OS, false); + + private final String id; + + private final Tag parent; + + private final boolean isAbstract; + + private final Tag[] additionalParents; + + private Tag() { + + this.id = ""; + this.parent = null; + this.isAbstract = true; + this.additionalParents = NO_TAGS; + } + + private Tag(String id, Tag parent, boolean isAbstract, Tag... additionalParents) { + + super(); + Objects.requireNonNull(id); + Objects.requireNonNull(parent); + assert (id.toLowerCase(Locale.ROOT).equals(id)); + this.id = id; + this.parent = parent; + this.isAbstract = isAbstract; + this.additionalParents = additionalParents; + } + + /** + * @return the identifier and name of this tag. + */ + public String getId() { + + return this.id; + } + + /** + * @return the parent {@link Tag} or {@code null} if this is the root tag. + */ + public Tag getParent() { + + return this.parent; + } + + /** + * @param i the index of the requested parent. Should be in the range from {@code 0} to + * {@link #getParentCount()}-1. + * @return the requested {@link Tag}. + */ + public Tag getParent(int i) { + + if (i == 0) { + return this.parent; + } + return this.additionalParents[i - 1]; + } + + /** + * @return the number of {@link #getParent(int) parents} available. + */ + public int getParentCount() { + + if (this.parent == null) { + return 0; + } + return this.additionalParents.length + 1; + } + + /** + * @return {@code true} if this {@link Tag} is abstract and cannot be selected since it is just a generic parent + * container, {@code false} otherwise. + */ + public boolean isAbstract() { + + return this.isAbstract; + } + + public boolean isAncestorOf(Tag tag) { + + return isAncestorOf(tag, false); + } + + /** + * @param tag the {@link Tag} to check. + * @param includeAdditionalParents - {@code true} if {@link #getParent(int) additional parents} should be included, + * {@code false} otherwise (only consider {@link #getParent() primary parent}). + * @return {@code true} if the given {@link Tag} is an ancestor of this tag, {@code false} otherwise. An ancestor is + * a direct or indirect {@link #getParent() parent}. Therefore, if {@link #ROOT} is given as {@link Tag} parameter, + * this method should always return {@code true}. + */ + public boolean isAncestorOf(Tag tag, boolean includeAdditionalParents) { + + Tag ancestor = this.parent; + while (ancestor != null) { + if (ancestor == tag) { + return true; + } + ancestor = ancestor.parent; + } + if (includeAdditionalParents) { + for (Tag p : this.additionalParents) { + do { + if (p == tag) { + return true; + } + p = p.parent; + } while (p != null); + } + } + return false; + } + + @Override + public String toString() { + + return this.id; + } + + /** + * @param id the {@link #getId() ID} of the tag. + * @param parent the {@link #getParent() parent tag}. + * @param isAbstract the {@link #isAbstract() abstract flag}. + * @return the new {@link Tag}. + */ + static Tag create(String id, Tag parent) { + + return create(id, parent, false); + } + + /** + * @param id the {@link #getId() ID} of the tag. + * @param parent the {@link #getParent() parent tag}. + * @param isAbstract the {@link #isAbstract() abstract flag}. + * @return the new {@link Tag}. + */ + static Tag create(String id, Tag parent, boolean isAbstract) { + + return create(id, parent, isAbstract, null, NO_TAGS); + } + + /** + * @param id the {@link #getId() ID} of the tag. + * @param parent the {@link #getParent() parent tag}. + * @param isAbstract the {@link #isAbstract() abstract flag}. + * @return the new {@link Tag}. + */ + static Tag create(String id, Tag parent, boolean isAbstract, String synonym) { + + return create(id, parent, isAbstract, new String[] { synonym }, NO_TAGS); + } + + /** + * @param id the {@link #getId() ID} of the tag. + * @param parent the {@link #getParent() parent tag}. + * @param isAbstract the {@link #isAbstract() abstract flag}. + * @return the new {@link Tag}. + */ + static Tag create(String id, Tag parent, boolean isAbstract, String... synonyms) { + + return create(id, parent, isAbstract, synonyms, NO_TAGS); + } + + /** + * @param id the {@link #getId() ID} of the tag. + * @param parent the {@link #getParent() parent tag}. + * @param isAbstract the {@link #isAbstract() abstract flag}. + * @return the new {@link Tag}. + */ + static Tag create(String id, Tag parent, boolean isAbstract, String[] synonyms, Tag... additionalParents) { + + Tag tag = new Tag(id, parent, isAbstract, additionalParents); + add(id, tag); + if (synonyms != null) { + for (String synonym : synonyms) { + add(synonym, tag); + } + } + return tag; + } + + private static void add(String key, Tag tag) { + + Tag duplicate = TAG_MAP.put(normalizeKey(key), tag); + if (duplicate != null) { + throw new IllegalStateException("Duplicate tag for " + key); + } + } + + private static String normalizeKey(String key) { + + return key.replace("-", "").replace(".", ""); + } + + private static Tag require(String key) { + + Tag tag = TAG_MAP.get(normalizeKey(key)); + if (tag == null) { + throw new IllegalStateException("Could not find required tag " + key); + } + return tag; + } + + /** + * @param id the {@link #getId() ID} of the requested {@link Tag}. + * @return the {@link Tag} with the given {@link #getId() ID}. Will be lazily created as child of {@link #MISC} if not + * already exists. + */ + public static Tag of(String id) { + + final String tagId = id.trim(); + int slash = tagId.indexOf('/'); + if (slash >= 0) { + String parentId = tagId.substring(0, slash); + Tag parent = require(parentId); + String childId = tagId.substring(slash + 1); + return TAG_MAP.computeIfAbsent(normalizeKey(childId), i -> new Tag(childId, parent, false, NO_TAGS)); + } + return TAG_MAP.computeIfAbsent(normalizeKey(tagId), i -> new Tag(tagId, MISC, false, NO_TAGS)); + } + + /** + * @return the {@link Collections} of all available {@link Tag}s. + */ + public static Collection getAll() { + + return ALL_TAGS; + } + + /** + * @param tagsCsv the tags as {@link String} in CSV format («first-tag»,...,«last-tag»). May be {@code null} or empty. + * @return the parsed {@link Set} of {@link Tag}s. + */ + public static Set parseCsv(String tagsCsv) { + + if (tagsCsv == null) { + return Collections.emptySet(); + } + tagsCsv = tagsCsv.trim().toLowerCase(Locale.ROOT); + if (tagsCsv.isEmpty()) { + return Collections.emptySet(); + } + String[] tagArray = tagsCsv.split(","); + Set tags = new HashSet<>(tagArray.length); + for (String tag : tagArray) { + tags.add(of(tag)); + } + assert (tags.size() == tagArray.length); + return Set.of(tags.toArray(new Tag[tags.size()])); + } + +} diff --git a/cli/src/main/java/com/devonfw/tools/ide/common/Tags.java b/cli/src/main/java/com/devonfw/tools/ide/common/Tags.java index ae80a5891..7f7fdfea9 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/common/Tags.java +++ b/cli/src/main/java/com/devonfw/tools/ide/common/Tags.java @@ -3,65 +3,14 @@ import java.util.Set; /** - * TODO hohwille This type ... - * + * Interface for an object that {@link #getTags() has} {@link Tag}s. */ public interface Tags { - /** {@link #getTags() Tag} for Java and JVM related tools. */ - String TAG_JAVA = "java"; - - /** {@link #getTags() Tag} for build tools. */ - String TAG_BUILD = "build"; - - /** {@link #getTags() Tag} for quality assurance (QA) tools. */ - String TAG_QA = "qa"; - - /** {@link #getTags() Tag} for artificial intelligence (AI) and machine learning (ML) tools. */ - String TAG_AI = "ai"; - - /** {@link #getTags() Tag} for documentation tools. */ - String TAG_DOCUMENTATION = "doc"; - - /** {@link #getTags() Tag} for tools supporting AsciiDoc. */ - String TAG_ASCIIDOC = "adoc"; - - /** {@link #getTags() Tag} for angular related tools. */ - String TAG_ANGULAR = "angular"; - - /** {@link #getTags() Tag} for TypeScript related tools. */ - String TAG_TYPE_SCRIPT = "ts"; - - /** {@link #getTags() Tag} for generic tools that increase productivity. */ - String TAG_PRODUCTIVITY = "productivity"; - - /** {@link #getTags() Tag} for DotNet related tools. */ - String TAG_DOT_NET = ".net"; - - /** {@link #getTags() Tag} for Python related tools. */ - String TAG_PYTHON = "python"; - - /** {@link #getTags() Tag} for tools that actually represent an IDE (Integrated Development Environment). */ - String TAG_IDE = "ide"; - - /** {@link #getTags() Tag} for tools providing a runtime environment (the core of a programming language). */ - String TAG_RUNTIME = "runtime"; - /** - * {@link #getTags() Tag} for cloud tools (e.g. CLI to manage infrastructure in the cloud). This is not limited to the - * hyper-scalers but also used for other platforms providing automation like openshift or even github. + * @return a {@link Set} with the tags classifying this object. E.g. for mvn (maven) the tags {@link Tag#JAVA java} + * and {@link Tag#BUILD build} could be associated. */ - String TAG_CLOUD = "cloud"; + Set getTags(); - /** {@link #getTags() Tag} for infrastructure-as-code (IAC) tools. */ - String TAG_IAC = "iac"; - - /** {@link #getTags() Tag} for frameworks. */ - String TAG_FRAMEWORK = "framework"; - - /** - * @return a {@link Set} with the tags classifying this object. E.g. for mvn (maven) the tags {@link #TAG_JAVA java} - * and {@link #TAG_BUILD build} could be associated. - */ - Set getTags(); } diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/GlobalToolCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/tool/GlobalToolCommandlet.java index 8c83a16a1..62cf0dcdf 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/GlobalToolCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/GlobalToolCommandlet.java @@ -1,5 +1,6 @@ package com.devonfw.tools.ide.tool; +import com.devonfw.tools.ide.common.Tag; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.io.FileAccess; import com.devonfw.tools.ide.process.ProcessContext; @@ -24,7 +25,7 @@ public abstract class GlobalToolCommandlet extends ToolCommandlet { * @param tags the {@link #getTags() tags} classifying the tool. Should be created via {@link Set#of(Object) Set.of} * method. */ - public GlobalToolCommandlet(IdeContext context, String tool, Set tags) { + public GlobalToolCommandlet(IdeContext context, String tool, Set tags) { super(context, tool, tags); } diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java index 8505dead7..1159f375a 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java @@ -1,6 +1,13 @@ package com.devonfw.tools.ide.tool; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.Set; + import com.devonfw.tools.ide.commandlet.Commandlet; +import com.devonfw.tools.ide.common.Tag; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.io.FileAccess; import com.devonfw.tools.ide.io.FileCopyMode; @@ -8,12 +15,6 @@ import com.devonfw.tools.ide.repo.ToolRepository; import com.devonfw.tools.ide.version.VersionIdentifier; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardOpenOption; -import java.util.Set; - /** * {@link ToolCommandlet} that is installed locally into the IDE. */ @@ -27,12 +28,11 @@ public abstract class LocalToolCommandlet extends ToolCommandlet { * @param tags the {@link #getTags() tags} classifying the tool. Should be created via {@link Set#of(Object) Set.of} * method. */ - public LocalToolCommandlet(IdeContext context, String tool, Set tags) { + public LocalToolCommandlet(IdeContext context, String tool, Set tags) { super(context, tool, tags); } - /** * @return the {@link Path} where the tool is located (installed). */ diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java index f5ab8b3be..b6220334b 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java @@ -10,6 +10,7 @@ import com.devonfw.tools.ide.cli.CliException; import com.devonfw.tools.ide.commandlet.Commandlet; +import com.devonfw.tools.ide.common.Tag; import com.devonfw.tools.ide.common.Tags; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.environment.EnvironmentVariables; @@ -31,7 +32,7 @@ public abstract class ToolCommandlet extends Commandlet implements Tags { /** @see #getName() */ protected final String tool; - private final Set tags; + private final Set tags; /** The commandline arguments to pass to the tool. */ public final StringListProperty arguments; @@ -46,7 +47,7 @@ public abstract class ToolCommandlet extends Commandlet implements Tags { * @param tags the {@link #getTags() tags} classifying the tool. Should be created via {@link Set#of(Object) Set.of} * method. */ - public ToolCommandlet(IdeContext context, String tool, Set tags) { + public ToolCommandlet(IdeContext context, String tool, Set tags) { super(context); this.tool = tool; @@ -73,7 +74,7 @@ protected String getBinaryName() { } @Override - public final Set getTags() { + public final Set getTags() { return this.tags; } diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/az/Azure.java b/cli/src/main/java/com/devonfw/tools/ide/tool/az/Azure.java index 12937026a..195ccf922 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/az/Azure.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/az/Azure.java @@ -3,6 +3,7 @@ import java.nio.file.Paths; import java.util.Set; +import com.devonfw.tools.ide.common.Tag; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.environment.EnvironmentVariables; import com.devonfw.tools.ide.environment.EnvironmentVariablesType; @@ -22,7 +23,7 @@ public class Azure extends LocalToolCommandlet { */ public Azure(IdeContext context) { - super(context, "az", Set.of(TAG_CLOUD)); + super(context, "az", Set.of(Tag.CLOUD)); } @Override @@ -34,6 +35,6 @@ public void postInstall() { EnvironmentVariables typeVariables = variables.getByType(EnvironmentVariablesType.CONF); typeVariables.set("AZURE_CONFIG_DIR", this.context.getConfPath().resolve(".azure").toString(), true); typeVariables.save(); - this.context.getFileAccess().symlink(Paths.get("wbin"), this.getToolPath().resolve("bin")); + this.context.getFileAccess().symlink(Paths.get("wbin"), getToolPath().resolve("bin")); } } diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/eclipse/Eclipse.java b/cli/src/main/java/com/devonfw/tools/ide/tool/eclipse/Eclipse.java index 38116d067..5d91ad304 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/eclipse/Eclipse.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/eclipse/Eclipse.java @@ -11,6 +11,7 @@ import com.devonfw.tools.ide.cli.CliArgument; import com.devonfw.tools.ide.cli.CliException; +import com.devonfw.tools.ide.common.Tag; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.log.IdeLogLevel; import com.devonfw.tools.ide.process.ProcessContext; @@ -32,7 +33,7 @@ public class Eclipse extends IdeToolCommandlet { */ public Eclipse(IdeContext context) { - super(context, "eclipse", Set.of(TAG_JAVA, TAG_IDE)); + super(context, "eclipse", Set.of(Tag.ECLIPSE)); } @Override diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/gh/Gh.java b/cli/src/main/java/com/devonfw/tools/ide/tool/gh/Gh.java index 65383ed92..f9d2af057 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/gh/Gh.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/gh/Gh.java @@ -2,6 +2,7 @@ import java.util.Set; +import com.devonfw.tools.ide.common.Tag; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.tool.LocalToolCommandlet; import com.devonfw.tools.ide.tool.ToolCommandlet; @@ -18,7 +19,7 @@ public class Gh extends LocalToolCommandlet { */ public Gh(IdeContext context) { - super(context, "gh", Set.of(TAG_CLOUD)); + super(context, "gh", Set.of(Tag.CLOUD)); } } diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/gradle/Gradle.java b/cli/src/main/java/com/devonfw/tools/ide/tool/gradle/Gradle.java index f0405ce14..476cc1ce8 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/gradle/Gradle.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/gradle/Gradle.java @@ -2,6 +2,7 @@ import java.util.Set; +import com.devonfw.tools.ide.common.Tag; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.tool.LocalToolCommandlet; import com.devonfw.tools.ide.tool.ToolCommandlet; @@ -19,7 +20,7 @@ public class Gradle extends LocalToolCommandlet { */ public Gradle(IdeContext context) { - super(context, "gradle", Set.of(TAG_JAVA, TAG_BUILD)); + super(context, "gradle", Set.of(Tag.JAVA, Tag.BUILD)); } @Override diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/helm/Helm.java b/cli/src/main/java/com/devonfw/tools/ide/tool/helm/Helm.java index 1f4a2a41b..8125315df 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/helm/Helm.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/helm/Helm.java @@ -1,11 +1,12 @@ package com.devonfw.tools.ide.tool.helm; +import java.util.Set; + +import com.devonfw.tools.ide.common.Tag; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.tool.LocalToolCommandlet; import com.devonfw.tools.ide.tool.ToolCommandlet; -import java.util.Set; - /** * {@link ToolCommandlet} for Helm, the package manager for Kubernetes. */ @@ -17,7 +18,7 @@ public class Helm extends LocalToolCommandlet { */ public Helm(IdeContext context) { - super(context, "helm", Set.of(TAG_CLOUD)); + super(context, "helm", Set.of(Tag.KUBERNETES)); } } diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/ide/IdeToolCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/tool/ide/IdeToolCommandlet.java index 44c2cdd3f..2cd8ec18b 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/ide/IdeToolCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/ide/IdeToolCommandlet.java @@ -12,6 +12,7 @@ import java.util.stream.Stream; import com.devonfw.tools.ide.cli.CliException; +import com.devonfw.tools.ide.common.Tag; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.io.FileAccess; import com.devonfw.tools.ide.tool.LocalToolCommandlet; @@ -38,10 +39,20 @@ public abstract class IdeToolCommandlet extends LocalToolCommandlet { * @param tags the {@link #getTags() tags} classifying the tool. Should be created via {@link Set#of(Object) Set.of} * method. */ - public IdeToolCommandlet(IdeContext context, String tool, Set tags) { + public IdeToolCommandlet(IdeContext context, String tool, Set tags) { super(context, tool, tags); - assert (tags.contains(TAG_IDE)); + assert (hasIde(tags)); + } + + private boolean hasIde(Set tags) { + + for (Tag tag : tags) { + if (tag.isAncestorOf(Tag.IDE)) { + return true; + } + } + throw new IllegalStateException("Tags of IdeTool hat to be connected with tag IDE: " + tags); } private Map getPluginsMap() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/ide/PluginDescriptorImpl.java b/cli/src/main/java/com/devonfw/tools/ide/tool/ide/PluginDescriptorImpl.java index 3abf8d70c..6b32d1061 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/ide/PluginDescriptorImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/ide/PluginDescriptorImpl.java @@ -4,11 +4,11 @@ import java.io.Reader; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Collections; import java.util.Locale; import java.util.Properties; import java.util.Set; +import com.devonfw.tools.ide.common.Tag; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.log.IdeLogger; @@ -25,7 +25,7 @@ public class PluginDescriptorImpl implements PluginDescriptor { private final boolean active; - private final Set tags; + private final Set tags; /** * The constructor. @@ -36,7 +36,7 @@ public class PluginDescriptorImpl implements PluginDescriptor { * @param active the {@link #isActive() active flag}. * @param tags the {@link #getTags() tags}. */ - public PluginDescriptorImpl(String id, String name, String url, boolean active, Set tags) { + public PluginDescriptorImpl(String id, String name, String url, boolean active, Set tags) { super(); this.id = id; @@ -71,7 +71,7 @@ public boolean isActive() { } @Override - public Set getTags() { + public Set getTags() { return this.tags; } @@ -100,12 +100,7 @@ public static PluginDescriptor of(Path propertiesFile, IdeLogger logger, boolean } boolean active = getBoolean(properties, "active", "plugin_active", propertiesFile, logger); String tagsCsv = getString(properties, "tags", "plugin_tags"); - Set tags; - if ((tagsCsv == null) || tagsCsv.isBlank()) { - tags = Collections.emptySet(); - } else { - tags = Set.of(tagsCsv.split(",")); - } + Set tags = Tag.parseCsv(tagsCsv); return new PluginDescriptorImpl(id, name, url, active, tags); } diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/intellij/Intellij.java b/cli/src/main/java/com/devonfw/tools/ide/tool/intellij/Intellij.java index cbab7deef..2c1f6b5cc 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/intellij/Intellij.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/intellij/Intellij.java @@ -2,6 +2,7 @@ import java.util.Set; +import com.devonfw.tools.ide.common.Tag; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.tool.ide.IdeToolCommandlet; import com.devonfw.tools.ide.tool.ide.PluginDescriptor; @@ -19,7 +20,7 @@ public class Intellij extends IdeToolCommandlet { */ public Intellij(IdeContext context) { - super(context, "intellij", Set.of(TAG_JAVA, TAG_IDE)); + super(context, "intellij", Set.of(Tag.INTELLIJ)); } @Override diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/java/Java.java b/cli/src/main/java/com/devonfw/tools/ide/tool/java/Java.java index e26c7308a..e40c1707c 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/java/Java.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/java/Java.java @@ -2,6 +2,7 @@ import java.util.Set; +import com.devonfw.tools.ide.common.Tag; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.tool.LocalToolCommandlet; import com.devonfw.tools.ide.tool.ToolCommandlet; @@ -18,7 +19,7 @@ public class Java extends LocalToolCommandlet { */ public Java(IdeContext context) { - super(context, "java", Set.of(TAG_JAVA, TAG_RUNTIME)); + super(context, "java", Set.of(Tag.JAVA, Tag.RUNTIME)); } } diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/kotlinc/Kotlinc.java b/cli/src/main/java/com/devonfw/tools/ide/tool/kotlinc/Kotlinc.java index 744d051f0..c0d32d2bf 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/kotlinc/Kotlinc.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/kotlinc/Kotlinc.java @@ -2,12 +2,14 @@ import java.util.Set; +import com.devonfw.tools.ide.common.Tag; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.tool.LocalToolCommandlet; import com.devonfw.tools.ide.tool.ToolCommandlet; /** - * {@link ToolCommandlet} for Kotlin command-line compiler (kotlinc). + * {@link ToolCommandlet} for Kotlin command-line compiler + * (kotlinc). */ public class Kotlinc extends LocalToolCommandlet { @@ -18,6 +20,6 @@ public class Kotlinc extends LocalToolCommandlet { */ public Kotlinc(IdeContext context) { - super(context, "kotlinc", Set.of(TAG_JAVA, TAG_RUNTIME)); + super(context, "kotlinc", Set.of(Tag.KOTLIN, Tag.RUNTIME)); } } diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/kotlinc/KotlincNative.java b/cli/src/main/java/com/devonfw/tools/ide/tool/kotlinc/KotlincNative.java index b8ed74b58..cc9073a33 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/kotlinc/KotlincNative.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/kotlinc/KotlincNative.java @@ -2,12 +2,14 @@ import java.util.Set; +import com.devonfw.tools.ide.common.Tag; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.tool.LocalToolCommandlet; import com.devonfw.tools.ide.tool.ToolCommandlet; /** - * {@link ToolCommandlet} for Kotlin Native (kotlincnative). + * {@link ToolCommandlet} for Kotlin Native + * (kotlincnative). */ public class KotlincNative extends LocalToolCommandlet { @@ -18,6 +20,6 @@ public class KotlincNative extends LocalToolCommandlet { */ public KotlincNative(IdeContext context) { - super(context, "kotlincnative", Set.of(TAG_JAVA, TAG_RUNTIME)); + super(context, "kotlincnative", Set.of(Tag.KOTLIN, Tag.RUNTIME)); } } 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 5ef339f7c..d99aa227c 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 @@ -2,6 +2,7 @@ import java.util.Set; +import com.devonfw.tools.ide.common.Tag; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.tool.LocalToolCommandlet; import com.devonfw.tools.ide.tool.ToolCommandlet; @@ -19,7 +20,7 @@ public class Mvn extends LocalToolCommandlet { */ public Mvn(IdeContext context) { - super(context, "mvn", Set.of(TAG_JAVA, TAG_BUILD)); + super(context, "mvn", Set.of(Tag.JAVA, Tag.BUILD)); } @Override diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/node/Node.java b/cli/src/main/java/com/devonfw/tools/ide/tool/node/Node.java index a3f9c976e..7d4ad94fa 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/node/Node.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/node/Node.java @@ -2,6 +2,7 @@ import java.util.Set; +import com.devonfw.tools.ide.common.Tag; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.tool.LocalToolCommandlet; import com.devonfw.tools.ide.tool.ToolCommandlet; @@ -18,7 +19,7 @@ public class Node extends LocalToolCommandlet { */ public Node(IdeContext context) { - super(context, "node", Set.of(TAG_RUNTIME)); + super(context, "node", Set.of(Tag.JAVA_SCRIPT, Tag.RUNTIME)); } } diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/oc/Oc.java b/cli/src/main/java/com/devonfw/tools/ide/tool/oc/Oc.java index 31a7f1b9f..527b40bbc 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/oc/Oc.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/oc/Oc.java @@ -1,11 +1,12 @@ package com.devonfw.tools.ide.tool.oc; +import java.util.Set; + +import com.devonfw.tools.ide.common.Tag; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.tool.LocalToolCommandlet; import com.devonfw.tools.ide.tool.ToolCommandlet; -import java.util.Set; - /** * {@link ToolCommandlet} for Openshift CLI. */ @@ -18,7 +19,7 @@ public class Oc extends LocalToolCommandlet { */ public Oc(IdeContext context) { - super(context, "oc", Set.of(TAG_CLOUD)); + super(context, "oc", Set.of(Tag.CLOUD)); } } diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/quarkus/Quarkus.java b/cli/src/main/java/com/devonfw/tools/ide/tool/quarkus/Quarkus.java index d135d9abc..70c8d7263 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/quarkus/Quarkus.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/quarkus/Quarkus.java @@ -1,11 +1,12 @@ package com.devonfw.tools.ide.tool.quarkus; +import java.util.Set; + +import com.devonfw.tools.ide.common.Tag; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.tool.LocalToolCommandlet; import com.devonfw.tools.ide.tool.ToolCommandlet; -import java.util.Set; - /** * {@link ToolCommandlet} for Quarkus. */ @@ -17,7 +18,7 @@ public class Quarkus extends LocalToolCommandlet { */ public Quarkus(IdeContext context) { - super(context, "quarkus", Set.of(TAG_CLOUD, TAG_FRAMEWORK)); + super(context, "quarkus", Set.of(Tag.JAVA, Tag.FRAMEWORK)); } } diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/terraform/Terraform.java b/cli/src/main/java/com/devonfw/tools/ide/tool/terraform/Terraform.java index 56174c0c7..745ececde 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/terraform/Terraform.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/terraform/Terraform.java @@ -2,6 +2,7 @@ import java.util.Set; +import com.devonfw.tools.ide.common.Tag; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.tool.LocalToolCommandlet; import com.devonfw.tools.ide.tool.ToolCommandlet; @@ -18,7 +19,7 @@ public class Terraform extends LocalToolCommandlet { */ public Terraform(IdeContext context) { - super(context, "terraform", Set.of(TAG_CLOUD, TAG_IAC)); + super(context, "terraform", Set.of(Tag.IAC)); } @Override diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/vscode/Vscode.java b/cli/src/main/java/com/devonfw/tools/ide/tool/vscode/Vscode.java index a7e85da00..563a6586a 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/vscode/Vscode.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/vscode/Vscode.java @@ -2,6 +2,7 @@ import java.util.Set; +import com.devonfw.tools.ide.common.Tag; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.tool.LocalToolCommandlet; import com.devonfw.tools.ide.tool.ToolCommandlet; @@ -16,9 +17,9 @@ public class Vscode extends LocalToolCommandlet { * * @param context the {@link IdeContext}. */ - public Vscode (IdeContext context) { + public Vscode(IdeContext context) { - super(context, "vscode", Set.of(TAG_IDE)); + super(context, "vscode", Set.of(Tag.VS_CODE)); } @Override diff --git a/cli/src/test/java/com/devonfw/tools/ide/common/TagTest.java b/cli/src/test/java/com/devonfw/tools/ide/common/TagTest.java new file mode 100644 index 000000000..bfa7d1ce0 --- /dev/null +++ b/cli/src/test/java/com/devonfw/tools/ide/common/TagTest.java @@ -0,0 +1,151 @@ +package com.devonfw.tools.ide.common; + +import java.util.Collection; +import java.util.Set; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * Test of {@link Tag}. + */ +public class TagTest extends Assertions { + + /** + * Test various predefined {@link Tag}s. + */ + @Test + public void testTags() { + + checkTag(Tag.ROOT, "", null, true); + checkTag(Tag.MACHINE_LEARNING, "machine-learning", Tag.ROOT); + checkTag(Tag.ARTIFICIAL_INTELLIGENCE, "artificial-intelligence", Tag.MACHINE_LEARNING); + } + + private void checkTag(Tag tag, String id, Tag parent) { + + checkTag(tag, id, parent, false); + } + + private void checkTag(Tag tag, String id, Tag parent, boolean isAbstract) { + + assertThat(tag.getId()).isEqualTo(id); + assertThat(tag.getParent()).isSameAs(parent); + assertThat(tag.isAbstract()).isEqualTo(isAbstract); + assertThat(tag.toString()).isEqualTo(id); + } + + /** + * Test of {@link Tag#of(String)}. + */ + @Test + public void testOf() { + + checkOf(Tag.ROOT); + checkOf(Tag.MACHINE_LEARNING, "ml", "machinelearning"); + checkOf(Tag.ARTIFICIAL_INTELLIGENCE, "ai", "artificialintelligence"); + } + + private void checkOf(Tag tag, String... synonyms) { + + assertThat(Tag.of(tag.getId())).isSameAs(tag); + for (String synonym : synonyms) { + assertThat(Tag.of(synonym)).isSameAs(tag); + } + } + + /** + * Test of {@link Tag#of(String)} with new tag that do not yet exist. + */ + @Test + public void testOfNew() { + + // arrange + String id = "undefined"; + // act + Tag tag = Tag.of(id); + // assert + checkTag(tag, id, Tag.MISC); + } + + /** + * Test of {@link Tag#of(String)} with new tags that do not yet exist and given parent. + */ + @Test + public void testOfNewWithParent() { + + // arrange + String id = "brandnew"; + // act + Tag tag = Tag.of("ide/" + id); + // assert + checkTag(tag, id, Tag.IDE); + } + + /** + * Test of {@link Tag#getAll()}. + */ + @Test + public void testGetAll() { + + // act + Collection tags = Tag.getAll(); + // assert + assertThat(tags).contains(Tag.ROOT, Tag.ANDROID_STUDIO, Tag.ECLIPSE, Tag.IDE, Tag.VS_CODE); + assertThat(tags.size()).isGreaterThan(150); + } + + /** + * Test of {@link Tag#parseCsv(String)}. + */ + @Test + public void testParseCsv() { + + // arrange + String csv = " c,c++, c# "; // also test trimming + // act + Set tags = Tag.parseCsv(csv); + // assert + assertThat(tags).containsExactlyInAnyOrder(Tag.C, Tag.CPP, Tag.CS); + } + + /** + * Test of {@link Tag#parseCsv(String)} with empty CSV. + */ + @Test + public void testParseCsvEmpty() { + + assertThat(Tag.parseCsv(null)).isEmpty(); + assertThat(Tag.parseCsv("")).isEmpty(); + assertThat(Tag.parseCsv(" ")).isEmpty(); + } + + /** + * Test of {@link Tag#getParent(int)} and {@link Tag#getParentCount()}. + */ + @Test + public void testGetParents() { + + assertThat(Tag.ROOT.getParentCount()).isZero(); + assertThat(Tag.DOCUMENTATION.getParentCount()).isOne(); + assertThat(Tag.DOCUMENTATION.getParent(0)).isSameAs(Tag.ROOT); + assertThat(Tag.ASCII_DOC.getParentCount()).isEqualTo(2); + assertThat(Tag.ASCII_DOC.getParent(0)).isSameAs(Tag.FORMAT); + assertThat(Tag.ASCII_DOC.getParent(1)).isSameAs(Tag.DOCUMENTATION); + } + + /** + * Test of {@link Tag#isAncestorOf(Tag)}. + */ + @Test + public void testIsAncestorOf() { + + assertThat(Tag.QUARKUS.isAncestorOf(Tag.ROOT)).isTrue(); + assertThat(Tag.QUARKUS.isAncestorOf(Tag.DOCUMENTATION)).isFalse(); + assertThat(Tag.QUARKUS.isAncestorOf(Tag.JAVA)).isFalse(); + assertThat(Tag.QUARKUS.isAncestorOf(Tag.QUARKUS)).isFalse(); + boolean includeAdditionalParents = true; + assertThat(Tag.QUARKUS.isAncestorOf(Tag.JAVA, includeAdditionalParents)).isTrue(); + assertThat(Tag.QUARKUS.isAncestorOf(Tag.LANGUAGE, includeAdditionalParents)).isTrue(); + } +} From e82a1a0a4d90fecb6b4650f05a2dca1251dbbdb2 Mon Sep 17 00:00:00 2001 From: Mattes Mrzik Date: Tue, 16 Jan 2024 14:05:08 +0100 Subject: [PATCH 5/5] devonfw/ide#1368: added updater for eclipse jee (#177) --- .../devonfw/tools/ide/tool/LocalToolCommandlet.java | 2 +- .../tools/ide/tool/eclipse/EclipseJeeUrlUpdater.java | 12 ++++++++++++ .../tools/ide/tool/eclipse/EclipseUrlUpdater.java | 2 +- .../devonfw/tools/ide/url/updater/UpdateManager.java | 10 ++++++---- 4 files changed, 20 insertions(+), 6 deletions(-) create mode 100644 cli/src/main/java/com/devonfw/tools/ide/tool/eclipse/EclipseJeeUrlUpdater.java diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java index 1159f375a..ef11a0fa1 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java @@ -80,7 +80,7 @@ protected boolean doInstall(boolean silent) { if (installedVersion == null) { this.context.success("Successfully installed {} in version {}", this.tool, resolvedVersion); } else { - this.context.success("Successfully installed {} in version {} replacing previous version {]", this.tool, + this.context.success("Successfully installed {} in version {} replacing previous version {}", this.tool, resolvedVersion, installedVersion); } postInstall(); diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/eclipse/EclipseJeeUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/eclipse/EclipseJeeUrlUpdater.java new file mode 100644 index 000000000..c8c905212 --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/eclipse/EclipseJeeUrlUpdater.java @@ -0,0 +1,12 @@ +package com.devonfw.tools.ide.tool.eclipse; + +/** + * {@link EclipseUrlUpdater} for "jee" (C++) edition of Eclipse. + */ +public class EclipseJeeUrlUpdater extends EclipseUrlUpdater { + @Override + protected String getEdition() { + + return "jee"; + } +} diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/eclipse/EclipseUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/eclipse/EclipseUrlUpdater.java index 63552aacb..f53b6175a 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/eclipse/EclipseUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/eclipse/EclipseUrlUpdater.java @@ -86,7 +86,7 @@ protected Pattern getVersionPattern() { @Override protected String mapVersion(String version) { - // TODO remove this hack and get versiosn from reliable API + // TODO remove this hack and get versions from reliable API return super.mapVersion(version.replace(" ", "-")); } diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java b/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java index 14642205b..637feca26 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java +++ b/cli/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java @@ -16,6 +16,7 @@ import com.devonfw.tools.ide.tool.docker.DockerRancherDesktopUrlUpdater; import com.devonfw.tools.ide.tool.dotnet.DotNetUrlUpdater; import com.devonfw.tools.ide.tool.eclipse.EclipseCppUrlUpdater; +import com.devonfw.tools.ide.tool.eclipse.EclipseJeeUrlUpdater; import com.devonfw.tools.ide.tool.eclipse.EclipseJavaUrlUpdater; import com.devonfw.tools.ide.tool.gcloud.GCloudUrlUpdater; import com.devonfw.tools.ide.tool.gcviewer.GcViewerUrlUpdater; @@ -57,10 +58,11 @@ public class UpdateManager extends AbstractProcessorWithTimeout { private final UrlRepository urlRepository; private final List updaters = Arrays.asList(new AndroidStudioUrlUpdater(), new AwsUrlUpdater(), - new AzureUrlUpdater(), new CobigenUrlUpdater(), new DockerDesktopUrlUpdater() , new DotNetUrlUpdater(), new EclipseCppUrlUpdater(), - new EclipseJavaUrlUpdater(), new GCloudUrlUpdater(), new GcViewerUrlUpdater(), new GhUrlUpdater(), - new GraalVmCommunityUpdater(), new GraalVmOracleUrlUpdater(), new GradleUrlUpdater(), new HelmUrlUpdater(), new IntellijUrlUpdater(), - new JavaUrlUpdater(), new JenkinsUrlUpdater(), new JmcUrlUpdater(), new KotlincUrlUpdater(), new KotlincNativeUrlUpdater(), + new AzureUrlUpdater(), new CobigenUrlUpdater(), new DockerDesktopUrlUpdater(), new DotNetUrlUpdater(), + new EclipseCppUrlUpdater(), new EclipseJeeUrlUpdater(), new EclipseJavaUrlUpdater(), new GCloudUrlUpdater(), + new GcViewerUrlUpdater(), new GhUrlUpdater(), new GraalVmCommunityUpdater(), new GraalVmOracleUrlUpdater(), + new GradleUrlUpdater(), new HelmUrlUpdater(), new IntellijUrlUpdater(), new JavaUrlUpdater(), + new JenkinsUrlUpdater(), new JmcUrlUpdater(), new KotlincUrlUpdater(), new KotlincNativeUrlUpdater(), new LazyDockerUrlUpdater(), new MvnUrlUpdater(), new NodeUrlUpdater(), new NpmUrlUpdater(), new OcUrlUpdater(), new PipUrlUpdater(), new PythonUrlUpdater(), new QuarkusUrlUpdater(), new DockerRancherDesktopUrlUpdater(), new SonarUrlUpdater(), new TerraformUrlUpdater(), new TomcatUrlUpdater(), new VsCodeUrlUpdater());