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 }