View Javadoc
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 }