package net.java.cargotracker.domain.model.voyage; import java.io.Serializable; import java.util.ArrayList; import java.util.Date; import java.util.List; import javax.persistence.Embedded; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.NamedQuery; import javax.validation.constraints.NotNull; import net.java.cargotracker.domain.model.location.Location; import org.apache.commons.lang3.Validate; @Entity @NamedQuery(name = "Voyage.findByVoyageNumber", query = "Select v from Voyage v where v.voyageNumber = :voyageNumber") public class Voyage implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue private Long id; @Embedded @NotNull private VoyageNumber voyageNumber; @Embedded @NotNull private Schedule schedule; // Null object pattern public static final Voyage NONE = new Voyage(new VoyageNumber(""), Schedule.EMPTY); public Voyage() { // Nothing to initialize } public Voyage(VoyageNumber voyageNumber, Schedule schedule) { Validate.notNull(voyageNumber, "Voyage number is required"); Validate.notNull(schedule, "Schedule is required"); this.voyageNumber = voyageNumber; this.schedule = schedule; } public VoyageNumber getVoyageNumber() { return voyageNumber; } public Schedule getSchedule() { return schedule; } @Override public int hashCode() { return voyageNumber.hashCode(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null) { return false; } if (!(o instanceof Voyage)) { return false; } Voyage that = (Voyage) o; return sameIdentityAs(that); } public boolean sameIdentityAs(Voyage other) { return other != null && this.getVoyageNumber().sameValueAs(other.getVoyageNumber()); } @Override public String toString() { return "Voyage " + voyageNumber; } /** * Builder pattern is used for incremental construction of a Voyage * aggregate. This serves as an aggregate factory. */ public static class Builder { private List<CarrierMovement> carrierMovements = new ArrayList<>(); private VoyageNumber voyageNumber; private Location departureLocation; public Builder(VoyageNumber voyageNumber, Location departureLocation) { Validate.notNull(voyageNumber, "Voyage number is required"); Validate.notNull(departureLocation, "Departure location is required"); this.voyageNumber = voyageNumber; this.departureLocation = departureLocation; } public Builder addMovement(Location arrivalLocation, Date departureTime, Date arrivalTime) { carrierMovements.add(new CarrierMovement(departureLocation, arrivalLocation, departureTime, arrivalTime)); // Next departure location is the same as this arrival location this.departureLocation = arrivalLocation; return this; } public Voyage build() { return new Voyage(voyageNumber, new Schedule(carrierMovements)); } } }