@Override
    public void onPosition(Position position, Callback callback) {
        var attributes = cacheManager.getDeviceObjects(position.getDeviceId(), Attribute.class).stream()
                .filter(attribute -> attribute.getPriority() < 0 == early)
                .sorted(Comparator.comparing(Attribute::getPriority).reversed())
                .toList();
        for (Attribute attribute : attributes) {
            if (attribute.getAttribute() != null) {
                try {
                    Object result = computeAttribute(attribute, position);
                    if (result != null) {
                        switch (attribute.getAttribute()) {
                            case "valid" -> position.setValid((Boolean) result);
                            case "latitude" -> position.setLatitude(((Number) result).doubleValue());
                            case "longitude" -> position.setLongitude(((Number) result).doubleValue());
                            case "altitude" -> position.setAltitude(((Number) result).doubleValue());
                            case "speed" -> position.setSpeed(((Number) result).doubleValue());
                            case "course" -> position.setCourse(((Number) result).doubleValue());
                            case "address" -> position.setAddress((String) result);
                            case "accuracy" -> position.setAccuracy(((Number) result).doubleValue());
                            default -> {
                                switch (attribute.getType()) {
                                    case "number" -> {
                                        Number numberValue = (Number) result;
                                        position.getAttributes().put(attribute.getAttribute(), numberValue);
                                    }
                                    case "boolean" -> {
                                        Boolean booleanValue = (Boolean) result;
                                        position.getAttributes().put(attribute.getAttribute(), booleanValue);
                                    }
                                    default -> {
                                        position.getAttributes().put(attribute.getAttribute(), result.toString());
                                    }
                                }
                            }
                        }
                    } else {
                        position.removeAttribute(attribute.getAttribute());
                    }
                } catch (JexlException error) {
                    LOGGER.warn("Attribute computation error", error);
                } catch (ClassCastException error) {
                    LOGGER.warn("Attribute cast error", error);
                }
            }
        }
        callback.processed(false);
    }
@deprecated logic needs to be extracted to be used in API resource
src/main/java/org/traccar/handler/ComputedAttributesHandler.java