*     unit is thrown off it.
     */
    private boolean followTradeRoute(Unit unit) {
        Player player = unit.getOwner();
        TradeRoute tr = unit.getTradeRoute();
        String name = tr.getName();
        List<ModelMessage> messages = new ArrayList<ModelMessage>();
        boolean detailed = freeColClient.getClientOptions()
            .getBoolean(ClientOptions.SHOW_GOODS_MOVEMENT);
        final List<Stop> stops = tr.getStops();
        Stop stop;
        boolean result = false;
        int tries = stops.size();

        for (;;) {
            stop = unit.getStop();
            // Complain and return if the stop is no longer valid.
            if (!TradeRoute.isStopValid(unit, stop)) {
                messages.add(new ModelMessage(MessageType.GOODS_MOVEMENT,
                        "traderoute.broken", unit)
                    .addName("%route%", name));
                clearOrders(unit);
                result = true;
                break;
            }

            // Is the unit at the stop already?
            boolean atStop;
            if (stop.getLocation() instanceof Europe) {
                atStop = unit.isInEurope();
            } else if (stop.getLocation() instanceof Colony) {
                atStop = unit.getTile() == stop.getLocation().getTile();
            } else {
                throw new IllegalStateException("Bogus stop location: "
                    + (FreeColGameObject) stop.getLocation());
            }
            if (atStop) {
                // Anything to unload?
                unloadUnitAtStop(unit, (detailed) ? messages : null);

                // Anything to load?
                loadUnitAtStop(unit, (detailed) ? messages : null);

                // If the un/load consumed the moves, break now before
                // updating the stop.  This allows next move to arrive
                // here again having taken a second shot at
                // un/loading, but this time should not have consumed
                // the moves.
                if (unit.getMovesLeft() <= 0) break;

                // Update the stop.
                int index = unit.validateCurrentStop();
                askServer().updateCurrentStop(unit);
                
                // Check if we have tried all the stops.  If so, this
                // means there is no work to do anywhere in the whole
                // trade route.  Skip the unit if so.
                // Otherwise optionally add messages notifying that a
                // stop has been skipped.
                int next = unit.validateCurrentStop();
                for (;;) {
                    if (++index >= stops.size()) index = 0;
                    tries--;
                    if (index == next) break;
                    if (detailed) {
                        Location loc = stops.get(index).getLocation();
                        messages.add(new ModelMessage(MessageType.GOODS_MOVEMENT,
                                "traderoute.skipStop", unit)
                            .addName("%route%", name)
                            .addStringTemplate("%unit%",
                                Messages.getLabel(unit))
                            .addStringTemplate("%location%",
                                loc.getLocationNameFor(player)));
                    }
                }
                if (tries < 0) {
                    Location loc = stop.getLocation();
                    if (detailed) { 
                        int i = 0; // Drop skip-messages if skipping them all.
                        while (i < messages.size()) {
                            ModelMessage m = messages.get(i);
                            if (m.getId().equals("traderoute.skipStop")) {
                                messages.remove(i);
                            } else {
                                i++;
                            }
                        }
                        messages.add(new ModelMessage(MessageType.GOODS_MOVEMENT,
                                "traderoute.noWork", unit)
                            .addName("%route%", name)
                            .addStringTemplate("%unit%",
                                Messages.getLabel(unit))
                            .addStringTemplate("%location%",
                                loc.getLocationNameFor(player)));
                    }
                    unit.setState(UnitState.SKIPPED);
                    break;
                }

                continue; // Stop was updated, loop.
            }

            // Not at stop, give up if no moves left.
            if (unit.getMovesLeft() <= 0
                || unit.getState() == UnitState.SKIPPED) {
                break;
            }

            // Find a path to the stop.  Skip if none.
            Location destination = stop.getLocation();
            PathNode path = unit.findPath(destination);
            if (path == null) {
                StringTemplate dest = destination.getLocationNameFor(player);
                messages.add(new ModelMessage(MessageType.GOODS_MOVEMENT,
                        "traderoute.noPath", unit)
                    .addName("%route%", name)
                    .addStringTemplate("%unit%", Messages.getLabel(unit))
                    .addStringTemplate("%location%", dest));
                unit.setState(UnitState.SKIPPED);
                break;
            }

            // Try to follow the path.
            // Ignore the result, check for unload before returning.
            followPath(unit, path);
        }

        for (ModelMessage m : messages) player.addModelMessage(m);
        return result;
    }