Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Create version compare utility <https://github.com/fugerit-org/fj-lib/issues/99>

### Deprecated

- MavenProps.getPropery() method. (flagged for removal in future versions)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package org.fugerit.java.core.cfg;

/**
* Utility class for comparing version strings in the format "MAJOR.MINOR.PATCH",
* optionally including qualifiers (e.g. "-SNAPSHOT"). Provides a compare method
* that handles versions with different lengths and a convenience method for
* checking if one version is greater than another.
*
* <p>This implementation follows standard Semantic Versioning comparison rules:
* numeric comparison of components, missing components treated as zero.</p>
*
* @author Your Name
* @since 1.0
*/
public class VersionCompare {

private VersionCompare() {
// Prevent instantiation
}

/**
* Extracts the numeric portion of a version string component, ignoring any
* qualifier suffix that begins with a hyphen.
*
* @param part the version component, e.g. "1", "2-SNAPSHOT"
* @return the integer value of the numeric portion
* @throws NumberFormatException if the numeric portion is not a valid integer
*/
private static int convertVersionPart(String part) {
String numeric = part.split("-", 2)[0];
return Integer.parseInt(numeric);
}

/**
* Compares two version strings in "MAJOR.MINOR.PATCH(...)" format.
* Missing components are treated as zero.
*
* @param v1 the first version string to compare
* @param v2 the second version string to compare
* @return a negative integer if {@code v1 < v2}, zero if equal, or a positive
* integer if {@code v1 > v2}
* @throws NumberFormatException if any component is not a valid integer
*
* @since 1.0
*/
public static int compare(String v1, String v2) {
String[] parts1 = v1.split("\\.");
String[] parts2 = v2.split("\\.");
int length = Math.max(parts1.length, parts2.length);

for (int i = 0; i < length; i++) {
int p1 = i < parts1.length ? convertVersionPart(parts1[i]) : 0;
int p2 = i < parts2.length ? convertVersionPart(parts2[i]) : 0;
if (p1 != p2) {
return Integer.compare(p1, p2);
}
}
return 0;
}

/**
* Determines if the first version string represents a version strictly greater
* than the second.
*
* @param v1 the first version string
* @param v2 the second version string
* @return {@code true} if {@code v1} is greater than {@code v2}, {@code false}
* otherwise (including equality)
*
* @since 1.0
*/
public static boolean isGreaterThan(String v1, String v2) {
return compare(v1, v2) > 0;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package test.org.fugerit.java.core.cfg;

import org.fugerit.java.core.cfg.VersionCompare;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class TestVersionCompare {

// Version constants
private static final String V_1_0_0 = "1.0.0";
private static final String V_2_0_0 = "2.0.0";
private static final String V_1_9_9 = "1.9.9";
private static final String V_3_0_0 = "3.0.0";
private static final String V_2_5_6 = "2.5.6";
private static final String V_1_2_0 = "1.2.0";
private static final String V_1_1_9 = "1.1.9";
private static final String V_1_3 = "1.3";
private static final String V_1_2_9 = "1.2.9";
private static final String V_1_0_1 = "1.0.1";
private static final String V_1_0_5 = "1.0.5";
private static final String V_1_0_4 = "1.0.4";
private static final String V_1_0 = "1.0";
private static final String V_1_0_0_SNAPSHOT = "1.0.0-SNAPSHOT";
private static final String V_1_0_1_RC1 = "1.0.1-RC1";
private static final String V_1_A_0 = "1.a.0";
private static final String V_X_Y_Z = "x.y.z";

@Test
void testEqualVersions() {
assertEquals(0, VersionCompare.compare(V_1_0_0, V_1_0_0));
assertFalse(VersionCompare.isGreaterThan(V_1_0_0, V_1_0_0));
}

@Test
void testGreaterMajorVersion() {
assertTrue(VersionCompare.isGreaterThan(V_2_0_0, V_1_9_9));
assertEquals(1, VersionCompare.compare(V_3_0_0, V_2_5_6));
}

@Test
void testGreaterMinorVersion() {
assertTrue(VersionCompare.isGreaterThan(V_1_2_0, V_1_1_9));
assertEquals(1, VersionCompare.compare(V_1_3, V_1_2_9));
}

@Test
void testGreaterPatchVersion() {
assertTrue(VersionCompare.isGreaterThan(V_1_0_1, V_1_0_0));
assertEquals(1, VersionCompare.compare(V_1_0_5, V_1_0_4));
}

@Test
void testVersionWithDifferentLength() {
assertEquals(0, VersionCompare.compare(V_1_0, V_1_0_0));
assertTrue(VersionCompare.isGreaterThan(V_1_0_1, V_1_0));
assertFalse(VersionCompare.isGreaterThan(V_1_0, V_1_0_1));
}

@Test
void testVersionWithQualifierSuffix() {
assertEquals(0, VersionCompare.compare(V_1_0_0_SNAPSHOT, V_1_0_0));
assertTrue(VersionCompare.isGreaterThan(V_1_0_1_RC1, V_1_0_0));
}

@Test
void testInvalidVersionShouldThrowException() {
assertThrows(NumberFormatException.class, () -> VersionCompare.compare(V_1_A_0, V_1_0_0));
assertThrows(NumberFormatException.class, () -> VersionCompare.compare(V_1_0, V_X_Y_Z));
}

}