package net.java.cargotracker.domain.model.handling;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQuery;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.validation.constraints.NotNull;
import net.java.cargotracker.domain.model.cargo.Cargo;
import net.java.cargotracker.domain.model.location.Location;
import net.java.cargotracker.domain.model.voyage.Voyage;
import net.java.cargotracker.domain.shared.DomainObjectUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
/**
* A HandlingEvent is used to register the event when, for instance, a cargo is
* unloaded from a carrier at a some location at a given time.
* <p/>
* The HandlingEvent's are sent from different Incident Logging Applications
* some time after the event occurred and contain information about the null {@link net.java.cargotracker.domain.model.cargo.TrackingId},
* {@link net.java.cargotracker.domain.model.location.Location}, time stamp of
* the completion of the event, and possibly, if applicable a
* {@link net.java.cargotracker.domain.model.voyage.Voyage}.
* <p/>
* This class is the only member, and consequently the root, of the
* HandlingEvent aggregate.
* <p/>
* HandlingEvent's could contain information about a {@link Voyage} and if so,
* the event type must be either {@link Type#LOAD} or {@link Type#UNLOAD}.
* <p/>
* All other events must be of {@link Type#RECEIVE}, {@link Type#CLAIM} or
* {@link Type#CUSTOMS}.
*/
@Entity
@NamedQuery(name = "HandlingEvent.findByTrackingId",
query = "Select e from HandlingEvent e where e.cargo.trackingId = :trackingId")
public class HandlingEvent implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
private Long id;
@Enumerated(EnumType.STRING)
@NotNull
private Type type;
@ManyToOne
@JoinColumn(name = "voyage_id")
private Voyage voyage;
@ManyToOne
@JoinColumn(name = "location_id")
@NotNull
private Location location;
@Temporal(TemporalType.DATE)
@NotNull
@Column(name = "completion")
private Date completionTime;
@Temporal(TemporalType.DATE)
@NotNull
@Column(name = "registration")
private Date registrationTime;
@ManyToOne
@JoinColumn(name = "cargo_id")
@NotNull
private Cargo cargo;
/**
* Handling event type. Either requires or prohibits a carrier movement
* association, it's never optional.
*/
public enum Type {
// Loaded onto voyage from port location.
LOAD(true),
// Unloaded from voyage to port location
UNLOAD(true),
// Received by carrier
RECEIVE(false),
// Cargo claimed by recepient
CLAIM(false),
// Cargo went through customs
CUSTOMS(false);
private final boolean voyageRequired;
/**
* Private enum constructor.
*
* @param voyageRequired whether or not a voyage is associated with this
* event type
*/
private Type(boolean voyageRequired) {
this.voyageRequired = voyageRequired;
}
/**
* @return True if a voyage association is required for this event type.
*/
public boolean requiresVoyage() {
return voyageRequired;
}
/**
* @return True if a voyage association is prohibited for this event
* type.
*/
public boolean prohibitsVoyage() {
return !requiresVoyage();
}
public boolean sameValueAs(Type other) {
return other != null && this.equals(other);
}
}
public HandlingEvent() {
// Nothing to initialize.
}
/**
* @param cargo The cargo
* @param completionTime completion time, the reported time that the event
* actually happened (e.g. the receive took place).
* @param registrationTime registration time, the time the message is
* received
* @param type type of event
* @param location where the event took place
* @param voyage the voyage
*/
public HandlingEvent(Cargo cargo, Date completionTime,
Date registrationTime, Type type, Location location, Voyage voyage) {
Validate.notNull(cargo, "Cargo is required");
Validate.notNull(completionTime, "Completion time is required");
Validate.notNull(registrationTime, "Registration time is required");
Validate.notNull(type, "Handling event type is required");
Validate.notNull(location, "Location is required");
Validate.notNull(voyage, "Voyage is required");
if (type.prohibitsVoyage()) {
throw new IllegalArgumentException(
"Voyage is not allowed with event type " + type);
}
this.voyage = voyage;
this.completionTime = (Date) completionTime.clone();
this.registrationTime = (Date) registrationTime.clone();
this.type = type;
this.location = location;
this.cargo = cargo;
}
/**
* @param cargo cargo
* @param completionTime completion time, the reported time that the event
* actually happened (e.g. the receive took place).
* @param registrationTime registration time, the time the message is
* received
* @param type type of event
* @param location where the event took place
*/
public HandlingEvent(Cargo cargo, Date completionTime,
Date registrationTime, Type type, Location location) {
Validate.notNull(cargo, "Cargo is required");
Validate.notNull(completionTime, "Completion time is required");
Validate.notNull(registrationTime, "Registration time is required");
Validate.notNull(type, "Handling event type is required");
Validate.notNull(location, "Location is required");
if (type.requiresVoyage()) {
throw new IllegalArgumentException(
"Voyage is required for event type " + type);
}
this.completionTime = (Date) completionTime.clone();
this.registrationTime = (Date) registrationTime.clone();
this.type = type;
this.location = location;
this.cargo = cargo;
this.voyage = null;
}
public Type getType() {
return this.type;
}
public Voyage getVoyage() {
return DomainObjectUtils.nullSafe(this.voyage, Voyage.NONE);
}
public Date getCompletionTime() {
return new Date(this.completionTime.getTime());
}
public Date getRegistrationTime() {
return new Date(this.registrationTime.getTime());
}
public Location getLocation() {
return this.location;
}
public Cargo getCargo() {
return this.cargo;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
HandlingEvent event = (HandlingEvent) o;
return sameEventAs(event);
}
private boolean sameEventAs(HandlingEvent other) {
return other != null
&& new EqualsBuilder().append(this.cargo, other.cargo)
.append(this.voyage, other.voyage)
.append(this.completionTime, other.completionTime)
.append(this.location, other.location)
.append(this.type, other.type).isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder().append(cargo).append(voyage)
.append(completionTime).append(location).append(type)
.toHashCode();
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder("\n--- Handling event ---\n")
.append("Cargo: ").append(cargo.getTrackingId()).append("\n")
.append("Type: ").append(type).append("\n")
.append("Location: ").append(location.getName()).append("\n")
.append("Completed on: ").append(completionTime).append("\n")
.append("Registered on: ").append(registrationTime)
.append("\n");
if (voyage != null) {
builder.append("Voyage: ").append(voyage.getVoyageNumber())
.append("\n");
}
return builder.toString();
}
}