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   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 &lt; 0 or &gt; 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  }