static Object convertToObject(
            final Prop prop,
            final String value,
            final Type genericType,
            final NullBehaviourMode nullBehaviourMode) {
        final Class<?> type = PropertyUtil.getDataType(genericType);

        try {
            if (value == null || value.isBlank()) {
                if (type.isPrimitive()) {
                    if (NullBehaviourMode.DEFAULT.equals(nullBehaviourMode)) {
                        return getDefaultValue(type);
                    } else {
                        throw new RuntimeException(LogUtil.message(
                                "A null or empty value is not a valid value for this property which has " +
                                "a primitive type of {}", type.getName()));
                    }
                } else {
                    return null;
                }
            }

            // If we reach here the value is non-null and non-blank
            if (type.equals(String.class)) {
                return value;
            } else if (type.equals(Byte.class) || type.equals(byte.class)) {
                return Byte.valueOf(value);
            } else if (type.equals(Integer.class) || type.equals(int.class)) {
                return Integer.valueOf(value);
            } else if (type.equals(Long.class) || type.equals(long.class)) {
                return Long.valueOf(value);
            } else if (type.equals(Short.class) || type.equals(short.class)) {
                return Short.valueOf(value);
            } else if (type.equals(Float.class) || type.equals(float.class)) {
                return Float.valueOf(value);
            } else if (type.equals(Double.class) || type.equals(double.class)) {
                return Double.valueOf(value);
            } else if (type.equals(Boolean.class) || type.equals(boolean.class)) {
                return parseBoolean(value);
            } else if ((type.equals(Character.class) || type.equals(char.class)) && value.length() > 0) {
                return value.charAt(0);
            } else if (List.class.isAssignableFrom(type)) {
                // determine the type of the list items
                final Class<?> itemType = getGenericsParam(genericType, 0);
                return stringToList(prop, value, itemType);
            } else if (Set.class.isAssignableFrom(type)) {
                // determine the type of the list items
                final Class<?> itemType = getGenericsParam(genericType, 0);
                return stringToSet(prop, value, itemType);
            } else if (Map.class.isAssignableFrom(type)) {
                // determine the types of the keys and values
                final Class<?> keyType = getGenericsParam(genericType, 0);
                final Class<?> valueType = getGenericsParam(genericType, 1);
                return stringToMap(prop, value, keyType, valueType);
            } else if (type.equals(DocRef.class)) {
                return stringToDocRef(value);
            } else if (Enum.class.isAssignableFrom(type)) {
                return stringToEnum(value, type);
            } else if (Path.class.isAssignableFrom(type)) {
                return Path.of(value);
            } else if (StroomDuration.class.isAssignableFrom(type)) {
                return StroomDuration.parse(value);
            } else if (ByteSize.class.isAssignableFrom(type)) {
                return ByteSize.parse(value);
            }
        } catch (Exception e) {
            // Don't include the original exception else gwt uses the msg of the
            // original which is not very user friendly. Enable debug to see the stack
            final String propName = (prop.getParentObject() == null
                    ? "null"
                    : prop.getParentObject().getClass().getSimpleName())
                                    + "."
                                    + prop.getName();

            LOGGER.debug(() -> LogUtil.message(
                    "Unable to convert value [{}] of property [{}] to type [{}] due to: {}",
                    value, propName, getDataTypeName(genericType), e.getMessage()), e);
            throw new ConfigPropertyValidationException(LogUtil.message(
                    "Unable to convert value [{}] of property [{}] to type [{}] due to: {}",
                    value, propName, getDataTypeName(genericType), e.getMessage()), e);
        }

        LOGGER.error("Unable to convert value [{}] of type [{}] to an Object", value, type);
        throw new ConfigPropertyValidationException(LogUtil.message(
                "Type [{}] is not supported for value [{}]", genericType, value));
    }
@param dbConfigProperty The config property object obtained from the database
@return The updated typed value from the object model
stroom-config/stroom-config-global-impl/src/main/java/stroom/config/global/impl/ConfigMapper.java