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 import java.util.regex.Pattern;
9
10 import de.powerstat.validation.interfaces.IValueObject;
11
12
13 /**
14 * IP V4 mask.
15 *
16 * Not DSGVO relevant.
17 *
18 * TODO IPV4Address filterMin(IPV4Address) 0
19 * TODO IPV4Address filterMax(IPV4Address) 255
20 */
21 public final class IPV4Mask implements Comparable<IPV4Mask>, IValueObject
22 {
23 /* *
24 * Cache for singletons.
25 */
26 // private static final Map<Integer, IPV4Mask> CACHE = new WeakHashMap<>();
27
28 /**
29 * 0.
30 */
31 private static final String ZERO = "0"; //$NON-NLS-1$
32
33 /**
34 * Bitmask array.
35 */
36 private static final String[] BITMASKS = {IPV4Mask.ZERO, "128", "192", "224", "240", "248", "252", "254", "255"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$
37
38 /**
39 * IP V4 mask regexp.
40 */
41 private static final Pattern IPV4_MASK_REGEXP = Pattern.compile("^(((255\\.){3}(255|254|252|248|240|224|192|128|0))|((255\\.){2}(254|252|248|240|224|192|128|0)\\.0)|((255\\.)(254|252|248|240|224|192|128|0)(\\.0){2})|((254|252|248|240|224|192|128|0)(\\.0){3}))$"); //$NON-NLS-1$
42
43 /**
44 * Prefix length.
45 */
46 private final int length;
47
48 /**
49 * Mask.
50 */
51 private final String mask;
52
53
54 /**
55 * Constructor.
56 *
57 * @param length Prefix length (0-32)
58 * @throws IndexOutOfBoundsException if the prefix length is < 0 or > 32
59 */
60 private IPV4Mask(final int length)
61 {
62 super();
63 if ((length < 0) || (length > 32))
64 {
65 throw new IndexOutOfBoundsException("Netmask prefix < 0 or > 32"); //$NON-NLS-1$
66 }
67 this.length = length;
68 if (length < 9)
69 {
70 this.mask = IPV4Mask.BITMASKS[length] + ".0.0.0"; //$NON-NLS-1$
71 }
72 else if (length < 17)
73 {
74 this.mask = "255." + IPV4Mask.BITMASKS[length - 8] + ".0.0"; //$NON-NLS-1$ //$NON-NLS-2$
75 }
76 else if (length < 25)
77 {
78 this.mask = "255.255." + IPV4Mask.BITMASKS[length - 16] + ".0"; //$NON-NLS-1$ //$NON-NLS-2$
79 }
80 else
81 {
82 this.mask = "255.255.255." + IPV4Mask.BITMASKS[length - 24]; //$NON-NLS-1$
83 }
84 }
85
86
87 /* *
88 * Constructor.
89 *
90 * @param mask Mask in the form like 255.255.255.0
91 * @throws NullPointerException if mask is null
92 * @throws IllegalArgumentException If mask is not an IP V4 network mask
93 */
94 /*
95 private IPV4Mask(final String mask)
96 {
97 super();
98 Objects.requireNonNull(mask, "mask"); //$NON-NLS-1$
99 if ((mask.length() < 7) || (mask.length() > 15))
100 {
101 throw new IllegalArgumentException("To short or long for an IP V4 network mask"); //$NON-NLS-1$
102 }
103 if (!IPV4Mask.IPV4_MASK_REGEXP.matcher(mask).matches())
104 {
105 throw new IllegalArgumentException("Not an IP V4 network mask"); //$NON-NLS-1$
106 }
107 this.mask = mask;
108 this.length = maskToLength(mask);
109 }
110 */
111
112
113 /**
114 * Calculate length from mask.
115 *
116 * @param mask Mask in the form like 255.255.255.0
117 * @return Prefix length (0-32)
118 */
119 private static int maskToLength(final String mask)
120 {
121 final String[] parts = mask.split("\\."); //$NON-NLS-1$
122 for (int part = 3; part >= 0; --part)
123 {
124 if (IPV4Mask.ZERO.equals(parts[part]))
125 {
126 continue;
127 }
128 for (int pos = 1; pos < IPV4Mask.BITMASKS.length; ++pos)
129 {
130 if (parts[part].equals(IPV4Mask.BITMASKS[pos]))
131 {
132 return (8 * part) + pos;
133 }
134 }
135 }
136 return 0;
137 }
138
139
140 /**
141 * IPV4Mask factory.
142 *
143 * @param length IP V4 prefix length 0-32
144 * @return IPV4Mask object
145 */
146 public static IPV4Mask of(final int length)
147 {
148 /*
149 synchronized (IPV4Mask.class)
150 {
151 IPV4Mask obj = IPV4Mask.CACHE.get(length);
152 if (obj != null)
153 {
154 return obj;
155 }
156 obj = new IPV4Mask(length);
157 IPV4Mask.CACHE.put(Integer.valueOf(length), obj);
158 return obj;
159 }
160 */
161 return new IPV4Mask(length);
162 }
163
164
165 /**
166 * IPV4Mask factory.
167 *
168 * @param mask IP V4 network mask in format like 255.255.255.0
169 * @return IPV4Mask object
170 *
171 * TODO mask=length as string (0-32)?
172 */
173 public static IPV4Mask of(final String mask)
174 {
175 Objects.requireNonNull(mask, "mask"); //$NON-NLS-1$
176 if ((mask.length() < 7) || (mask.length() > 15))
177 {
178 throw new IllegalArgumentException("To short or long for an IP V4 network mask"); //$NON-NLS-1$
179 }
180 if (!IPV4Mask.IPV4_MASK_REGEXP.matcher(mask).matches())
181 {
182 throw new IllegalArgumentException("Not an IP V4 network mask"); //$NON-NLS-1$
183 }
184 return new IPV4Mask(maskToLength(mask));
185 }
186
187
188 /**
189 * Returns the value of this IPV4Mask as an int.
190 *
191 * @return The numeric value represented by this object after conversion to type int (0-32).
192 */
193 public int intValue()
194 {
195 return this.length;
196 }
197
198
199 /**
200 * Returns the value of this IPV4Mask as a string.
201 *
202 * @return The text value represented by this object after conversion to type string of format 255.255.255.0.
203 */
204 @Override
205 public String stringValue()
206 {
207 return this.mask;
208 }
209
210
211 /**
212 * Calculate hash code.
213 *
214 * @return Hash
215 * @see java.lang.Object#hashCode()
216 */
217 @Override
218 public int hashCode()
219 {
220 return Integer.hashCode(this.length);
221 }
222
223
224 /**
225 * Is equal with another object.
226 *
227 * @param obj Object
228 * @return true when equal, false otherwise
229 * @see java.lang.Object#equals(java.lang.Object)
230 */
231 @Override
232 public boolean equals(final Object obj)
233 {
234 if (this == obj)
235 {
236 return true;
237 }
238 if (!(obj instanceof IPV4Mask))
239 {
240 return false;
241 }
242 final IPV4Mask other = (IPV4Mask)obj;
243 return this.length == other.length;
244 }
245
246
247 /**
248 * Returns the string representation of this IPV4Address.
249 *
250 * The exact details of this representation are unspecified and subject to change, but the following may be regarded as typical:
251 *
252 * "IPV4Mask[length=24, mask=255.255.255.0]"
253 *
254 * @return String representation of this IPV4Mask
255 * @see java.lang.Object#toString()
256 */
257 @Override
258 public String toString()
259 {
260 final var builder = new StringBuilder(24);
261 builder.append("IPV4Mask[length=").append(this.length).append(", mask=").append(this.mask).append(']'); //$NON-NLS-1$ //$NON-NLS-2$
262 return builder.toString();
263 }
264
265
266 /**
267 * Compare with another object.
268 *
269 * @param obj Object to compare with
270 * @return 0: equal; 1: greater; -1: smaller
271 * @see java.lang.Comparable#compareTo(java.lang.Object)
272 */
273 @Override
274 public int compareTo(final IPV4Mask obj)
275 {
276 Objects.requireNonNull(obj, "obj"); //$NON-NLS-1$
277 return Integer.compare(this.length, obj.length);
278 }
279
280 }