/*
* Copyright 2015 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.optaplanner.examples.coachshuttlegathering.domain.solver;
import java.util.List;
import java.util.Objects;
import org.optaplanner.core.impl.domain.variable.listener.VariableListener;
import org.optaplanner.core.impl.score.director.ScoreDirector;
import org.optaplanner.examples.coachshuttlegathering.domain.Bus;
import org.optaplanner.examples.coachshuttlegathering.domain.BusOrStop;
import org.optaplanner.examples.coachshuttlegathering.domain.BusStop;
import org.optaplanner.examples.coachshuttlegathering.domain.Shuttle;
import org.optaplanner.examples.coachshuttlegathering.domain.StopOrHub;
public class TransportTimeToHubUpdatingVariableListener implements VariableListener<BusOrStop> {
@Override
public void beforeEntityAdded(ScoreDirector scoreDirector, BusOrStop busOrStop) {
// Do nothing
}
@Override
public void afterEntityAdded(ScoreDirector scoreDirector, BusOrStop busOrStop) {
if (busOrStop instanceof BusStop) {
updateTransportTimeToHub(scoreDirector, (BusStop) busOrStop);
} else if (busOrStop instanceof Shuttle) {
updateTransportTimeToHubOfShuttle(scoreDirector, (Shuttle) busOrStop);
}
}
@Override
public void beforeVariableChanged(ScoreDirector scoreDirector, BusOrStop busOrStop) {
// Do nothing
}
@Override
public void afterVariableChanged(ScoreDirector scoreDirector, BusOrStop busOrStop) {
if (busOrStop instanceof BusStop) {
updateTransportTimeToHub(scoreDirector, (BusStop) busOrStop);
} else if (busOrStop instanceof Shuttle) {
updateTransportTimeToHubOfShuttle(scoreDirector, (Shuttle) busOrStop);
}
}
@Override
public void beforeEntityRemoved(ScoreDirector scoreDirector, BusOrStop busOrStop) {
// Do nothing
}
@Override
public void afterEntityRemoved(ScoreDirector scoreDirector, BusOrStop busOrStop) {
// Do nothing
}
protected void updateTransportTimeToHub(ScoreDirector scoreDirector, BusStop sourceStop) {
Bus bus = sourceStop.getBus();
Integer transportTimeToHub;
if (bus == null) {
transportTimeToHub = null;
} else {
StopOrHub destination = bus.getDestination();
if (destination instanceof BusStop // Also implies bus is a Shuttle because a Coach destination is a Hub
&& ((BusStop) destination).getBus() instanceof Shuttle) {
// A shuttle that follows a shuttle should have only transportTimeToHub null
transportTimeToHub = null;
} else {
StopOrHub next = sourceStop.getNextStop();
if (next != null) {
transportTimeToHub = next.getTransportTimeToHub();
} else if (destination != null) {
transportTimeToHub = destination.getTransportTimeToHub();
next = destination;
} else {
transportTimeToHub = null;
}
transportTimeToHub = addTransportTime(transportTimeToHub, sourceStop, next);
}
}
updateTransportTime(scoreDirector, sourceStop, bus, transportTimeToHub);
}
private void updateTransportTime(ScoreDirector scoreDirector, BusStop sourceStop, Bus bus, Integer transportTimeToHub) {
if (Objects.equals(sourceStop.getTransportTimeToHub(), transportTimeToHub)) {
return;
}
scoreDirector.beforeVariableChanged(sourceStop, "transportTimeToHub");
sourceStop.setTransportTimeToHub(transportTimeToHub);
scoreDirector.afterVariableChanged(sourceStop, "transportTimeToHub");
updateTransportTimeForTransferShuttleList(scoreDirector, sourceStop, bus);
BusStop toStop = sourceStop;
for (BusOrStop busOrStop = sourceStop.getPreviousBusOrStop(); busOrStop instanceof BusStop;) {
BusStop stop = (BusStop) busOrStop;
transportTimeToHub = addTransportTime(transportTimeToHub, stop, toStop);
scoreDirector.beforeVariableChanged(stop, "transportTimeToHub");
stop.setTransportTimeToHub(transportTimeToHub);
scoreDirector.afterVariableChanged(stop, "transportTimeToHub");
updateTransportTimeForTransferShuttleList(scoreDirector, stop, bus);
toStop = stop;
busOrStop = stop.getPreviousBusOrStop();
}
}
private void updateTransportTimeForTransferShuttleList(ScoreDirector scoreDirector, BusStop parentStop,
Bus parentBus) {
List<Shuttle> transferShuttleList = parentStop.getTransferShuttleList();
if (transferShuttleList.isEmpty()) {
return;
}
Integer parentTransportTimeToHub = parentStop.getTransportTimeToHub();
if (parentBus instanceof Shuttle) {
// Avoid stack overflow if 2 shuttles bite each others tail or if 1 shuttle bites its own tail
parentTransportTimeToHub = null;
}
for (Shuttle shuttle : transferShuttleList) {
updateTransportTimeToHubOfShuttle(scoreDirector, parentStop, parentTransportTimeToHub, shuttle);
}
}
private void updateTransportTimeToHubOfShuttle(ScoreDirector scoreDirector, Shuttle shuttle) {
StopOrHub destination = shuttle.getDestination();
Integer destinationTransportTimeToHub;
if (destination != null) {
if (destination instanceof BusStop
&& ((BusStop) destination).getBus() instanceof Shuttle) {
// A shuttle that follows a shuttle should have only transportTimeToHub null
destinationTransportTimeToHub = null;
} else {
destinationTransportTimeToHub = destination.getTransportTimeToHub();
}
} else {
destinationTransportTimeToHub = null;
}
updateTransportTimeToHubOfShuttle(scoreDirector, destination, destinationTransportTimeToHub, shuttle);
}
private void updateTransportTimeToHubOfShuttle(ScoreDirector scoreDirector, StopOrHub parentStop, Integer parentTransportTimeToHub, Shuttle shuttle) {
if (shuttle.getNextStop() == null) {
return;
}
BusStop lastStop = null;
for (BusStop stop = shuttle.getNextStop(); stop != null; stop = stop.getNextStop()) {
lastStop = stop;
}
Integer transportTimeToHub = parentTransportTimeToHub;
transportTimeToHub = addTransportTime(transportTimeToHub, lastStop, parentStop);
updateTransportTime(scoreDirector, lastStop, shuttle, transportTimeToHub);
}
private static Integer addTransportTime(Integer transportTimeToHub, BusStop fromStop, StopOrHub toStop) {
if (transportTimeToHub == null) {
return null;
}
return transportTimeToHub + fromStop.getBus().getDurationFromTo(fromStop.getLocation(), toStop.getLocation());
}
}