View Javadoc
1   /*
2    * Copyright (C) 2020-2023 Dipl.-Inform. Kai Hofmann. All rights reserved!
3    */
4   package de.powerstat.validation.values;
5   
6   
7   import java.util.Objects;
8   
9   import de.powerstat.validation.interfaces.IValueObject;
10  
11  
12  /**
13   * Month.
14   *
15   * Not DSGVO relevant.
16   *
17   * TODO constructor with year
18   * TODO daysWithin() = 31, 30, 29, 28, n (Year specific for february, or october 1582)
19   * TODO Listener
20   * TODO Translations short/long
21   * TODO min, max
22   */
23  public final class Month implements Comparable<Month>, IValueObject
24   {
25    /* *
26     * Cache for singletons.
27     */
28    // private static final Map<Integer, Month> CACHE = new WeakHashMap<>();
29  
30    /**
31     * Overflow constant.
32     */
33    private static final String OVERFLOW = "Overflow"; //$NON-NLS-1$
34  
35    /**
36     * Underflow constant.
37     */
38    private static final String UNDERFLOW = "Underflow"; //$NON-NLS-1$
39  
40    /**
41     * Month.
42     */
43    private final int month;
44  
45  
46    /**
47     * Constructor.
48     *
49     * @param month Month 1-12
50     * @throws IndexOutOfBoundsException When the month is less than 1 or greater than 12
51     */
52    private Month(final int month)
53     {
54      super();
55      if ((month < 1) || (month > 12))
56       {
57        throw new IndexOutOfBoundsException("Month number out of range (1-12)!"); //$NON-NLS-1$
58       }
59      this.month = month;
60     }
61  
62  
63    /**
64     * Month factory.
65     *
66     * @param month Month 1-12
67     * @return Month object
68     * @throws IndexOutOfBoundsException When the month is less than 1 or greater than 12
69     */
70    public static Month of(final int month)
71     {
72      /*
73      synchronized (Month.class)
74       {
75        Month obj = Month.CACHE.get(month);
76        if (obj != null)
77         {
78          return obj;
79         }
80        obj = new Month(month);
81        Month.CACHE.put(Integer.valueOf(month), obj);
82        return obj;
83       }
84      */
85      return new Month(month);
86     }
87  
88  
89    /**
90     * Month factory.
91     *
92     * @param value Month 1-12 string
93     * @return Month object
94     * @throws IndexOutOfBoundsException When the month is less than 1 or greater than 12
95     */
96    public static Month of(final String value)
97     {
98      return of(Integer.parseInt(value));
99     }
100 
101 
102   /**
103    * Returns the value of this Month as an int.
104    *
105    * @return The numeric value represented by this object after conversion to type int.
106    */
107   public int intValue()
108    {
109     return this.month;
110    }
111 
112 
113   /**
114    * Returns the value of this Month as an String.
115    *
116    * @return The numeric value represented by this object after conversion to type String.
117    */
118   @Override
119   public String stringValue()
120    {
121     return String.valueOf(this.month);
122    }
123 
124 
125   /**
126    * Calculate hash code.
127    *
128    * @return Hash
129    * @see java.lang.Object#hashCode()
130    */
131   @Override
132   public int hashCode()
133    {
134     return Integer.hashCode(this.month);
135    }
136 
137 
138   /**
139    * Is equal with another object.
140    *
141    * @param obj Object
142    * @return true when equal, false otherwise
143    * @see java.lang.Object#equals(java.lang.Object)
144    */
145   @Override
146   public boolean equals(final Object obj)
147    {
148     if (this == obj)
149      {
150       return true;
151      }
152     if (!(obj instanceof Month))
153      {
154       return false;
155      }
156     final Month other = (Month)obj;
157     return this.month == other.month;
158    }
159 
160 
161   /**
162    * Returns the string representation of this Month.
163    *
164    * The exact details of this representation are unspecified and subject to change, but the following may be regarded as typical:
165    *
166    * "Month[month=1]"
167    *
168    * @return String representation of this Month
169    * @see java.lang.Object#toString()
170    */
171   @Override
172   public String toString()
173    {
174     final var builder = new StringBuilder();
175     builder.append("Month[month=").append(this.month).append(']'); //$NON-NLS-1$
176     return builder.toString();
177    }
178 
179 
180   /**
181    * Compare with another object.
182    *
183    * @param obj Object to compare with
184    * @return 0: equal; 1: greater; -1: smaller
185    * @see java.lang.Comparable#compareTo(java.lang.Object)
186    */
187   @Override
188   public int compareTo(final Month obj)
189    {
190     Objects.requireNonNull(obj, "obj"); //$NON-NLS-1$
191     return Integer.compare(this.month, obj.month);
192    }
193 
194 
195   /**
196    * Add months to this month.
197    *
198    * @param months Months to add to this month
199    * @return New month after adding the months to this month
200    * @throws ArithmeticException In case of an overflow
201    */
202   public Month add(final Months months)
203    {
204     final long newMonth = Math.toIntExact(Math.addExact(this.month, months.longValue()));
205     if (newMonth > 12) // while (newMonth > 12)
206      {
207       // TODO Listener
208       // newMonth -= 12;
209       // incrementYear();
210       throw new ArithmeticException(Month.OVERFLOW);
211      }
212     return Month.of(Math.toIntExact(newMonth));
213    }
214 
215 
216   /**
217    * Subtract months from this month.
218    *
219    * @param months Months to subtract from this month
220    * @return New month after subtracting months from this month
221    * @throws ArithmeticException In case of an underflow
222    */
223   public Month subtract(final Months months)
224    {
225     final long newMonth = Math.toIntExact(Math.subtractExact(this.month, months.longValue()));
226     if (newMonth <= 0) // while (newMonth <= 0)
227      {
228       // TODO Listener
229       // newMonth += 12;
230       // decrementYear();
231       throw new ArithmeticException(Month.UNDERFLOW);
232      }
233     return Month.of(Math.toIntExact(newMonth));
234    }
235 
236 
237   /**
238    * Increment this month.
239    *
240    * @return New month after incrementing this month
241    * @throws ArithmeticException In case of an overflow
242    */
243   public Month increment()
244    {
245     final int newMonth = Math.incrementExact(this.month);
246     if (newMonth == 13)
247      {
248       // TODO Listener
249       // newMonth = 1;
250       // incrementYear();
251       throw new ArithmeticException(Month.OVERFLOW);
252      }
253     return Month.of(newMonth);
254    }
255 
256 
257   /**
258    * Decrement this month.
259    *
260    * @return New month after decrement this month
261    * @throws ArithmeticException In case of an overflow
262    */
263   public Month decrement()
264    {
265     final int newMonth = Math.decrementExact(this.month);
266     if (newMonth == 0)
267      {
268       // TODO Listener
269       // newMonth = 12;
270       // decrementYear();
271       throw new ArithmeticException(Month.UNDERFLOW);
272      }
273     return Month.of(newMonth);
274    }
275 
276  }