Order.java
package org.example.customer;
/*
* This is free and unencumbered software released into the public domain.
* Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software,
* either in source code form or as a compiled binary, for any purpose, commercial or
* non-commercial, and by any means.
*
* In jurisdictions that recognize copyright laws, the author or authors of this
* software dedicate any and all copyright interest in the software to the public domain.
* We make this dedication for the benefit of the public at large and to the detriment of
* our heirs and successors. We intend this dedication to be an overt act of relinquishment in
* perpetuity of all present and future rights to this software under copyright law.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* For more information, please refer to: https://unlicense.org/
*/
import java.io.Serializable;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.example.customer.utility.CustomerEntity;
import org.example.websecurity.XssSanitizer;
import org.example.websecurity.XssSanitizerImpl;
/**
* The Order Entity for the Customer application.
* <br>
* <br>
* This class represents the following DB Table:
*
* <pre>
* CREATE TABLE ORDERS (
* ID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),
* ORDER_DATE DATE NOT NULL,
* CUSTOMER_ID INTEGER,
* TOTAL_AMOUNT DECIMAL(12,2),
* ORDER_NUMBER VARCHAR(10),
* CONSTRAINT PK_ORDER PRIMARY KEY (ID)
* );
* </pre>
*
* @author Jonathan Earl
* @version 1.0
*
*/
public final class Order extends CustomerEntity
implements Serializable, Comparable<Order>
{
private static final long serialVersionUID = 1L;
private static final Logger LOG = LogManager.getLogger();
private LocalDate myOrderDate;
private int myCustomerId;
private double myTotalAmount;
private String myOrderNumber;
private XssSanitizer mySanitizer;
private List<OrderItem> myOrderItems;
/**
* The default constructor for the Order class.
* <br>
* <br>
* The initial values are:
* <ul>
* <li>id: 1</li>
* <li>orderDate: Current Date</li>
* <li>customerId: 1</li>
* <li>totalAmount: 0.00</li>
* <li>orderNumber: null</li>
* <li>orderItems: empty</li>
* </ul>
*/
public Order()
{
this(new XssSanitizerImpl());
LOG.debug("Finishing the default Constructor");
}
/**
* The overloaded constructor for the order class that takes an XssSanitizer as input.
* <p>
* The initial values are:
* <ul>
* <li>id: 1</li>
* <li>orderDate: Current Date</li>
* <li>customerId: 1</li>
* <li>totalAmount: 0.00</li>
* <li>orderNumber: null</li>
* <li>orderItems: empty</li>
* </ul>
*
* @param sanitizer the XssSanitizer used by this instance
*/
public Order(final XssSanitizer sanitizer)
{
LOG.debug("Starting the overloaded Constructor");
final int initialId = 1;
final LocalDate initialOrderDate = LocalDate.now();
final int initialCustomerId = 1;
final String initialOrderNumber = null;
final List<OrderItem> initalOrderItems = new ArrayList<OrderItem>();
mySanitizer = sanitizer;
setId(initialId);
setOrderDate(initialOrderDate);
setCustomerId(initialCustomerId);
setOrderNumber(initialOrderNumber);
setOrderItems(initalOrderItems);
}
/**
* This will enable sorting of Order by Order number concatenated with Order Date.
* <br>
* <br>
* @param other the Order object to compare with
* @return the sort value of negative/zero/positive
*/
@Override
public int compareTo(Order other)
{
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM-dd-yyyy");
String thisSortValue = this.getOrderNumber() + " "
+ this.getOrderDate().format(formatter);
String otherSortValue = other.getOrderNumber() + " "
+ other.getOrderDate().format(formatter);
return thisSortValue.compareToIgnoreCase(otherSortValue);
}
/**
* Returns the orderDate value for the Order.
*
* @return the orderDate value for the order
*/
public LocalDate getOrderDate()
{
LOG.debug("returning the Oder Date: " + myOrderDate);
return myOrderDate;
}
/**
* Sets the order date value for the Customer.
* <p>
* The business rules are:
* <ul>
* <li>the order date must <strong>not</strong> be null</li>
* <li>the order date must <strong>today or earlier</strong></li>
* </ul>
*
* @param orderDate the value to set into the orderDate field
* @throws IllegalArgumentException if the first name is invalid
*/
public void setOrderDate(final LocalDate orderDate)
{
LOG.debug("setting the order Date");
if (orderDate == null)
{
LOG.error("Order Date must not be null");
throw new IllegalArgumentException("Order Date must not be null");
}
LocalDate tomorrow = LocalDate.now().plusDays(1);
if (orderDate.isBefore(tomorrow))
{
this.myOrderDate = orderDate;
}
else
{
LOG.error("OrderDate must be today or earlier");
throw new IllegalArgumentException("OrderDate must be today or earlier");
}
}
/**
* Returns the id value for the Customer Id.
*
* @return the id value for the customer id
*/
public int getCustomerId()
{
LOG.debug("returning the CustomerId: " + myCustomerId);
return myCustomerId;
}
/**
* Sets the id value for the Customer ID.
* <p>
* The business rules are:
* <ul>
* <li>the id must be 1 or greater</li>
* </ul>
*
* @param customerId the value to set into the customer id field
* @throws IllegalArgumentException if the customerId is invalid
*/
public void setCustomerId(final int customerId)
{
LOG.debug("setting the CustomerId: " + customerId);
final int min = 1;
if (customerId < min)
{
LOG.error("CustomerId must be greater then zero");
throw new IllegalArgumentException("CustomerId must be greater then zero");
}
this.myCustomerId = customerId;
}
/**
* Returns the total amount for the order.
*
* @return the totalAmount value for the order
*/
public double getTotalAmount()
{
LOG.debug("returning the totalAmount: " + myTotalAmount);
return myTotalAmount;
}
/**
* Sets the total amount value for the Orde.
* <p>
* The business rules are:
* <ul>
* <li>the TotalAmount must be 0.01 or greater</li>
* </ul>
*
* @param totalAmount the value to set into the totalAmount field
* @throws IllegalArgumentException if the totalAmount is invalid
*/
public void setTotalAmount(final double totalAmount)
{
LOG.debug("setting the totalAmount: " + totalAmount);
final double min = 0.01;
if (totalAmount < min)
{
LOG.error("totalAmount must be greater then zero");
throw new IllegalArgumentException("totalAmount must be greater then zero");
}
this.myTotalAmount = totalAmount;
}
/**
* Returns the orderNumber value for the Order.
*
* @return the orderNumber value for the order
*/
public String getOrderNumber()
{
LOG.debug("returning the OrderNumber: " + myOrderNumber);
return myOrderNumber;
}
/**
* Sets the orderNumber value for the Order.
* <p>
* The business rules are:
* <ul>
* <li>the orderNumber <strong>may</strong> be null</li>
* <li>the orderNumber must <strong>not</strong> be empty</li>
* <li>the orderNumber must min length of 2 chars</li>
* <li>the orderNumber must max length of 10 chars</li>
* <li>XSS strings within the orderNumber will be removed</li>
* </ul>
*
* @param orderNumber the value to set into the Order Number field
* @throws IllegalArgumentException if the orderNumber is invalid
*/
public void setOrderNumber(final String orderNumber)
{
LOG.debug("setting the OrderNumber");
final int max = 10;
final int min = 2;
if (orderNumber == null)
{
LOG.debug("orderNumber is set to null");
this.myOrderNumber = null;
return;
}
String safeOrderNumber = mySanitizer.sanitizeInput(orderNumber);
if (safeOrderNumber.isEmpty())
{
LOG.error("OrderNumber must not be empty");
throw new IllegalArgumentException("OrderNumber must not be empty");
}
if (safeOrderNumber.length() > max || safeOrderNumber.length() < min)
{
LOG.error("OrderNumber must be between 2 and 10 chars in length");
throw new IllegalArgumentException("OrderNumber must be between 2 and 10 chars in length");
}
LOG.debug("setting the OrderNumber to: " + safeOrderNumber);
this.myOrderNumber = safeOrderNumber;
}
/**
* Returns the orderItem list value for the Order.
*
* @return the myOrderItems for the order
*/
public List<OrderItem> getOrderItems()
{
LOG.debug("returning the OrderItems: " + myOrderItems);
return myOrderItems;
}
/**
* Sets the orderItem list value for the Order.
* <p>
* The business rules are:
* <ul>
* <li>the orderItems must <strong>not</strong> be null</li>
* <li>the orderItems may be empty</li>
* </ul>
*
* @param orderItems the list to set into the orderItems
* @throws IllegalArgumentException if the orderItems is invalid
*/
public void setOrderItems(final List<OrderItem> orderItems)
{
LOG.debug("setting the Orderitems");
if (orderItems == null)
{
LOG.error("Orderitems must not be null");
throw new IllegalArgumentException("Orderitems must not be null");
}
double total = 0.0;
for (OrderItem current : orderItems)
{
total += current.getSubTotal();
}
this.myTotalAmount = total;
this.myOrderItems = orderItems;
}
/**
* The hashCode() method of the Order class.
* <p>
* <strong>This method uses:</strong>
* <ul>
* <li>id</li>
* <li>order date</li>
* <li>customer id</li>
* <li>order number</li>
* </ul>
*
* @see java.lang.Object#hashCode()
* @return the hashCode value for this Order object
*/
@Override
public int hashCode()
{
LOG.debug("building HashCode");
return new HashCodeBuilder()
.append(getId())
.append(myOrderDate)
.append(myCustomerId)
.append(myOrderNumber)
.toHashCode();
}
/**
* The equals() method of the order class.
* <p>
* <strong>This method uses:</strong>
* <ul>
* <li>id</li>
* <li>order date</li>
* <li>customer id</li>
* <li>order number</li>
* </ul>
*
* @see java.lang.Object#equals(Object obj)
* @param obj the incoming object to compare against
* @return true if the fields being compared are equal
*/
@Override
public boolean equals(final Object obj)
{
LOG.debug("checking equals");
if (obj instanceof Order)
{
final Order other = (Order) obj;
return new EqualsBuilder()
.append(getId(), other.getId())
.append(myOrderDate, other.myOrderDate)
.append(myCustomerId, other.myCustomerId)
.append(myOrderNumber, other.myOrderNumber)
.isEquals();
}
else
{
return false;
}
}
/**
* The toString method for the Order class.
*
* this method will return:<br>
* Order [Id=xxx, OrderDate=xx/xx/xx, CustomerId=xxx,
* TotalAmount=xxx, OrderNumber=xxx]
*/
@Override
public String toString()
{
return "Order [Id=" + getId() + ", OrderDate=" + myOrderDate + ", CustomerId=" + myCustomerId
+ ", TotalAmount=" + myTotalAmount + ", OrderNumber=" + myOrderNumber
+ ", OrderItems=" + myOrderItems.size() + "]";
}
}