# Customer.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.customer.utility.Location;
    import org.example.customer.utility.Phone;
    import org.example.websecurity.XssSanitizer;
    import org.example.websecurity.XssSanitizerImpl;

    /**
     * The Customer Entity for the Customer application.
     * <br>
     * <br>
     * This class represents the following DB Table:
     *
     * <pre>
     * CREATE TABLE CUSTOMER (
     *      ID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),
     *      FIRST_NAME VARCHAR(40) NOT NULL,
     *      LAST_NAME VARCHAR(40) NOT NULL,
     *      CITY VARCHAR(40),
     *      COUNTRY VARCHAR(40),
     *      PHONE VARCHAR(20),
     *      CONSTRAINT PK_CUSTOMER PRIMARY KEY (ID)
     *   );
     * </pre>
     *
     * @author Jonathan Earl
     * @version 1.0
     *
     */
    public final class Customer extends CustomerEntity
        implements Serializable, Comparable<Customer>
    {
        private static final long serialVersionUID = 1L;

        private static final Logger LOG = LogManager.getLogger();

        private String myFirstName;
        private String myLastName;
        private Location myLocation;
        private Phone myPhone;

        private XssSanitizer mySanitizer;

        /**
         * The default constructor for the Customer class.
         * <p>
         * The initial values are:
         * <ul>
         *   <li>id: 1</li>
         *   <li>first name: John</li>
         *   <li>last name: Smith</li>
         *   <li>city: New York City</li>
         *   <li>country: United States of America</li>
         *   <li>phone: null</li>
         * </ul>
         */
        public Customer()
        {
            this(new XssSanitizerImpl());
            LOG.debug("Finishing the default Constructor");
        }

        /**
         * The overloaded constructor for the Customer class that takes an XssSanitizer as input.
         * <p>
         * The initial values are:
         * <ul>
         *   <li>id: 1</li>
         *   <li>first name: John</li>
         *   <li>last name: Smith</li>
         *   <li>city: New York City</li>
         *   <li>country: United States of America</li>
         *   <li>phone: null</li>
         * </ul>
         *
         * @param sanitizer the XssSanitizer used by this instance
         */
        public Customer(final XssSanitizer sanitizer)
        {
            LOG.debug("Starting the overloaded Constructor");
            final int initialId = 1;
            final String initialFirstName = "John";
            final String initialLastName = "Smith";
            final String initialNumber = null;

            mySanitizer = sanitizer;

            setId(initialId);
            setFirstName(initialFirstName);
            setLastName(initialLastName);
            setLocation(new Location());
            Phone temp2 = new Phone();
            temp2.setNumber(initialNumber);
            setPhone(temp2);
        }

        /**
         * This will enable sorting of Customers by full name.
         * <br>
         * <br>
         * @param other the Customer object to compare with
         * @return the sort value of negative/zero/positive
         */
        @Override
        public int compareTo(Customer other)
        {
            String thisFullName = this.getFirstName() + " "
                    + this.getLastName();
            String otherFullName = other.getFirstName() + " "
                    + other.getLastName();
            return thisFullName.compareToIgnoreCase(otherFullName);
        }

        /**
         * Returns the first name value for the Customer.
         *
         * @return the first name value for the customer
         */
        public String getFirstName()
        {
            LOG.debug("returning the First Name: " + myFirstName);
            return myFirstName;
        }

        /**
         * Sets the first name value for the Customer.
         * <br>
         * <br>
         * The business rules are:
         * <ul>
         *   <li>the first name must <strong>not</strong> be null</li>
         *   <li>the first name must <strong>not</strong> be empty</li>
         *   <li>the first name must max length of 40 chars</li>
         *   <li>XSS strings within the first name will be removed</li>
         * </ul>
         *
         * @param firstName the value to set into the customer first name field
         * @throws IllegalArgumentException if the first name is invalid
         */
        public void setFirstName(final String firstName)
        {
            LOG.debug("setting the First Name");
            final int max = 40;

            if (firstName == null)
            {
                LOG.error("First Name must not be null");
                throw new IllegalArgumentException("First Name must not be null");
            }

            String safeFirstName = mySanitizer.sanitizeInput(firstName);
            if (safeFirstName.isEmpty())
            {
                LOG.error("First Name must not be empty");
                throw new IllegalArgumentException("First Name must not be empty");
            }
            if (safeFirstName.length() > max)
            {
                LOG.error("First Name must be up to 40 chars in length");
                throw new IllegalArgumentException("First Name must be up to 40 chars in length");
            }
            LOG.debug("setting the First Name to: " + safeFirstName);
            this.myFirstName = safeFirstName;
        }

        /**
         * Returns the last name value for the Customer.
         *
         * @return the last name value for the customer
         */
        public String getLastName()
        {
            LOG.debug("returning the Last Name: " + myLastName);
            return myLastName;
        }

        /**
         * Sets the last name value for the Customer.
         * <p>
         * The business rules are:
         * <ul>
         *   <li>the last name must <strong>not</strong> be null</li>
         *   <li>the last name must <strong>not</strong> be empty</li>
         *   <li>the last name must min length of 2 chars</li>
         *   <li>the last name must max length of 40 chars</li>
         *   <li>XSS strings within the last name will be removed</li>
         * </ul>
         *
         * @param lastName the value to set into the customer last name field
         * @throws IllegalArgumentException if the last name is invalid
         */
        public void setLastName(final String lastName)
        {
            LOG.debug("setting the Last Name");
            final int max = 40;
            final int min = 2;

            if (lastName == null)
            {
                LOG.error("Last Name must not be null");
                throw new IllegalArgumentException("Last Name must not be null");
            }

            String safeLastName = mySanitizer.sanitizeInput(lastName);
            if (safeLastName.isEmpty())
            {
                LOG.error("Last Name must not be empty");
                throw new IllegalArgumentException("Last Name must not be empty");
            }
            if (safeLastName.length() > max || safeLastName.length() < min)
            {
                LOG.error("Last Name must be between 2 and 40 chars in length");
                throw new IllegalArgumentException("Last Name must be between 2 and 40 chars in length");
            }
            LOG.debug("setting the Last Name to: " + safeLastName);
            this.myLastName = safeLastName;
        }

        /**
         * Return the Location for the Customer.
         * <p>
         * @return the myLocation
         */
        public Location getLocation()
        {
            LOG.debug("returning the Location: " + myLocation);
            return myLocation;
        }

        /**
         * Sets the location value for the Customer.
         * <p>
         * The business rules are:
         * <ul>
         *   <li>the location <strong>may</strong> be null</li>
         * </ul>
         *
         * @param location the value to set into the customer location field
         * @throws IllegalArgumentException if the location is invalid
         */
        public void setLocation(final Location location)
        {
            LOG.debug("setting the Location");
            if (location == null)
            {
                LOG.error("Location must not be null");
                throw new IllegalArgumentException("Location must not be null");
            }
            this.myLocation = location;
        }

        /**
         * Returns the phone value for the Customer.
         *
         * @return the phone value for the customer
         */
        public Phone getPhone()
        {
            LOG.debug("returning the Phone: " + myPhone);
            return myPhone;
        }

        /**
         * Sets the phone value for the Customer.
         * <p>
         * The business rules are:
         * <ul>
         *   <li>the phone <strong>may</strong> be null</li>
         * </ul>
         *
         * @param phone the value to set into the customer phone field
         * @throws IllegalArgumentException if the phone is invalid
         */
        public void setPhone(final Phone phone)
        {
            LOG.debug("setting the Phone");
            this.myPhone = phone;
        }

        /**
         * The hashCode() method of the Customer class.
         * <p>
         * <strong>This method uses:</strong>
         * <ul>
         *  <li>id</li>
         *  <li>first name</li>
         *  <li>last name</li>
         *  <li>city</li>
         *  <li>country</li>
         * </ul>
         *
         * @see java.lang.Object#hashCode()
         * @return the hashCode value for this Customer object
         */
        @Override
        public int hashCode()
        {
            LOG.debug("building HashCode");
            return new HashCodeBuilder()
                    .append(getId())
                    .append(myFirstName)
                    .append(myLastName)
                    .append(myLocation.hashCode())
                    .toHashCode();
        }

        /**
         * The equals() method of the Customer class.
         * <p>
         * <strong>This method uses:</strong>
         * <ul>
         *  <li>id</li>
         *  <li>first name</li>
         *  <li>last name</li>
         *  <li>city</li>
         *  <li>country</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 Customer)
            {
                final Customer other = (Customer) obj;
                return new EqualsBuilder()
                        .append(getId(), other.getId())
                        .append(myFirstName, other.myFirstName)
                        .append(myLastName, other.myLastName)
                        .append(myLocation.getCity(), other.myLocation.getCity())
                        .append(myLocation.getCountry(), other.myLocation.getCountry())
                        .isEquals();
            }
            else
            {
                return false;
            }
        }

        /**
         * The toString method for the Customer class.
         *
         * this method will return:<br>
         * Customer [Id=xxx, FirstName=xxx, LastName=xxx, City=xxx,
         *      Country=xxx, Phone=xxx]
         */
        @Override
        public String toString()
        {
            return "Customer [Id=" + getId() + ", FirstName=" + myFirstName
                    + ", LastName=" + myLastName + ", City="
                    + myLocation.getCity() + ", Country=" + myLocation.getCountry()
                    + ", Phone=" + myPhone.getNumber() + "]";
        }

    }