class TimeWindowedCustomer extends Customer {

    // Times are multiplied by 1000 to avoid floating point arithmetic rounding errors
    private long readyTime;
    private long dueTime;
    private long serviceDuration;

    // Shadow variable
    private Long arrivalTime;

    public TimeWindowedCustomer() {
    }

    public TimeWindowedCustomer(long id, Location location, int demand, long readyTime, long dueTime, long serviceDuration) {
        super(id, location, demand);
        this.readyTime = readyTime;
        this.dueTime = dueTime;
        this.serviceDuration = serviceDuration;
    }

    /**
     * @return a positive number, the time multiplied by 1000 to avoid floating point arithmetic rounding errors
     */
    public long getReadyTime() {
        return readyTime;
    }

    public void setReadyTime(long readyTime) {
        this.readyTime = readyTime;
    }

    /**
     * @return a positive number, the time multiplied by 1000 to avoid floating point arithmetic rounding errors
     */
    public long getDueTime() {
        return dueTime;
    }

    public void setDueTime(long dueTime) {
        this.dueTime = dueTime;
    }

    /**
     * @return a positive number, the time multiplied by 1000 to avoid floating point arithmetic rounding errors
     */
    public long getServiceDuration() {
        return serviceDuration;
    }

    public void setServiceDuration(long serviceDuration) {
        this.serviceDuration = serviceDuration;
    }

    /**
     * @return a positive number, the time multiplied by 1000 to avoid floating point arithmetic rounding errors
     */
    // Arguable, to adhere to API specs (although this works), nextCustomer should also be a source,
    // because this shadow must be triggered after nextCustomer (but there is no need to be triggered by nextCustomer)
    @ShadowVariable(variableListenerClass = ArrivalTimeUpdatingVariableListener.class, sourceVariableName = "vehicle")
    @ShadowVariable(variableListenerClass = ArrivalTimeUpdatingVariableListener.class, sourceVariableName = "previousCustomer")
    public Long getArrivalTime() {
        return arrivalTime;
    }

    public void setArrivalTime(Long arrivalTime) {
        this.arrivalTime = arrivalTime;
    }

    // ************************************************************************
    // Complex methods
    // ************************************************************************

    /**
     * @return a positive number, the time multiplied by 1000 to avoid floating point arithmetic rounding errors
     */
    public Long getDepartureTime() {
        if (arrivalTime == null) {
            return null;
        }
        return Math.max(arrivalTime, readyTime) + serviceDuration;
    }

    public boolean isArrivalBeforeReadyTime() {
        return arrivalTime != null
                && arrivalTime < readyTime;
    }

    public boolean isArrivalAfterDueTime() {
        return arrivalTime != null
                && dueTime < arrivalTime;
    }

    /**
     * @return a positive number, the time multiplied by 1000 to avoid floating point arithmetic rounding errors
     */
    public long getTimeWindowGapTo(TimeWindowedCustomer other) {<FILL_FUNCTION_BODY>}

}

        // dueTime doesn't account for serviceDuration
        long latestDepartureTime = dueTime + serviceDuration;
        long otherLatestDepartureTime = other.getDueTime() + other.getServiceDuration();
        if (latestDepartureTime < other.getReadyTime()) {
            return other.getReadyTime() - latestDepartureTime;
        }
        if (otherLatestDepartureTime < readyTime) {
            return readyTime - otherLatestDepartureTime;
        }
        return 0L;
    
<methods>public void <init>() ,public void <init>(long, org.optaplanner.examples.vehiclerouting.domain.location.Location, int) ,public int getDemand() ,public long getDistanceFromPreviousStandstill() ,public long getDistanceToDepot() ,public org.optaplanner.examples.vehiclerouting.domain.location.Location getLocation() ,public org.optaplanner.examples.vehiclerouting.domain.Customer getNextCustomer() ,public org.optaplanner.examples.vehiclerouting.domain.Customer getPreviousCustomer() ,public org.optaplanner.examples.vehiclerouting.domain.Vehicle getVehicle() ,public void setDemand(int) ,public void setLocation(org.optaplanner.examples.vehiclerouting.domain.location.Location) ,public void setNextCustomer(org.optaplanner.examples.vehiclerouting.domain.Customer) ,public void setPreviousCustomer(org.optaplanner.examples.vehiclerouting.domain.Customer) ,public void setVehicle(org.optaplanner.examples.vehiclerouting.domain.Vehicle) ,public java.lang.String toString() <variables>protected int demand,protected org.optaplanner.examples.vehiclerouting.domain.location.Location location,protected org.optaplanner.examples.vehiclerouting.domain.Customer nextCustomer,protected org.optaplanner.examples.vehiclerouting.domain.Customer previousCustomer,protected org.optaplanner.examples.vehiclerouting.domain.Vehicle vehicle