package net.java.cargotracker.domain.model.cargo;
import java.io.Serializable;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Embeddable;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
import javax.validation.constraints.Size;
import net.java.cargotracker.domain.model.handling.HandlingEvent;
import net.java.cargotracker.domain.model.location.Location;
import org.apache.commons.lang3.Validate;
import org.eclipse.persistence.annotations.PrivateOwned;
@Embeddable
public class Itinerary implements Serializable {
private static final long serialVersionUID = 1L;
private static final Date END_OF_DAYS = new Date(Long.MAX_VALUE);
// Null object pattern.
public static final Itinerary EMPTY_ITINERARY = new Itinerary();
// TODO Look into why cascade delete doesn't work.
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "cargo_id")
// TODO Index this is in leg_index
@OrderBy("loadTime")
@PrivateOwned
@Size(min = 1)
private List<Leg> legs = Collections.emptyList();
public Itinerary() {
// Nothing to initialize.
}
public Itinerary(List<Leg> legs) {
Validate.notEmpty(legs);
Validate.noNullElements(legs);
this.legs = legs;
}
public List<Leg> getLegs() {
return Collections.unmodifiableList(legs);
}
/**
* Test if the given handling event is expected when executing this
* itinerary.
*/
public boolean isExpected(HandlingEvent event) {
if (legs.isEmpty()) {
return true;
}
// TODO Convert this to a switch statement?
if (event.getType() == HandlingEvent.Type.RECEIVE) {
// Check that the first leg's origin is the event's location
Leg leg = legs.get(0);
return (leg.getLoadLocation().equals(event.getLocation()));
}
if (event.getType() == HandlingEvent.Type.LOAD) {
// Check that the there is one leg with same load location and
// voyage
for (Leg leg : legs) {
if (leg.getLoadLocation().sameIdentityAs(event.getLocation())
&& leg.getVoyage().sameIdentityAs(event.getVoyage())) {
return true;
}
}
return false;
}
if (event.getType() == HandlingEvent.Type.UNLOAD) {
// Check that the there is one leg with same unload location and
// voyage
for (Leg leg : legs) {
if (leg.getUnloadLocation().equals(event.getLocation())
&& leg.getVoyage().equals(event.getVoyage())) {
return true;
}
}
return false;
}
if (event.getType() == HandlingEvent.Type.CLAIM) {
// Check that the last leg's destination is from the event's
// location
Leg leg = getLastLeg();
return (leg.getUnloadLocation().equals(event.getLocation()));
}
// HandlingEvent.Type.CUSTOMS;
return true;
}
Location getInitialDepartureLocation() {
if (legs.isEmpty()) {
return Location.UNKNOWN;
} else {
return legs.get(0).getLoadLocation();
}
}
Location getFinalArrivalLocation() {
if (legs.isEmpty()) {
return Location.UNKNOWN;
} else {
return getLastLeg().getUnloadLocation();
}
}
/**
* @return Date when cargo arrives at final destination.
*/
Date getFinalArrivalDate() {
Leg lastLeg = getLastLeg();
if (lastLeg == null) {
return new Date(END_OF_DAYS.getTime());
} else {
return new Date(lastLeg.getUnloadTime().getTime());
}
}
/**
* @return The last leg on the itinerary.
*/
Leg getLastLeg() {
if (legs.isEmpty()) {
return null;
} else {
return legs.get(legs.size() - 1);
}
}
private boolean sameValueAs(Itinerary other) {
return other != null && legs.equals(other.legs);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Itinerary itinerary = (Itinerary) o;
return sameValueAs(itinerary);
}
@Override
public int hashCode() {
return legs.hashCode();
}
@Override
public String toString() {
return "Itinerary{" + "legs=" + legs + '}';
}
}