Location.java

package org.example.customer.utility;

/*
 * 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.websecurity.XssSanitizer;
import org.example.websecurity.XssSanitizerImpl;

/**
 * The Customer Location for the Customer application.
 <br>
 <br>
 * This class represents the common City and Country fields in the Customer Database
 *
 * @author Jonathan Earl
 * @version 1.0
 *
 */
public class Location
    implements Serializable
{
    private static final long serialVersionUID = 1L;

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

    private String myCity;
    private String myCountry;

    private XssSanitizer mySanitizer;

    /**
     * The default constructor for the Location class.
     * <p>
     * The initial values are:
     * <ul>
     *   <li>city: New York City</li>
     *   <li>country: United States of America</li>
     * </ul>
     */
    public Location()
    {
        this(new XssSanitizerImpl());
        LOG.debug("Finishing the default Constructor");
    }
    
    /**
     * The overloaded constructor for the Location class that takes an XssSanitizer as input.
     * <p>
     * The initial values are:
     * <ul>
     *   <li>city: New York City</li>
     *   <li>country: United States of America</li>
     * </ul>
     * 
     * @param sanitizer the XssSanitizer used by this instance
     */
    public Location(final XssSanitizer sanitizer)
    {
        LOG.debug("Starting the overloaded Constructor");
        final String initialCity = "New York City";
        final String initialCountry = "United States of America";
        
        mySanitizer = sanitizer;

        setCity(initialCity);
        setCountry(initialCountry);
    }

    
    /**
     * Returns the city value for the Location.
     *  
     * @return the city value for the location
     */
    public String getCity()
    {
        LOG.debug("returning the City: " + myCity);
        return myCity;
    }

    /**
     * Sets the city value for the Location.
     * <p>
     * The business rules are:
     * <ul>
     *   <li>the city <strong>may</strong> be null</li>
     *   <li>the city must <strong>not</strong> be empty</li>
     *   <li>the city must min length of 2 chars</li>
     *   <li>the city must max length of 40 chars</li>
     *   <li>XSS strings within the city will be removed</li>
     * </ul>
     * 
     * @param city the value to set into the location city field
     * @throws IllegalArgumentException if the city is invalid
     */
    public void setCity(final String city)
    {
        LOG.debug("setting the City");
        final int max = 40;
        final int min = 2;
        
        if (city == null)
        {
            LOG.debug("City is set to null");
            this.myCity = null;
            return;
        }
        
        String safeCity = mySanitizer.sanitizeInput(city);
        if (safeCity.isEmpty())
        {
            LOG.error("City must not be empty");
            throw new IllegalArgumentException("City must not be empty");
        }
        if (safeCity.length() > max || safeCity.length() < min)
        {
            LOG.error("City must be between 2 and 40 chars in length");
            throw new IllegalArgumentException("City must be between 2 and 40 chars in length");
        }
        LOG.debug("setting the City to: " + safeCity);
        this.myCity = safeCity;
    }

    /**
     * Returns the country value for the Location.
     *  
     * @return the country value for the location
     */
    public String getCountry()
    {
        LOG.debug("returning the Country: " + myCountry);
        return myCountry;
    }

    /**
     * Sets the country value for the Location.
     * <p>
     * The business rules are:
     * <ul>
     *   <li>the country <strong>may</strong> be null</li>
     *   <li>the country must <strong>not</strong> be empty</li>
     *   <li>the country must min length of 2 chars</li>
     *   <li>the country must max length of 40 chars</li>
     *   <li>XSS strings within the country will be removed</li>
     * </ul>
     * 
     * @param country the value to set into the location country field
     * @throws IllegalArgumentException if the country is invalid
     */
    public void setCountry(final String country)
    {
        LOG.debug("setting the Country");
        final int max = 40;
        final int min = 2;
        
        if (country == null)
        {
            LOG.debug("Country is set to null");
            this.myCountry = null;
            return;
        }
        
        String safeCountry = mySanitizer.sanitizeInput(country);
        if (safeCountry.isEmpty())
        {
            LOG.error("Country must not be empty");
            throw new IllegalArgumentException("Country must not be empty");
        }
        if (safeCountry.length() > max || safeCountry.length() < min)
        {
            LOG.error("Country must be between 2 and 40 chars in length");
            throw new IllegalArgumentException("Country must be between 2 and 40 chars in length");
        }
        LOG.debug("setting the Country to: " + safeCountry);
        this.myCountry = safeCountry;
    }
    
    /**
     * The hashCode() method of the Location class.
     * <p>
     * <strong>This method uses:</strong>
     * <ul>
     *  <li>city</li>
     *  <li>country</li>
     * </ul>
     * 
     * @see java.lang.Object#hashCode()
     * @return the hashCode value for this Location object
     */
    @Override
    public int hashCode()
    {
        LOG.debug("building HashCode");
        return new HashCodeBuilder()
                .append(getCity())
                .append(getCountry())
                .toHashCode();
    }

    /**
     * The equals() method of the Location class.
     * <p>
     * <strong>This method uses:</strong>
     * <ul>
     *  <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 Location)
        {
            final Location other = (Location) obj;
            return new EqualsBuilder()
                    .append(getCity(), other.getCity())
                    .append(getCountry(), other.getCountry())
                    .isEquals();
        }
        else
        {
            return false;
        }
    }

    /**
     * The toString method for the Location class.
     * 
     * this method will return:<br>
     * Location [City=xxx Country=XXX]
     */
    @Override
    public String toString()
    {
        return "Location [City=" + myCity + ", Country=" + myCountry + "]";
    }

}