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  import java.time.LocalDate;
26  import java.time.format.DateTimeFormatter;
27  import java.util.ArrayList;
28  import java.util.List;
29  
30  import org.apache.commons.lang3.builder.EqualsBuilder;
31  import org.apache.commons.lang3.builder.HashCodeBuilder;
32  import org.apache.logging.log4j.LogManager;
33  import org.apache.logging.log4j.Logger;
34  import org.example.customer.utility.CustomerEntity;
35  import org.example.websecurity.XssSanitizer;
36  import org.example.websecurity.XssSanitizerImpl;
37  
38  /**
39   * The Order Entity for the Customer application.
40   * <br>
41   * <br>
42   * This class represents the following DB Table:
43   * 
44   * <pre>
45   * CREATE TABLE ORDERS (
46   *      ID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),
47   *      ORDER_DATE DATE NOT NULL,
48   *      CUSTOMER_ID  INTEGER,
49   *      TOTAL_AMOUNT DECIMAL(12,2),
50   *      ORDER_NUMBER VARCHAR(10),
51   *      CONSTRAINT PK_ORDER PRIMARY KEY (ID)
52   *   );
53   * </pre>
54   * 
55   * @author Jonathan Earl
56   * @version 1.0
57   *
58   */
59  public final class Order extends CustomerEntity
60     implements Serializable, Comparable<Order>
61  {
62      private static final long serialVersionUID = 1L;
63      
64      private static final Logger LOG = LogManager.getLogger();
65  
66      private LocalDate myOrderDate;
67      private int myCustomerId;
68      private double myTotalAmount;
69      private String myOrderNumber;
70  
71      private XssSanitizer mySanitizer;
72      
73      private List<OrderItem> myOrderItems;
74  
75      /**
76       * The default constructor for the Order class.
77       * <br>
78       * <br>
79       * The initial values are:
80       * <ul>
81       *   <li>id: 1</li>
82       *   <li>orderDate: Current Date</li>
83       *   <li>customerId: 1</li>
84       *   <li>totalAmount: 0.00</li>
85       *   <li>orderNumber: null</li>
86       *   <li>orderItems: empty</li>
87       * </ul>
88       */
89      public Order()
90      {
91          this(new XssSanitizerImpl());
92          LOG.debug("Finishing the default Constructor");
93      }
94  
95      /**
96       * The overloaded constructor for the order class that takes an XssSanitizer as input.
97       * <p>
98       * The initial values are:
99       * <ul>
100      *   <li>id: 1</li>
101      *   <li>orderDate: Current Date</li>
102      *   <li>customerId: 1</li>
103      *   <li>totalAmount: 0.00</li>
104      *   <li>orderNumber: null</li>
105      *   <li>orderItems: empty</li>
106      * </ul>
107      * 
108      * @param sanitizer the XssSanitizer used by this instance
109      */
110     public Order(final XssSanitizer sanitizer)
111     {
112         LOG.debug("Starting the overloaded Constructor");
113         final int initialId = 1;
114         final LocalDate initialOrderDate = LocalDate.now();
115         final int initialCustomerId = 1;
116         final String initialOrderNumber = null;
117         final List<OrderItem> initalOrderItems = new ArrayList<OrderItem>();
118         
119         mySanitizer = sanitizer;
120         
121         setId(initialId);
122         setOrderDate(initialOrderDate);
123         setCustomerId(initialCustomerId);
124         setOrderNumber(initialOrderNumber);
125         setOrderItems(initalOrderItems);
126     }
127     
128     /**
129      * This will enable sorting of Order by Order number concatenated with Order Date.
130      * <br>
131      * <br>
132      * @param other the Order object to compare with
133      * @return the sort value of negative/zero/positive
134      */
135     @Override
136     public int compareTo(Order other)
137     {
138         DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM-dd-yyyy");
139         String thisSortValue = this.getOrderNumber() + " " 
140                 + this.getOrderDate().format(formatter);
141         String otherSortValue = other.getOrderNumber() + " " 
142                 + other.getOrderDate().format(formatter);
143         return thisSortValue.compareToIgnoreCase(otherSortValue);
144     }
145 
146     /**
147      * Returns the orderDate value for the Order.
148      *  
149      * @return the orderDate value for the order
150      */
151     public LocalDate getOrderDate()
152     {
153         LOG.debug("returning the Oder Date: " + myOrderDate);
154         return myOrderDate;
155     }
156 
157     /**
158      * Sets the order date value for the Customer.
159      * <p>
160      * The business rules are:
161      * <ul>
162      *   <li>the order date must <strong>not</strong> be null</li>
163      *   <li>the order date must <strong>today or earlier</strong></li>
164      * </ul>
165      * 
166      * @param orderDate the value to set into the orderDate field
167      * @throws IllegalArgumentException if the first name is invalid
168      */
169     public void setOrderDate(final LocalDate orderDate)
170     {
171         LOG.debug("setting the order Date");
172         
173         if (orderDate == null)
174         {
175             LOG.error("Order Date must not be null");
176             throw new IllegalArgumentException("Order Date must not be null");
177         }
178         LocalDate tomorrow = LocalDate.now().plusDays(1);
179         if (orderDate.isBefore(tomorrow))
180         {
181             this.myOrderDate = orderDate;
182         }
183         else
184         {
185             LOG.error("OrderDate must be today or earlier");
186             throw new IllegalArgumentException("OrderDate must be today or earlier");
187         }
188     }
189 
190     /**
191      * Returns the id value for the Customer Id.
192      *  
193      * @return the id value for the customer id
194      */
195     public int getCustomerId()
196     {
197         LOG.debug("returning the CustomerId: " + myCustomerId);
198         return myCustomerId;
199     }
200 
201     /**
202      * Sets the id value for the Customer ID.
203      * <p>
204      * The business rules are:
205      * <ul>
206      *   <li>the id must be 1 or greater</li>
207      * </ul>
208      * 
209      * @param customerId the value to set into the customer id field
210      * @throws IllegalArgumentException if the customerId is invalid
211      */
212     public void setCustomerId(final int customerId)
213     {
214         LOG.debug("setting the CustomerId: " + customerId);
215         final int min = 1;
216         
217         if (customerId < min)
218         {
219             LOG.error("CustomerId must be greater then zero");
220             throw new IllegalArgumentException("CustomerId must be greater then zero");
221         }
222         this.myCustomerId = customerId;
223     }
224 
225 
226     /**
227      * Returns the total amount for the order.
228      *  
229      * @return the totalAmount value for the order
230      */
231     public double getTotalAmount()
232     {
233         LOG.debug("returning the totalAmount: " + myTotalAmount);
234         return myTotalAmount;
235     }
236     
237     /**
238      * Sets the total amount value for the Orde.
239      * <p>
240      * The business rules are:
241      * <ul>
242      *   <li>the TotalAmount must be 0.01 or greater</li>
243      * </ul>
244      * 
245      * @param totalAmount the value to set into the totalAmount field
246      * @throws IllegalArgumentException if the totalAmount is invalid
247      */
248     public void setTotalAmount(final double totalAmount)
249     {
250         LOG.debug("setting the totalAmount: " + totalAmount);
251         final double min = 0.01;
252         
253         if (totalAmount < min)
254         {
255             LOG.error("totalAmount must be greater then zero");
256             throw new IllegalArgumentException("totalAmount must be greater then zero");
257         }
258         this.myTotalAmount = totalAmount;
259     }
260 
261     /**
262      * Returns the orderNumber value for the Order.
263      *  
264      * @return the orderNumber value for the order
265      */
266     public String getOrderNumber()
267     {
268         LOG.debug("returning the OrderNumber: " + myOrderNumber);
269         return myOrderNumber;
270     }
271 
272     /**
273      * Sets the orderNumber value for the Order.
274      * <p>
275      * The business rules are:
276      * <ul>
277      *   <li>the orderNumber <strong>may</strong> be null</li>
278      *   <li>the orderNumber must <strong>not</strong> be empty</li>
279      *   <li>the orderNumber must min length of 2 chars</li>
280      *   <li>the orderNumber must max length of 10 chars</li>
281      *   <li>XSS strings within the orderNumber will be removed</li>
282      * </ul>
283      * 
284      * @param orderNumber the value to set into the Order Number field
285      * @throws IllegalArgumentException if the orderNumber is invalid
286      */
287     public void setOrderNumber(final String orderNumber)
288     {
289         LOG.debug("setting the OrderNumber");
290         final int max = 10;
291         final int min = 2;
292         
293         if (orderNumber == null)
294         {
295             LOG.debug("orderNumber is set to null");
296             this.myOrderNumber = null;
297             return;
298         }
299         
300         String safeOrderNumber = mySanitizer.sanitizeInput(orderNumber);
301         if (safeOrderNumber.isEmpty())
302         {
303             LOG.error("OrderNumber must not be empty");
304             throw new IllegalArgumentException("OrderNumber must not be empty");
305         }
306         if (safeOrderNumber.length() > max || safeOrderNumber.length() < min)
307         {
308             LOG.error("OrderNumber must be between 2 and 10 chars in length");
309             throw new IllegalArgumentException("OrderNumber must be between 2 and 10 chars in length");
310         }
311         LOG.debug("setting the OrderNumber to: " + safeOrderNumber);
312         this.myOrderNumber = safeOrderNumber;
313     }
314         
315     /**
316      * Returns the orderItem list value for the Order.
317      *  
318      * @return the myOrderItems for the order
319      */
320     public List<OrderItem> getOrderItems()
321     {
322         LOG.debug("returning the OrderItems: " + myOrderItems);
323         return myOrderItems;
324     }
325 
326     /**
327      * Sets the orderItem list value for the Order.
328      * <p>
329      * The business rules are:
330      * <ul>
331      *   <li>the orderItems must <strong>not</strong> be null</li>
332      *   <li>the orderItems may be empty</li>
333      * </ul>
334      * 
335      * @param orderItems the list to set into the orderItems
336      * @throws IllegalArgumentException if the orderItems is invalid
337      */
338 	public void setOrderItems(final List<OrderItem> orderItems)
339 	{
340         LOG.debug("setting the Orderitems");
341         if (orderItems == null)
342         {
343             LOG.error("Orderitems must not be null");
344             throw new IllegalArgumentException("Orderitems must not be null");
345         }
346         double total = 0.0;
347         for (OrderItem current : orderItems)
348         {
349             total += current.getSubTotal();
350         }
351         this.myTotalAmount = total;
352         this.myOrderItems = orderItems;
353     }
354 
355     /**
356      * The hashCode() method of the Order class.
357      * <p>
358      * <strong>This method uses:</strong>
359      * <ul>
360      *  <li>id</li>
361      *  <li>order date</li>
362      *  <li>customer id</li>
363      *  <li>order number</li>
364      * </ul>
365      * 
366      * @see java.lang.Object#hashCode()
367      * @return the hashCode value for this Order object
368      */
369     @Override
370     public int hashCode()
371     {
372         LOG.debug("building HashCode");
373         return new HashCodeBuilder()
374                 .append(getId())
375                 .append(myOrderDate)
376                 .append(myCustomerId)
377                 .append(myOrderNumber)
378                 .toHashCode();
379     }
380 
381     /**
382      * The equals() method of the order class.
383      * <p>
384      * <strong>This method uses:</strong>
385      * <ul>
386      *  <li>id</li>
387      *  <li>order date</li>
388      *  <li>customer id</li>
389      *  <li>order number</li>
390      * </ul>
391      * 
392      * @see java.lang.Object#equals(Object obj)
393      * @param obj the incoming object to compare against
394      * @return true if the fields being compared are equal
395      */
396     @Override
397     public boolean equals(final Object obj)
398     {
399         LOG.debug("checking equals");
400         if (obj instanceof Order)
401         {
402             final Order other = (Order) obj;
403             return new EqualsBuilder()
404                     .append(getId(), other.getId())
405                     .append(myOrderDate, other.myOrderDate)
406                     .append(myCustomerId, other.myCustomerId)
407                     .append(myOrderNumber, other.myOrderNumber)
408                     .isEquals();
409         }
410         else
411         {
412             return false;
413         }
414     }
415     
416     
417 
418     /**
419      * The toString method for the Order class.
420      * 
421      * this method will return:<br>
422      * Order [Id=xxx, OrderDate=xx/xx/xx, CustomerId=xxx, 
423      *   TotalAmount=xxx, OrderNumber=xxx]
424      */
425     @Override
426     public String toString()
427     {
428         return "Order [Id=" + getId() + ", OrderDate=" + myOrderDate + ", CustomerId=" + myCustomerId
429                 + ", TotalAmount=" + myTotalAmount + ", OrderNumber=" + myOrderNumber 
430                 + ", OrderItems=" + myOrderItems.size() + "]";
431     }
432 }