mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2026-07-31 07:44:24 +02:00
Garmin: Improve field cast safety
This commit is contained in:
+58
@@ -2,6 +2,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
@@ -18,8 +19,11 @@ import nodomain.freeyourgadget.gadgetbridge.util.GBToStringBuilder;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.baseTypes.BaseType.STRING;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class RecordData {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(RecordData.class);
|
||||
|
||||
private final RecordDefinition recordDefinition;
|
||||
private final RecordHeader recordHeader;
|
||||
@@ -143,6 +147,59 @@ public class RecordData {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> T getFieldByNumber(int number, final Class<T> clazz) {
|
||||
return safeCast(getFieldByNumber(number), clazz);
|
||||
}
|
||||
|
||||
public <T> T[] getArrayFieldByNumber(int number, final Class<T> clazz) {
|
||||
final Object object = getFieldByNumber(number);
|
||||
if (object == null)
|
||||
return null;
|
||||
if (!object.getClass().isArray()) {
|
||||
final T casted = safeCast(object, clazz);
|
||||
if (casted != null) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final T[] ret = (T[]) Array.newInstance(clazz, 1);
|
||||
ret[0] = casted;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
final Object[] objectsArray = (Object[]) object;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
final T[] ret = (T[]) Array.newInstance(clazz, objectsArray.length);
|
||||
|
||||
for (int i = 0; i < objectsArray.length; i++) {
|
||||
ret[i] = safeCast(objectsArray[i], clazz);
|
||||
if (ret[i] == null) {
|
||||
// One of the safe casts failed - abort
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private <T> T safeCast(final Object object, final Class<T> clazz) {
|
||||
if (object == null) {
|
||||
return null;
|
||||
}
|
||||
if (clazz.isInstance(object)) {
|
||||
return clazz.cast(object);
|
||||
}
|
||||
|
||||
LOG.error(
|
||||
"Unable to cast {} ({}) to {}, returning null - this is likely a bug. Record: {}",
|
||||
object,
|
||||
object.getClass().getSimpleName(),
|
||||
clazz.getSimpleName(),
|
||||
this
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Object getFieldByName(String name) {
|
||||
for (FieldData fieldData :
|
||||
fieldDataList) {
|
||||
@@ -290,6 +347,7 @@ public class RecordData {
|
||||
return fieldDefinition.decode(valueHolder);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String toString() {
|
||||
return "(" + fieldDefinition.getBaseType().name() + "/" + size + ")";
|
||||
}
|
||||
|
||||
+8
-16
@@ -52,7 +52,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
||||
@RequiresApi(api = Build.VERSION_CODES.CUR_DEVELOPMENT)
|
||||
public class FitCodeGen {
|
||||
private static final String COPYRIGHT_HEADER = """
|
||||
/* Copyright (C) 2025 Freeyourgadget
|
||||
/* Copyright (C) 2026 Freeyourgadget
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@@ -126,7 +126,8 @@ public class FitCodeGen {
|
||||
final String result = template
|
||||
.replace("${copyrightHeader}", (existingHeader.isEmpty() ? COPYRIGHT_HEADER : existingHeader).strip())
|
||||
.replace("${generatorClass}", Objects.requireNonNull(getClass().getCanonicalName()))
|
||||
.replace("${switchCases}", String.join("\n", switchCases));
|
||||
.replace("${switchCases}", String.join("\n", switchCases))
|
||||
.replaceAll("\\R", System.lineSeparator());
|
||||
|
||||
FileUtils.copyStringToFile(result, factoryFile, "replace");
|
||||
}
|
||||
@@ -246,18 +247,7 @@ public class FitCodeGen {
|
||||
|
||||
@Nullable
|
||||
public ${fieldType}[] ${getterName}() {
|
||||
final Object object = getFieldByNumber(${primitiveNumber});
|
||||
if (object == null)
|
||||
return null;
|
||||
if (!object.getClass().isArray()) {
|
||||
return new ${fieldType}[]{(${fieldType}) object};
|
||||
}
|
||||
final Object[] objectsArray = (Object[]) object;
|
||||
final ${fieldType}[] ret = new ${fieldType}[objectsArray.length];
|
||||
for (int i = 0; i < objectsArray.length; i++) {
|
||||
ret[i] = (${fieldType}) objectsArray[i];
|
||||
}
|
||||
return ret;
|
||||
return getArrayFieldByNumber(${primitiveNumber}, ${fieldType}.class);
|
||||
}
|
||||
""";
|
||||
} else {
|
||||
@@ -266,7 +256,7 @@ public class FitCodeGen {
|
||||
|
||||
@Nullable
|
||||
public ${fieldType} ${getterName}() {
|
||||
return (${fieldType}) getFieldByNumber(${primitiveNumber});
|
||||
return getFieldByNumber(${primitiveNumber}, ${fieldType}.class);
|
||||
}
|
||||
""";
|
||||
}
|
||||
@@ -344,7 +334,9 @@ public class FitCodeGen {
|
||||
sb.append("}\n");
|
||||
}
|
||||
|
||||
FileUtils.copyStringToFile(sb.toString(), outputFile, "replace");
|
||||
final String output = sb.toString().replaceAll("\\R", System.lineSeparator());
|
||||
|
||||
FileUtils.copyStringToFile(output, outputFile, "replace");
|
||||
}
|
||||
|
||||
public Class<?> getFieldType(final GlobalFITMessage.FieldDefinitionPrimitive primitive) {
|
||||
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
# nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.codegenFitCodeGen
|
||||
# is hard coded to uses \n
|
||||
*.java text eol=lf
|
||||
Reference in New Issue
Block a user