1 package org.example.customer;
2
3 /*
4 * This is free and unencumbered software released into the public domain.
5 * Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software,
6 * either in source code form or as a compiled binary, for any purpose, commercial or
7 * non-commercial, and by any means.
8 *
9 * In jurisdictions that recognize copyright laws, the author or authors of this
10 * software dedicate any and all copyright interest in the software to the public domain.
11 * We make this dedication for the benefit of the public at large and to the detriment of
12 * our heirs and successors. We intend this dedication to be an overt act of relinquishment in
13 * perpetuity of all present and future rights to this software under copyright law.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
17 * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES
18 * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 *
21 * For more information, please refer to: https://unlicense.org/
22 */
23
24 import java.io.Serializable;
25
26 import org.apache.commons.lang3.builder.EqualsBuilder;
27 import org.apache.commons.lang3.builder.HashCodeBuilder;
28 import org.apache.logging.log4j.LogManager;
29 import org.apache.logging.log4j.Logger;
30 import org.example.customer.utility.CustomerEntity;
31 import org.example.customer.utility.Location;
32 import org.example.customer.utility.Phone;
33 import org.example.websecurity.XssSanitizer;
34 import org.example.websecurity.XssSanitizerImpl;
35
36 /**
37 * The Customer Entity for the Customer application.
38 * <br>
39 * <br>
40 * This class represents the following DB Table:
41 *
42 * <pre>
43 * CREATE TABLE CUSTOMER (
44 * ID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),
45 * FIRST_NAME VARCHAR(40) NOT NULL,
46 * LAST_NAME VARCHAR(40) NOT NULL,
47 * CITY VARCHAR(40),
48 * COUNTRY VARCHAR(40),
49 * PHONE VARCHAR(20),
50 * CONSTRAINT PK_CUSTOMER PRIMARY KEY (ID)
51 * );
52 * </pre>
53 *
54 * @author Jonathan Earl
55 * @version 1.0
56 *
57 */
58 public final class Customer extends CustomerEntity
59 implements Serializable, Comparable<Customer>
60 {
61 private static final long serialVersionUID = 1L;
62
63 private static final Logger LOG = LogManager.getLogger();
64
65 private String myFirstName;
66 private String myLastName;
67 private Location myLocation;
68 private Phone myPhone;
69
70 private XssSanitizer mySanitizer;
71
72 /**
73 * The default constructor for the Customer class.
74 * <p>
75 * The initial values are:
76 * <ul>
77 * <li>id: 1</li>
78 * <li>first name: John</li>
79 * <li>last name: Smith</li>
80 * <li>city: New York City</li>
81 * <li>country: United States of America</li>
82 * <li>phone: null</li>
83 * </ul>
84 */
85 public Customer()
86 {
87 this(new XssSanitizerImpl());
88 LOG.debug("Finishing the default Constructor");
89 }
90
91 /**
92 * The overloaded constructor for the Customer class that takes an XssSanitizer as input.
93 * <p>
94 * The initial values are:
95 * <ul>
96 * <li>id: 1</li>
97 * <li>first name: John</li>
98 * <li>last name: Smith</li>
99 * <li>city: New York City</li>
100 * <li>country: United States of America</li>
101 * <li>phone: null</li>
102 * </ul>
103 *
104 * @param sanitizer the XssSanitizer used by this instance
105 */
106 public Customer(final XssSanitizer sanitizer)
107 {
108 LOG.debug("Starting the overloaded Constructor");
109 final int initialId = 1;
110 final String initialFirstName = "John";
111 final String initialLastName = "Smith";
112 final String initialNumber = null;
113
114 mySanitizer = sanitizer;
115
116 setId(initialId);
117 setFirstName(initialFirstName);
118 setLastName(initialLastName);
119 setLocation(new Location());
120 Phone temp2 = new Phone();
121 temp2.setNumber(initialNumber);
122 setPhone(temp2);
123 }
124
125 /**
126 * This will enable sorting of Customers by full name.
127 * <br>
128 * <br>
129 * @param other the Customer object to compare with
130 * @return the sort value of negative/zero/positive
131 */
132 @Override
133 public int compareTo(Customer other)
134 {
135 String thisFullName = this.getFirstName() + " "
136 + this.getLastName();
137 String otherFullName = other.getFirstName() + " "
138 + other.getLastName();
139 return thisFullName.compareToIgnoreCase(otherFullName);
140 }
141
142 /**
143 * Returns the first name value for the Customer.
144 *
145 * @return the first name value for the customer
146 */
147 public String getFirstName()
148 {
149 LOG.debug("returning the First Name: " + myFirstName);
150 return myFirstName;
151 }
152
153 /**
154 * Sets the first name value for the Customer.
155 * <br>
156 * <br>
157 * The business rules are:
158 * <ul>
159 * <li>the first name must <strong>not</strong> be null</li>
160 * <li>the first name must <strong>not</strong> be empty</li>
161 * <li>the first name must max length of 40 chars</li>
162 * <li>XSS strings within the first name will be removed</li>
163 * </ul>
164 *
165 * @param firstName the value to set into the customer first name field
166 * @throws IllegalArgumentException if the first name is invalid
167 */
168 public void setFirstName(final String firstName)
169 {
170 LOG.debug("setting the First Name");
171 final int max = 40;
172
173 if (firstName == null)
174 {
175 LOG.error("First Name must not be null");
176 throw new IllegalArgumentException("First Name must not be null");
177 }
178
179 String safeFirstName = mySanitizer.sanitizeInput(firstName);
180 if (safeFirstName.isEmpty())
181 {
182 LOG.error("First Name must not be empty");
183 throw new IllegalArgumentException("First Name must not be empty");
184 }
185 if (safeFirstName.length() > max)
186 {
187 LOG.error("First Name must be up to 40 chars in length");
188 throw new IllegalArgumentException("First Name must be up to 40 chars in length");
189 }
190 LOG.debug("setting the First Name to: " + safeFirstName);
191 this.myFirstName = safeFirstName;
192 }
193
194 /**
195 * Returns the last name value for the Customer.
196 *
197 * @return the last name value for the customer
198 */
199 public String getLastName()
200 {
201 LOG.debug("returning the Last Name: " + myLastName);
202 return myLastName;
203 }
204
205 /**
206 * Sets the last name value for the Customer.
207 * <p>
208 * The business rules are:
209 * <ul>
210 * <li>the last name must <strong>not</strong> be null</li>
211 * <li>the last name must <strong>not</strong> be empty</li>
212 * <li>the last name must min length of 2 chars</li>
213 * <li>the last name must max length of 40 chars</li>
214 * <li>XSS strings within the last name will be removed</li>
215 * </ul>
216 *
217 * @param lastName the value to set into the customer last name field
218 * @throws IllegalArgumentException if the last name is invalid
219 */
220 public void setLastName(final String lastName)
221 {
222 LOG.debug("setting the Last Name");
223 final int max = 40;
224 final int min = 2;
225
226 if (lastName == null)
227 {
228 LOG.error("Last Name must not be null");
229 throw new IllegalArgumentException("Last Name must not be null");
230 }
231
232 String safeLastName = mySanitizer.sanitizeInput(lastName);
233 if (safeLastName.isEmpty())
234 {
235 LOG.error("Last Name must not be empty");
236 throw new IllegalArgumentException("Last Name must not be empty");
237 }
238 if (safeLastName.length() > max || safeLastName.length() < min)
239 {
240 LOG.error("Last Name must be between 2 and 40 chars in length");
241 throw new IllegalArgumentException("Last Name must be between 2 and 40 chars in length");
242 }
243 LOG.debug("setting the Last Name to: " + safeLastName);
244 this.myLastName = safeLastName;
245 }
246
247 /**
248 * Return the Location for the Customer.
249 * <p>
250 * @return the myLocation
251 */
252 public Location getLocation()
253 {
254 LOG.debug("returning the Location: " + myLocation);
255 return myLocation;
256 }
257
258 /**
259 * Sets the location value for the Customer.
260 * <p>
261 * The business rules are:
262 * <ul>
263 * <li>the location <strong>may</strong> be null</li>
264 * </ul>
265 *
266 * @param location the value to set into the customer location field
267 * @throws IllegalArgumentException if the location is invalid
268 */
269 public void setLocation(final Location location)
270 {
271 LOG.debug("setting the Location");
272 if (location == null)
273 {
274 LOG.error("Location must not be null");
275 throw new IllegalArgumentException("Location must not be null");
276 }
277 this.myLocation = location;
278 }
279
280 /**
281 * Returns the phone value for the Customer.
282 *
283 * @return the phone value for the customer
284 */
285 public Phone getPhone()
286 {
287 LOG.debug("returning the Phone: " + myPhone);
288 return myPhone;
289 }
290
291 /**
292 * Sets the phone value for the Customer.
293 * <p>
294 * The business rules are:
295 * <ul>
296 * <li>the phone <strong>may</strong> be null</li>
297 * </ul>
298 *
299 * @param phone the value to set into the customer phone field
300 * @throws IllegalArgumentException if the phone is invalid
301 */
302 public void setPhone(final Phone phone)
303 {
304 LOG.debug("setting the Phone");
305 this.myPhone = phone;
306 }
307
308 /**
309 * The hashCode() method of the Customer class.
310 * <p>
311 * <strong>This method uses:</strong>
312 * <ul>
313 * <li>id</li>
314 * <li>first name</li>
315 * <li>last name</li>
316 * <li>city</li>
317 * <li>country</li>
318 * </ul>
319 *
320 * @see java.lang.Object#hashCode()
321 * @return the hashCode value for this Customer object
322 */
323 @Override
324 public int hashCode()
325 {
326 LOG.debug("building HashCode");
327 return new HashCodeBuilder()
328 .append(getId())
329 .append(myFirstName)
330 .append(myLastName)
331 .append(myLocation.hashCode())
332 .toHashCode();
333 }
334
335 /**
336 * The equals() method of the Customer class.
337 * <p>
338 * <strong>This method uses:</strong>
339 * <ul>
340 * <li>id</li>
341 * <li>first name</li>
342 * <li>last name</li>
343 * <li>city</li>
344 * <li>country</li>
345 * </ul>
346 *
347 * @see java.lang.Object#equals(Object obj)
348 * @param obj the incoming object to compare against
349 * @return true if the fields being compared are equal
350 */
351 @Override
352 public boolean equals(final Object obj)
353 {
354 LOG.debug("checking equals");
355 if (obj instanceof Customer)
356 {
357 final Customer other = (Customer) obj;
358 return new EqualsBuilder()
359 .append(getId(), other.getId())
360 .append(myFirstName, other.myFirstName)
361 .append(myLastName, other.myLastName)
362 .append(myLocation.getCity(), other.myLocation.getCity())
363 .append(myLocation.getCountry(), other.myLocation.getCountry())
364 .isEquals();
365 }
366 else
367 {
368 return false;
369 }
370 }
371
372 /**
373 * The toString method for the Customer class.
374 *
375 * this method will return:<br>
376 * Customer [Id=xxx, FirstName=xxx, LastName=xxx, City=xxx,
377 * Country=xxx, Phone=xxx]
378 */
379 @Override
380 public String toString()
381 {
382 return "Customer [Id=" + getId() + ", FirstName=" + myFirstName
383 + ", LastName=" + myLastName + ", City="
384 + myLocation.getCity() + ", Country=" + myLocation.getCountry()
385 + ", Phone=" + myPhone.getNumber() + "]";
386 }
387
388 }