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.websecurity.XssSanitizer;
32 import org.example.websecurity.XssSanitizerImpl;
33
34 //CREATE TABLE PRODUCT (
35 // ID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),
36 // PRODUCT_NAME VARCHAR(50) NOT NULL,
37 // SUPPLIER_ID INTEGER NOT NULL,
38 // UNIT_PRICE DECIMAL(12,2),
39 // PACKAGE VARCHAR(30),
40 // IS_DISCONTINUED BOOLEAN,
41 // CONSTRAINT PK_PRODUCT PRIMARY KEY (ID)
42 // );
43
44 /**
45 * The Product Entity for the Customer application.
46 * <p>
47 * This class represents the following DB Table:
48 *
49 * <pre>
50 * CREATE TABLE PRODUCT (
51 * ID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),
52 * PRODUCT_NAME VARCHAR(50) NOT NULL,
53 * SUPPLIER_ID INTEGER NOT NULL,
54 * UNIT_PRICE DECIMAL(12,2),
55 * PACKAGE VARCHAR(30),
56 * IS_DISCONTINUED BOOLEAN,
57 * CONSTRAINT PK_PRODUCT PRIMARY KEY (ID)
58 * );
59 * </pre>
60 *
61 * @author Jonathan Earl
62 * @version 1.0
63 *
64 */
65 public final class Product extends CustomerEntity
66 implements Serializable, Comparable<Product>
67 {
68 private static final long serialVersionUID = 1L;
69
70 private static final Logger LOG = LogManager.getLogger();
71
72 private String myProductName;
73 private int mySupplierId;
74 private double myUnitPrice;
75 private String myPackaging;
76 private boolean myDiscontinued;
77
78 private XssSanitizer mySanitizer;
79
80 /**
81 * The default constructor for the Product class.
82 * <p>
83 * The initial values are:
84 * <ul>
85 * <li>id: 1</li>
86 * <li>product name: Paper Envelope</li>
87 * <li>supplier id: 1</li>
88 * <li>unit price: 0.01</li>
89 * <li>packaging: single</li>
90 * <li>discontinued: false</li>
91 * </ul>
92 */
93 public Product()
94 {
95 this(new XssSanitizerImpl());
96 LOG.debug("Finishing the default Constructor");
97 }
98
99 /**
100 * The overloaded constructor for the Product class that takes an XssSanitizer as input.
101 * <p>
102 * The initial values are:
103 * <ul>
104 * <li>id: 1</li>
105 * <li>product name: Paper Envelope</li>
106 * <li>supplier id: 1</li>
107 * <li>unit price: 0.01</li>
108 * <li>packaging: single</li>
109 * <li>discontinued: false</li>
110 * </ul>
111 *
112 * @param sanitizer the XssSanitizer used by this instance
113 */
114 public Product(final XssSanitizer sanitizer)
115 {
116 LOG.debug("Starting the overloaded Constructor");
117 final int initialId = 1;
118 final String initialProductName = "Paper Envelope";
119 final int initialSupplierId = 1;
120 final double initialUnitPrice = 0.01;
121 final String initialPackaging = "single";
122 final boolean initialDiscontinued = false;
123
124 mySanitizer = sanitizer;
125
126 setId(initialId);
127 setProductName(initialProductName);
128 setSupplierId(initialSupplierId);
129 setUnitPrice(initialUnitPrice);
130 setPackaging(initialPackaging);
131 setDiscontinued(initialDiscontinued);
132 }
133
134 /**
135 * This will enable sorting of Products by name.
136 * <br>
137 * <br>
138 * @param other the Product object to compare with
139 * @return the sort value of negative/zero/positive
140 */
141 @Override
142 public int compareTo(Product other)
143 {
144 return this.getProductName().compareToIgnoreCase(other.getProductName());
145 }
146
147 /**
148 * Returns the product name value for the Product.
149 *
150 * @return the product name value for the Product
151 */
152 public String getProductName()
153 {
154 LOG.debug("returning the Product Name: " + myProductName);
155 return myProductName;
156 }
157
158 /**
159 * Sets the product name value for the Product.
160 * <p>
161 * The business rules are:
162 * <ul>
163 * <li>the product name must <strong>not</strong> be null</li>
164 * <li>the product name must <strong>not</strong> be empty</li>
165 * <li>the product name must max length of 50 chars</li>
166 * <li>XSS strings within the product name will be removed</li>
167 * </ul>
168 *
169 * @param productName the value to set into the product name field
170 * @throws IllegalArgumentException if the product name is invalid
171 */
172 public void setProductName(final String productName)
173 {
174 LOG.debug("setting the Product Name");
175 final int max = 50;
176
177 if (productName == null)
178 {
179 LOG.error("Product Name must not be null");
180 throw new IllegalArgumentException("Product Name must not be null");
181 }
182
183 String safeProductName = mySanitizer.sanitizeInput(productName);
184 if (safeProductName.isEmpty())
185 {
186 LOG.error("Product Name must not be empty");
187 throw new IllegalArgumentException("Product Name must not be empty");
188 }
189 if (safeProductName.length() > max)
190 {
191 LOG.error("Product Name must be up to 50 chars in length");
192 throw new IllegalArgumentException("Product Name must be up to 50 chars in length");
193 }
194 LOG.debug("setting the Product Name to: " + safeProductName);
195 this.myProductName = safeProductName;
196 }
197
198 /**
199 * Returns the SupplierId value for the Product.
200 *
201 * @return the supplierId value for the Product
202 */
203 public int getSupplierId()
204 {
205 LOG.debug("returning the SupplierId: " + mySupplierId);
206 return mySupplierId;
207 }
208
209 /**
210 * Sets the SupplierId value for the Product.
211 * <p>
212 * The business rules are:
213 * <ul>
214 * <li>the SupplierId must be 1 or greater</li>
215 * </ul>
216 *
217 * @param supplierId the value to set into the supplier id field
218 * @throws IllegalArgumentException if the id is invalid
219 */
220 public void setSupplierId(final int supplierId)
221 {
222 LOG.debug("setting the SuppliierId: " + supplierId);
223 final int min = 1;
224
225 if (supplierId < min)
226 {
227 LOG.error("SupplierId must be greater then zero");
228 throw new IllegalArgumentException("SupplierId must be greater then zero");
229 }
230 this.mySupplierId = supplierId;
231 }
232
233 /**
234 * Returns the UnitPrice value for the Product.
235 *
236 * @return the UnitPrice value for the Product
237 */
238 public double getUnitPrice()
239 {
240 LOG.debug("returning the UnitPrice: " + myUnitPrice);
241 return myUnitPrice;
242 }
243
244 /**
245 * Sets the UnitPrice value for the Product.
246 * <p>
247 * The business rules are:
248 * <ul>
249 * <li>the UnitPrice must be 0.01 or greater</li>
250 * </ul>
251 *
252 * @param unitPrice the value to set into the unitPrice field
253 * @throws IllegalArgumentException if the UnitPrice is invalid
254 */
255 public void setUnitPrice(final double unitPrice)
256 {
257 LOG.debug("setting the unitPrice: " + unitPrice);
258 final double min = 0.01;
259
260 if (unitPrice < min)
261 {
262 LOG.error("unitPrice must be greater then zero");
263 throw new IllegalArgumentException("unitPrice must be greater then zero");
264 }
265 this.myUnitPrice = unitPrice;
266 }
267
268 /**
269 * Returns the packaging value for the Product.
270 *
271 * @return the packaging value for the Product
272 */
273 public String getPackaging()
274 {
275 LOG.debug("returning the Packaging: " + myPackaging);
276 return myPackaging;
277 }
278
279 /**
280 * Sets the packaging value for the Product.
281 * <p>
282 * The business rules are:
283 * <ul>
284 * <li>the packaging <strong>may</strong> be null</li>
285 * <li>the packaging must <strong>not</strong> be empty</li>
286 * <li>the packaging must min length of 2 chars</li>
287 * <li>the packaging must max length of 30 chars</li>
288 * <li>XSS strings within the packaging will be removed</li>
289 * </ul>
290 *
291 * @param packaging the value to set into the Product packaging field
292 * @throws IllegalArgumentException if the packaging is invalid
293 */
294 public void setPackaging(final String packaging)
295 {
296 LOG.debug("setting the Packaging");
297 final int max = 30;
298 final int min = 2;
299
300 if (packaging == null)
301 {
302 LOG.debug("Packaging is set to null");
303 this.myPackaging = null;
304 return;
305 }
306
307 String safePackaging = mySanitizer.sanitizeInput(packaging);
308 if (safePackaging.isEmpty())
309 {
310 LOG.error("Packaging must not be empty");
311 throw new IllegalArgumentException("Packaging must not be empty");
312 }
313 if (safePackaging.length() > max || safePackaging.length() < min)
314 {
315 LOG.error("Packaging must be between 2 and 30 chars in length");
316 throw new IllegalArgumentException("Packaging must be between 2 and 30 chars in length");
317 }
318 LOG.debug("setting the Packaging to: " + safePackaging);
319 this.myPackaging = safePackaging;
320 }
321
322 /**
323 * Returns the Discontinued value for the Product.
324 *
325 * @return the discontinued value for the Product
326 */
327 public boolean isDiscontinued()
328 {
329 LOG.debug("returning the Discontinued: " + myDiscontinued);
330 return myDiscontinued;
331 }
332
333 /**
334 * Sets the Discontinued value for the Product.
335 *
336 * @param discontinued the value to set field
337 */
338 public void setDiscontinued(final boolean discontinued)
339 {
340 LOG.debug("setting the discontinued value to: " + discontinued);
341 this.myDiscontinued = discontinued;
342 }
343
344 /**
345 * The hashCode() method of the Product class.
346 * <p>
347 * <strong>This method uses:</strong>
348 * <ul>
349 * <li>id</li>
350 * <li>product name</li>
351 * <li>supplier id</li>
352 * <li>unit price</li>
353 * <li>packaging</li>
354 * </ul>
355 *
356 * @see java.lang.Object#hashCode()
357 * @return the hashCode value for this Product object
358 */
359 @Override
360 public int hashCode()
361 {
362 LOG.debug("building HashCode");
363 return new HashCodeBuilder()
364 .append(getId())
365 .append(myProductName)
366 .append(mySupplierId)
367 .append(myUnitPrice)
368 .append(myPackaging)
369 .toHashCode();
370 }
371
372 /**
373 * The equals() method of the Product class.
374 * <p>
375 * <strong>This method uses:</strong>
376 * <ul>
377 * <li>id</li>
378 * <li>product name</li>
379 * <li>supplier id</li>
380 * <li>unit price</li>
381 * <li>packaging</li>
382 * </ul>
383 *
384 * @see java.lang.Object#equals(Object obj)
385 * @param obj the incoming object to compare against
386 * @return true if the fields being compared are equal
387 */
388 @Override
389 public boolean equals(final Object obj)
390 {
391 LOG.debug("checking equals");
392 if (obj instanceof Product)
393 {
394 final Product other = (Product) obj;
395 return new EqualsBuilder()
396 .append(getId(), other.getId())
397 .append(myProductName, other.myProductName)
398 .append(mySupplierId, other.mySupplierId)
399 .append(myUnitPrice, other.myUnitPrice)
400 .append(myPackaging, other.myPackaging)
401 .isEquals();
402 }
403 else
404 {
405 return false;
406 }
407 }
408
409 /**
410 * The toString method for the Product class.
411 *
412 * this method will return:<br>
413 * Product [myId=xxx, myProductName=xxx, mySupplierId=xxx,
414 * myUnitPrice=xxx, myPackaging=xxx, myDiscontinued=xxx]
415 */
416 @Override
417 public String toString()
418 {
419 LOG.debug("calling toString()");
420 return "Product [Id=" + getId() + ", ProductName=" + myProductName
421 + ", SupplierId=" + mySupplierId
422 + ", UnitPrice=" + myUnitPrice + ", Packaging=" + myPackaging
423 + ", Discontinued=" + myDiscontinued + "]";
424 }
425 }