class CargoTrackingDTOConverter {
    private static final DateTimeFormatter formatter = DateTimeFormatter
            .ofLocalizedDateTime(FormatStyle.MEDIUM)
            .withZone(ZoneOffset.UTC);

    public static CargoTrackingDTO convert(Cargo cargo, List<HandlingEvent> handlingEvents, MessageSource messageSource, Locale locale) {
        List<HandlingEventDTO> handlingEventDTOs = convertHandlingEvents(handlingEvents, cargo, messageSource, locale);
        return new CargoTrackingDTO(
                convertTrackingId(cargo),
                convertStatusText(cargo, messageSource, locale),
                convertDestination(cargo),
                convertEta(cargo),
                convertNextExpectedActivity(cargo),
                convertIsMisdirected(cargo),
                handlingEventDTOs);
    }

    private static List<HandlingEventDTO> convertHandlingEvents(List<HandlingEvent> handlingEvents, Cargo cargo, MessageSource messageSource, Locale locale) {
        return handlingEvents.stream().map(he -> new HandlingEventDTO(
                convertLocation(he),
                convertTime(he),
                convertType(he),
                convertVoyageNumber(he),
                convertIsExpected(he, cargo),
                convertDescription(he, messageSource, locale)
        )).collect(Collectors.toList());
    }

    protected static String convertDescription(HandlingEvent handlingEvent, MessageSource messageSource, Locale locale) {
        Object[] args;

        switch (handlingEvent.type()) {
            case LOAD:
            case UNLOAD:
                args = new Object[]{
                        handlingEvent.voyage().voyageNumber().idString(),
                        handlingEvent.location().name(),
                        formatter.format(handlingEvent.completionTime())
                };
                break;
            case RECEIVE:
            case CUSTOMS:
            case CLAIM:
                args = new Object[]{
                        handlingEvent.location().name(),
                        formatter.format(handlingEvent.completionTime())
                };
                break;

            default:
                args = new Object[]{};
        }

        String key = "deliveryHistory.eventDescription." + handlingEvent.type().name();

        return messageSource.getMessage(key, args, locale);
    }

    private static boolean convertIsExpected(HandlingEvent handlingEvent, Cargo cargo) {
        return cargo.itinerary().isExpected(handlingEvent);
    }

    private static String convertVoyageNumber(HandlingEvent handlingEvent) {
        return handlingEvent.voyage().voyageNumber().idString();
    }

    private static String convertType(HandlingEvent handlingEvent) {
        return handlingEvent.type().toString();
    }

    private static String convertTime(HandlingEvent handlingEvent) {
        return formatter
                .format(handlingEvent.completionTime());
    }

    private static String convertLocation(HandlingEvent handlingEvent) {
        return handlingEvent.location().name();
    }

    private static String convertTrackingId(Cargo cargo) {
        return cargo.trackingId().idString();
    }

    protected static String convertStatusText(Cargo cargo, MessageSource messageSource, Locale locale) {
        final Delivery delivery = cargo.delivery();
        final String code = "cargo.status." + delivery.transportStatus().name();

        final Object[] args;
        switch (delivery.transportStatus()) {
            case IN_PORT:
                args = new Object[]{delivery.lastKnownLocation().name()};
                break;
            case ONBOARD_CARRIER:
                args = new Object[]{delivery.currentVoyage().voyageNumber().idString()};
                break;
            case CLAIMED:
            case NOT_RECEIVED:
            case UNKNOWN:
            default:
                args = null;
                break;
        }

        return messageSource.getMessage(code, args, "[Unknown status]", locale);
    }

    private static String convertDestination(Cargo cargo) {
        return cargo.routeSpecification().destination().name();
    }

    private static String convertEta(Cargo cargo) {
        Instant date = cargo.delivery().estimatedTimeOfArrival();
        return date == null ? "Unknown" : formatter.format(date);
    }

    protected static String convertNextExpectedActivity(Cargo cargo) {<FILL_FUNCTION_BODY>}

    private static boolean convertIsMisdirected(Cargo cargo) {
        return cargo.delivery().isMisdirected();
    }
}

        HandlingActivity activity = cargo.delivery().nextExpectedActivity();
        if (activity == null) {
            return "";
        }

        String text = "Next expected activity is to ";
        HandlingEvent.Type type = activity.type();
        if (type.sameValueAs(HandlingEvent.Type.LOAD)) {
            return
                    text + type.name().toLowerCase() + " cargo onto voyage " + activity.voyage().voyageNumber() +
                            " in " + activity.location().name();
        } else if (type.sameValueAs(HandlingEvent.Type.UNLOAD)) {
            return
                    text + type.name().toLowerCase() + " cargo off of " + activity.voyage().voyageNumber() +
                            " in " + activity.location().name();
        } else {
            return text + type.name().toLowerCase() + " cargo in " + activity.location().name();
        }
    
<no_super_class>