Java SDK: non-Enum POJO types

Hi,

in the Java SDK we have two types of POJOs for types: Enums and Classes.

This is pretty inconsistent and makes working in an object oriented way pretty hard.

Is there a reason for this that I am not seeing?
Would it be possible to migrate the Class POJOs to Enums in the next SDK updates?

  • This pattern has been in place since the SDK was released in 2009, and making the change that you are suggesting would be a breaking change with a high impact. So it is not a change that will be made.

    If you look at the Types tab in Profile.xlsx, the Types that have a base-type of enum become enums in the SDK. The Types that have a base type of uint32, or similar, become classes with the static final fields in the SDK. 
    https://github.com/garmin/fit-sdk-tools 

    Which base type is used depends on the situation. The enum and class POJOs both have some combination of the getStringFromValue, getByValue, and getValueFromString methods, which are meant to make these POJOs have somewhat of a common interface. The FIT CSV Tool uses the Java SDK and uses these methods to convert between FIT & CSV, and vice versa. It is meant to mimic the functionality of enums where you can go between the string value and the underlying integer value.


    But we absolutely understand the problem. In the Swift SDK that came out last year where possible the Types are enums. I get that this does not solve your problem in the Java SDK, just saying that we understand the issue and would fix it if we could.

    These two are classes in Java and enums in Swift.
    https://github.com/garmin/fit-swift-sdk/blob/main/Sources/FITSwiftSDK/Profile/Types/BleDeviceType.swift

    https://github.com/garmin/fit-swift-sdk/blob/main/Sources/FITSwiftSDK/Profile/Types/AntplusDeviceType.swift

    There are some types that are used as bitmasks, so those had to be structs in Swift. For example

    https://github.com/garmin/fit-swift-sdk/blob/main/Sources/FITSwiftSDK/Profile/Types/FileFlags.swift 

  • Thanks for that insight.

    So, I understand correctly, that the main reason for those Class types is basically historic dept?
    In that case: I think it would be possible to migrate those Classes while largely maintaining backwards compatibility.

    Look at the current Class implementation of AntplusDeviceType:

    public class AntplusDeviceType  {
    public static final short ANTFS = 1;
    public static final short BIKE_POWER = 11;
    public static final short ENVIRONMENT_SENSOR_LEGACY = 12;
    public static final short MULTI_SPORT_SPEED_DISTANCE = 15;
    // ...
    public static final short INVALID = Fit.UINT8_INVALID;

    private static final Map<Short, String> stringMap;

    static {
    stringMap = new HashMap<Short, String>();
    stringMap.put(ANTFS, "ANTFS");
    stringMap.put(BIKE_POWER, "BIKE_POWER");
    stringMap.put(ENVIRONMENT_SENSOR_LEGACY, "ENVIRONMENT_SENSOR_LEGACY");
    stringMap.put(MULTI_SPORT_SPEED_DISTANCE, "MULTI_SPORT_SPEED_DISTANCE");
    // ...
    }

    public static String getStringFromValue( Short value ) {
    if( stringMap.containsKey( value ) ) {
    return stringMap.get( value );
    }
    return "";
    }

    public static Short getValueFromString( String value ) {
    for( Map.Entry<Short, String> entry : stringMap.entrySet() ) {
    if( entry.getValue().equals( value ) ) {
    return entry.getKey();
    }
    }
    return INVALID;
    }
    }

    This could be modified to:

    public enum AntplusDeviceType() {
    ANTFS((short)1),
    BIKE_POWER((short)11),
    ENVIRONMENT_SENSOR_LEGACY((short)12),
    MULTI_SPORT_SPEED_DISTANCE((short)15),
    // ...
    INVALID(Fit.UINT8_INVALID);

    protected short value;

    private AntplusDeviceType(short value) {
    this.value = value;
    }

    // new implementation; same signature
    public static String getStringFromValue( Short value ) {
    return getByValue(value).name();
    }

    // new implementation; same signature
    public static Short getValueFromString( String value ) {
    for (final AntplusDeviceType type : AntplusDeviceType.values()) {
    if (value.equals(type.name()))
    return type.value;
    }
    return AntplusDeviceType.INVALID.value;
    }

    // fully new
    public static AntplusDeviceType getByValue(final Short value) {
    for (final AntplusDeviceType type : AntplusDeviceType.values()) {
    if (value == type.value)
    return type;
    }
    return AntplusDeviceType.INVALID;
    }

    // fully new
    public static String getStringFromValue( AntplusDeviceType value ) {
    return value.name();
    }

    // fully new
    public short getValue() {
    return value;
    }

    }

    All the methods remain existent.

    There is only one thing that comes to my mind: The Class could be instantiated, the Enum can't.
    But to be fair: That Class can do nothing at all, if it is instantiated. 

    So, in my opinion, a migration could be possible and would be highly beneficial to the codebase of the SDK.

    One could mark the public constructor of the current Class implementations as deprecated for some releases, to be sure.