Product.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 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;
//CREATE TABLE PRODUCT (
// ID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),
// PRODUCT_NAME VARCHAR(50) NOT NULL,
// SUPPLIER_ID INTEGER NOT NULL,
// UNIT_PRICE DECIMAL(12,2),
// PACKAGE VARCHAR(30),
// IS_DISCONTINUED BOOLEAN,
// CONSTRAINT PK_PRODUCT PRIMARY KEY (ID)
// );
/**
* The Product Entity for the Customer application.
* <p>
* This class represents the following DB Table:
*
* <pre>
* CREATE TABLE PRODUCT (
* ID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),
* PRODUCT_NAME VARCHAR(50) NOT NULL,
* SUPPLIER_ID INTEGER NOT NULL,
* UNIT_PRICE DECIMAL(12,2),
* PACKAGE VARCHAR(30),
* IS_DISCONTINUED BOOLEAN,
* CONSTRAINT PK_PRODUCT PRIMARY KEY (ID)
* );
* </pre>
*
* @author Jonathan Earl
* @version 1.0
*
*/
public final class Product extends CustomerEntity
implements Serializable, Comparable<Product>
{
private static final long serialVersionUID = 1L;
private static final Logger LOG = LogManager.getLogger();
private String myProductName;
private int mySupplierId;
private double myUnitPrice;
private String myPackaging;
private boolean myDiscontinued;
private XssSanitizer mySanitizer;
/**
* The default constructor for the Product class.
* <p>
* The initial values are:
* <ul>
* <li>id: 1</li>
* <li>product name: Paper Envelope</li>
* <li>supplier id: 1</li>
* <li>unit price: 0.01</li>
* <li>packaging: single</li>
* <li>discontinued: false</li>
* </ul>
*/
public Product()
{
this(new XssSanitizerImpl());
LOG.debug("Finishing the default Constructor");
}
/**
* The overloaded constructor for the Product class that takes an XssSanitizer as input.
* <p>
* The initial values are:
* <ul>
* <li>id: 1</li>
* <li>product name: Paper Envelope</li>
* <li>supplier id: 1</li>
* <li>unit price: 0.01</li>
* <li>packaging: single</li>
* <li>discontinued: false</li>
* </ul>
*
* @param sanitizer the XssSanitizer used by this instance
*/
public Product(final XssSanitizer sanitizer)
{
LOG.debug("Starting the overloaded Constructor");
final int initialId = 1;
final String initialProductName = "Paper Envelope";
final int initialSupplierId = 1;
final double initialUnitPrice = 0.01;
final String initialPackaging = "single";
final boolean initialDiscontinued = false;
mySanitizer = sanitizer;
setId(initialId);
setProductName(initialProductName);
setSupplierId(initialSupplierId);
setUnitPrice(initialUnitPrice);
setPackaging(initialPackaging);
setDiscontinued(initialDiscontinued);
}
/**
* This will enable sorting of Products by name.
* <br>
* <br>
* @param other the Product object to compare with
* @return the sort value of negative/zero/positive
*/
@Override
public int compareTo(Product other)
{
return this.getProductName().compareToIgnoreCase(other.getProductName());
}
/**
* Returns the product name value for the Product.
*
* @return the product name value for the Product
*/
public String getProductName()
{
LOG.debug("returning the Product Name: " + myProductName);
return myProductName;
}
/**
* Sets the product name value for the Product.
* <p>
* The business rules are:
* <ul>
* <li>the product name must <strong>not</strong> be null</li>
* <li>the product name must <strong>not</strong> be empty</li>
* <li>the product name must max length of 50 chars</li>
* <li>XSS strings within the product name will be removed</li>
* </ul>
*
* @param productName the value to set into the product name field
* @throws IllegalArgumentException if the product name is invalid
*/
public void setProductName(final String productName)
{
LOG.debug("setting the Product Name");
final int max = 50;
if (productName == null)
{
LOG.error("Product Name must not be null");
throw new IllegalArgumentException("Product Name must not be null");
}
String safeProductName = mySanitizer.sanitizeInput(productName);
if (safeProductName.isEmpty())
{
LOG.error("Product Name must not be empty");
throw new IllegalArgumentException("Product Name must not be empty");
}
if (safeProductName.length() > max)
{
LOG.error("Product Name must be up to 50 chars in length");
throw new IllegalArgumentException("Product Name must be up to 50 chars in length");
}
LOG.debug("setting the Product Name to: " + safeProductName);
this.myProductName = safeProductName;
}
/**
* Returns the SupplierId value for the Product.
*
* @return the supplierId value for the Product
*/
public int getSupplierId()
{
LOG.debug("returning the SupplierId: " + mySupplierId);
return mySupplierId;
}
/**
* Sets the SupplierId value for the Product.
* <p>
* The business rules are:
* <ul>
* <li>the SupplierId must be 1 or greater</li>
* </ul>
*
* @param supplierId the value to set into the supplier id field
* @throws IllegalArgumentException if the id is invalid
*/
public void setSupplierId(final int supplierId)
{
LOG.debug("setting the SuppliierId: " + supplierId);
final int min = 1;
if (supplierId < min)
{
LOG.error("SupplierId must be greater then zero");
throw new IllegalArgumentException("SupplierId must be greater then zero");
}
this.mySupplierId = supplierId;
}
/**
* Returns the UnitPrice value for the Product.
*
* @return the UnitPrice value for the Product
*/
public double getUnitPrice()
{
LOG.debug("returning the UnitPrice: " + myUnitPrice);
return myUnitPrice;
}
/**
* Sets the UnitPrice value for the Product.
* <p>
* The business rules are:
* <ul>
* <li>the UnitPrice must be 0.01 or greater</li>
* </ul>
*
* @param unitPrice the value to set into the unitPrice field
* @throws IllegalArgumentException if the UnitPrice is invalid
*/
public void setUnitPrice(final double unitPrice)
{
LOG.debug("setting the unitPrice: " + unitPrice);
final double min = 0.01;
if (unitPrice < min)
{
LOG.error("unitPrice must be greater then zero");
throw new IllegalArgumentException("unitPrice must be greater then zero");
}
this.myUnitPrice = unitPrice;
}
/**
* Returns the packaging value for the Product.
*
* @return the packaging value for the Product
*/
public String getPackaging()
{
LOG.debug("returning the Packaging: " + myPackaging);
return myPackaging;
}
/**
* Sets the packaging value for the Product.
* <p>
* The business rules are:
* <ul>
* <li>the packaging <strong>may</strong> be null</li>
* <li>the packaging must <strong>not</strong> be empty</li>
* <li>the packaging must min length of 2 chars</li>
* <li>the packaging must max length of 30 chars</li>
* <li>XSS strings within the packaging will be removed</li>
* </ul>
*
* @param packaging the value to set into the Product packaging field
* @throws IllegalArgumentException if the packaging is invalid
*/
public void setPackaging(final String packaging)
{
LOG.debug("setting the Packaging");
final int max = 30;
final int min = 2;
if (packaging == null)
{
LOG.debug("Packaging is set to null");
this.myPackaging = null;
return;
}
String safePackaging = mySanitizer.sanitizeInput(packaging);
if (safePackaging.isEmpty())
{
LOG.error("Packaging must not be empty");
throw new IllegalArgumentException("Packaging must not be empty");
}
if (safePackaging.length() > max || safePackaging.length() < min)
{
LOG.error("Packaging must be between 2 and 30 chars in length");
throw new IllegalArgumentException("Packaging must be between 2 and 30 chars in length");
}
LOG.debug("setting the Packaging to: " + safePackaging);
this.myPackaging = safePackaging;
}
/**
* Returns the Discontinued value for the Product.
*
* @return the discontinued value for the Product
*/
public boolean isDiscontinued()
{
LOG.debug("returning the Discontinued: " + myDiscontinued);
return myDiscontinued;
}
/**
* Sets the Discontinued value for the Product.
*
* @param discontinued the value to set field
*/
public void setDiscontinued(final boolean discontinued)
{
LOG.debug("setting the discontinued value to: " + discontinued);
this.myDiscontinued = discontinued;
}
/**
* The hashCode() method of the Product class.
* <p>
* <strong>This method uses:</strong>
* <ul>
* <li>id</li>
* <li>product name</li>
* <li>supplier id</li>
* <li>unit price</li>
* <li>packaging</li>
* </ul>
*
* @see java.lang.Object#hashCode()
* @return the hashCode value for this Product object
*/
@Override
public int hashCode()
{
LOG.debug("building HashCode");
return new HashCodeBuilder()
.append(getId())
.append(myProductName)
.append(mySupplierId)
.append(myUnitPrice)
.append(myPackaging)
.toHashCode();
}
/**
* The equals() method of the Product class.
* <p>
* <strong>This method uses:</strong>
* <ul>
* <li>id</li>
* <li>product name</li>
* <li>supplier id</li>
* <li>unit price</li>
* <li>packaging</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 Product)
{
final Product other = (Product) obj;
return new EqualsBuilder()
.append(getId(), other.getId())
.append(myProductName, other.myProductName)
.append(mySupplierId, other.mySupplierId)
.append(myUnitPrice, other.myUnitPrice)
.append(myPackaging, other.myPackaging)
.isEquals();
}
else
{
return false;
}
}
/**
* The toString method for the Product class.
*
* this method will return:<br>
* Product [myId=xxx, myProductName=xxx, mySupplierId=xxx,
* myUnitPrice=xxx, myPackaging=xxx, myDiscontinued=xxx]
*/
@Override
public String toString()
{
LOG.debug("calling toString()");
return "Product [Id=" + getId() + ", ProductName=" + myProductName
+ ", SupplierId=" + mySupplierId
+ ", UnitPrice=" + myUnitPrice + ", Packaging=" + myPackaging
+ ", Discontinued=" + myDiscontinued + "]";
}
}