Eliminate IetfYangUtil.INSTANCE
[mdsal.git] / model / ietf / rfc6991-ietf-inet-types / src / main / java / org / opendaylight / yang / gen / v1 / urn / ietf / params / xml / ns / yang / ietf / inet / types / rev130715 / AbstractIetfInetUtil.java
1 /*
2  * Copyright (c) 2016 Pantheon Technologies s.r.o. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.annotations.Beta;
14 import com.google.common.net.InetAddresses;
15 import java.net.Inet4Address;
16 import java.net.Inet6Address;
17 import java.net.InetAddress;
18 import java.net.UnknownHostException;
19 import java.util.AbstractMap.SimpleImmutableEntry;
20 import java.util.Map.Entry;
21 import org.eclipse.jdt.annotation.NonNull;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.opendaylight.mdsal.binding.spec.reflect.StringValueObjectFactory;
24 import org.opendaylight.mdsal.model.ietf.util.Ipv4Utils;
25 import org.opendaylight.mdsal.model.ietf.util.Ipv6Utils;
26
27 /**
28  * A set of utility methods to efficiently instantiate various ietf-inet-types DTOs.
29  */
30 @Beta
31 @SuppressWarnings("checkstyle:classTypeParameterName")
32 public abstract class AbstractIetfInetUtil {
33     private final StringValueObjectFactory<Ipv4AddressNoZone> address4NoZoneFactory =
34         StringValueObjectFactory.create(Ipv4AddressNoZone.class, "0.0.0.0");
35     private final StringValueObjectFactory<Ipv4Prefix> prefix4Factory =
36         StringValueObjectFactory.create(Ipv4Prefix.class, "0.0.0.0/0");
37     private final StringValueObjectFactory<Ipv6AddressNoZone> address6NoZoneFactory =
38         StringValueObjectFactory.create(Ipv6AddressNoZone.class, "::0");
39     private final StringValueObjectFactory<Ipv6Prefix> prefix6Factory =
40         StringValueObjectFactory.create(Ipv6Prefix.class, "::0/0");
41
42     protected abstract @NonNull IpAddress ipv4Address(@NonNull Ipv4AddressNoZone addr);
43
44     protected abstract @NonNull IpAddressNoZone ipv4AddressNoZone(@NonNull Ipv4AddressNoZone addr);
45
46     protected abstract @NonNull IpAddress ipv6Address(@NonNull Ipv6AddressNoZone addr);
47
48     protected abstract @NonNull IpAddressNoZone ipv6AddressNoZone(@NonNull Ipv6AddressNoZone addr);
49
50     protected abstract @Nullable Ipv4Address maybeIpv4Address(@NonNull IpAddress addr);
51
52     protected abstract @Nullable Ipv4AddressNoZone maybeIpv4AddressNoZone(@NonNull IpAddressNoZone addr);
53
54     protected abstract @Nullable Ipv6Address maybeIpv6Address(@NonNull IpAddress addr);
55
56     protected abstract @Nullable Ipv6AddressNoZone maybeIpv6AddressNoZone(@NonNull IpAddressNoZone addr);
57
58     protected abstract @NonNull IpPrefix ipv4Prefix(@NonNull Ipv4Prefix addr);
59
60     protected abstract @NonNull IpPrefix ipv6Prefix(@NonNull Ipv6Prefix addr);
61
62     protected abstract @NonNull String ipv4AddressString(@NonNull Ipv4Address addr);
63
64     protected abstract @NonNull String ipv6AddressString(@NonNull Ipv6Address addr);
65
66     protected abstract @NonNull String ipv4PrefixString(@NonNull Ipv4Prefix prefix);
67
68     protected abstract @NonNull String ipv6PrefixString(@NonNull Ipv6Prefix prefix);
69
70     /**
71      * Create an IpAddress by interpreting input bytes as an IPv4 or IPv6 address, based on array length.
72      *
73      * @param bytes 4-byte (IPv4) or 6-byte (IPv6) array
74      * @return An IpAddress object
75      * @throws IllegalArgumentException if bytes has length different from 4 or 6
76      * @throws NullPointerException if bytes is null
77      */
78     public final @NonNull IpAddress ipAddressFor(final byte @NonNull[] bytes) {
79         return switch (bytes.length) {
80             case Ipv4Utils.INET4_LENGTH -> ipv4Address(ipv4AddressFor(bytes));
81             case Ipv6Utils.INET6_LENGTH -> ipv6Address(ipv6AddressFor(bytes));
82             default -> throwInvalidArray(bytes);
83         };
84     }
85
86     public final @NonNull IpAddress ipAddressFor(final @NonNull InetAddress addr) {
87         requireAddress(addr);
88         if (addr instanceof Inet4Address) {
89             return ipv4Address(ipv4AddressFor(addr));
90         } else if (addr instanceof Inet6Address) {
91             return ipv6Address(ipv6AddressFor(addr));
92         } else {
93             throw unhandledAddress(addr);
94         }
95     }
96
97     private static <T> @NonNull T requireAddress(final T addr) {
98         return requireNonNull(addr, "Address must not be null");
99     }
100
101     /**
102      * Create an IpAddress by interpreting input bytes as an IPv4 or IPv6 address, based on array length.
103      *
104      * @param bytes 4-byte (IPv4) or 6-byte (IPv6) array
105      * @return A no-zone IpAddress object
106      * @throws IllegalArgumentException if bytes has length different from 4 or 6
107      * @throws NullPointerException if bytes is null
108      */
109     public final @NonNull IpAddressNoZone ipAddressNoZoneFor(final byte @NonNull[] bytes) {
110         return switch (bytes.length) {
111             case Ipv4Utils.INET4_LENGTH -> ipv4AddressNoZone(ipv4AddressFor(bytes));
112             case Ipv6Utils.INET6_LENGTH -> ipv6AddressNoZone(ipv6AddressFor(bytes));
113             default -> throwInvalidArray(bytes);
114         };
115     }
116
117     public final @NonNull IpAddressNoZone ipAddressNoZoneFor(final @NonNull InetAddress addr) {
118         requireAddress(addr);
119         if (addr instanceof Inet4Address) {
120             return ipv4AddressNoZone(ipv4AddressFor(addr));
121         } else if (addr instanceof Inet6Address) {
122             return ipv6AddressNoZone(ipv6AddressFor(addr));
123         } else {
124             throw unhandledAddress(addr);
125         }
126     }
127
128     private static <T> T throwInvalidArray(final byte[] bytes) {
129         throw new IllegalArgumentException("Invalid array length " + bytes.length);
130     }
131
132     private static IllegalArgumentException unhandledAddress(final InetAddress addr) {
133         return new IllegalArgumentException("Unhandled address " + addr);
134     }
135
136     /**
137      * Create an IpPrefix by combining the address with a mask. The address
138      * bytes are interpreted as an address and the specified mask is concatenated to
139      * it. The address bytes are not masked.
140      *
141      * @param bytes Input address as a 4-byte (IPv4) or 16-byte (IPv6) array
142      * @param mask Prefix mask
143      * @return An IpPrefix object
144      * @throws IllegalArgumentException if bytes has length different from 4 or 16 or if mask is not
145      *         in range 0-32 or 0-128 respectively
146      * @throws NullPointerException if bytes is null
147      */
148     public final @NonNull IpPrefix ipPrefixFor(final byte @NonNull[] bytes, final int mask) {
149         return switch (bytes.length) {
150             case Ipv4Utils.INET4_LENGTH -> ipv4Prefix(ipv4PrefixFor(bytes, mask));
151             case Ipv6Utils.INET6_LENGTH -> ipv6Prefix(ipv6PrefixFor(bytes, mask));
152             default -> throwInvalidArray(bytes);
153         };
154     }
155
156     public final @NonNull IpPrefix ipPrefixFor(final @NonNull InetAddress addr, final int mask) {
157         requireAddress(addr);
158         if (addr instanceof Inet4Address) {
159             return ipv4Prefix(ipv4PrefixFor(addr, mask));
160         } else if (addr instanceof Inet6Address) {
161             return ipv6Prefix(ipv6PrefixFor(addr, mask));
162         } else {
163             throw unhandledAddress(addr);
164         }
165     }
166
167     public final @NonNull IpPrefix ipPrefixFor(final @NonNull IpAddress addr) {
168         final var v4 = maybeIpv4Address(addr);
169         return v4 != null ? ipv4Prefix(ipv4PrefixFor(v4)) : ipv6Prefix(ipv6PrefixFor(coerceIpv6Address(addr)));
170     }
171
172     public final @NonNull IpPrefix ipPrefixForNoZone(final @NonNull IpAddressNoZone addr) {
173         final var v4 = maybeIpv4AddressNoZone(addr);
174         return v4 != null ? ipv4Prefix(ipv4PrefixFor(inet4AddressForNoZone(v4)))
175             : ipv6Prefix(ipv6PrefixFor(coerceIpv6AddressNoZone(addr)));
176     }
177
178     public final @NonNull InetAddress inetAddressFor(final @NonNull IpAddress addr) {
179         final var v4 = maybeIpv4Address(addr);
180         return v4 != null ? inet4AddressFor(v4) : inet6AddressFor(coerceIpv6Address(addr));
181     }
182
183     public final @NonNull InetAddress inetAddressForNoZone(final @NonNull IpAddressNoZone addr) {
184         final var v4 = maybeIpv4AddressNoZone(addr);
185         return v4 != null ? inet4AddressForNoZone(v4) : inet6AddressForNoZone(coerceIpv6AddressNoZone(addr));
186     }
187
188     public final @NonNull Inet4Address inet4AddressFor(final @NonNull Ipv4Address addr) {
189         try {
190             return (Inet4Address) InetAddress.getByAddress(ipv4AddressBytes(addr));
191         } catch (UnknownHostException e) {
192             throw new IllegalArgumentException("Invalid address " + addr, e);
193         }
194     }
195
196     public final @NonNull Inet4Address inet4AddressForNoZone(final @NonNull Ipv4AddressNoZone addr) {
197         try {
198             return (Inet4Address) InetAddress.getByAddress(ipv4AddressNoZoneBytes(addr));
199         } catch (UnknownHostException e) {
200             throw new IllegalArgumentException("Invalid address " + addr, e);
201         }
202     }
203
204     public final @NonNull Inet6Address inet6AddressFor(final @NonNull Ipv6Address addr) {
205         try {
206             return (Inet6Address) InetAddress.getByAddress(ipv6AddressBytes(addr));
207         } catch (UnknownHostException e) {
208             throw new IllegalArgumentException("Invalid address " + addr, e);
209         }
210     }
211
212     public final @NonNull Inet6Address inet6AddressForNoZone(final @NonNull Ipv6AddressNoZone addr) {
213         try {
214             return (Inet6Address) InetAddress.getByAddress(ipv6AddressNoZoneBytes(addr));
215         } catch (UnknownHostException e) {
216             throw new IllegalArgumentException("Invalid address " + addr, e);
217         }
218     }
219
220     /**
221      * Create an Ipv4AddressNoZone by interpreting input bytes as an IPv4 address.
222      *
223      * @param bytes 4-byte array
224      * @return An Ipv4AddressNoZone object
225      * @throws IllegalArgumentException if bytes has length different from 4
226      * @throws NullPointerException if bytes is null
227      */
228     public final @NonNull Ipv4AddressNoZone ipv4AddressFor(final byte @NonNull[] bytes) {
229         return address4NoZoneFactory.newInstance(Ipv4Utils.addressString(bytes));
230     }
231
232     /**
233      * Create an Ipv4AddressNoZone by interpreting an {@link Inet4Address}.
234      *
235      * @param addr An {@link Inet4Address}
236      * @return An Ipv4AddressNoZone object
237      * @throws IllegalArgumentException if addr is not an {@link Inet4Address}
238      * @throws NullPointerException if addr is null
239      */
240     public final @NonNull Ipv4AddressNoZone ipv4AddressFor(final @NonNull InetAddress addr) {
241         return address4NoZoneFactory.newInstance(addressStringV4(addr));
242     }
243
244     /**
245      * Create an Ipv4AddressNoZone by interpreting input 32 bits as an IPv4 address in big-endian format.
246      *
247      * @param bits 32 bits, big endian
248      * @return An Ipv4AddressNoZone object
249      */
250     public final @NonNull Ipv4AddressNoZone ipv4AddressFor(final int bits) {
251         return address4NoZoneFactory.newInstance(Ipv4Utils.addressString(bits));
252     }
253
254     /**
255      * Create an Ipv4AddressNoZone by interpreting an Ipv4Address.
256      *
257      * @param addr An Ipv4Address
258      * @return An Ipv4AddressNoZone object
259      * @throws NullPointerException if addr is null
260      */
261     public final @NonNull Ipv4AddressNoZone ipv4AddressNoZoneFor(final @NonNull Ipv4Address addr) {
262         requireAddress(addr);
263         return addr instanceof Ipv4AddressNoZone noZone ? noZone
264             :  address4NoZoneFactory.newInstance(stripZone(ipv4AddressString(addr)));
265     }
266
267     public final @NonNull Ipv4AddressNoZone ipv4AddressFrom(final @NonNull Ipv4Prefix prefix) {
268         return prefixToAddress(address4NoZoneFactory, ipv4PrefixString(prefix));
269     }
270
271     public final byte @NonNull[] ipv4AddressBytes(final @NonNull Ipv4Address addr) {
272         /*
273          * This implementation relies heavily on the input string having been validated to comply with
274          * the Ipv4Address pattern, which may include a zone index.
275          */
276         final String str = ipv4AddressString(addr);
277         final int percent = str.indexOf('%');
278         return Ipv4Utils.addressBytes(str, percent == -1 ? str.length() : percent);
279     }
280
281     public final int ipv4AddressBits(final @NonNull Ipv4Address addr) {
282         final String str = ipv4AddressString(addr);
283         final int percent = str.indexOf('%');
284         return Ipv4Utils.addressBits(str, percent == -1 ? str.length() : percent);
285     }
286
287     public final byte @NonNull[] ipv4AddressNoZoneBytes(final @NonNull Ipv4AddressNoZone addr) {
288         /*
289          * This implementation relies heavily on the input string having been validated to comply with
290          * the Ipv4AddressNoZone pattern, which must not include a zone index.
291          */
292         final String str = ipv4AddressString(addr);
293         return Ipv4Utils.addressBytes(str, str.length());
294     }
295
296     public final int ipv4AddressNoZoneBits(final @NonNull Ipv4AddressNoZone addr) {
297         final String str = ipv4AddressString(addr);
298         return Ipv4Utils.addressBits(str, str.length());
299     }
300
301     /**
302      * Create a /32 Ipv4Prefix by interpreting input bytes as an IPv4 address.
303      *
304      * @param bytes four-byte array
305      * @return An Ipv4Prefix object
306      * @throws IllegalArgumentException if bytes has length different from 4
307      * @throws NullPointerException if bytes is null
308      */
309     public final @NonNull Ipv4Prefix ipv4PrefixFor(final byte @NonNull[] bytes) {
310         return prefix4Factory.newInstance(prefixStringV4(bytes));
311     }
312
313     /**
314      * Create a Ipv4Prefix by combining the address with a mask. The address
315      * bytes are interpreted as an address and the specified mask is concatenated to
316      * it. The address bytes are not masked, hence input <code>address = { 1, 2, 3, 4 }</code>
317      * and <code>mask=24</code> will result in <code>1.2.3.4/24</code>.
318      *
319      * @param address Input address as a 4-byte array
320      * @param mask Prefix mask
321      * @return An Ipv4Prefix object
322      * @throws IllegalArgumentException if bytes has length different from 4 or if mask is not in range 0-32
323      * @throws NullPointerException if bytes is null
324      */
325     public final @NonNull Ipv4Prefix ipv4PrefixFor(final byte @NonNull[] address, final int mask) {
326         return prefix4Factory.newInstance(prefixStringV4(address, mask));
327     }
328
329     /**
330      * Create a /32 Ipv4Prefix for an {@link Inet4Address}.
331      *
332      * @param addr An {@link Inet4Address}
333      * @return An Ipv4Prefix object
334      * @throws IllegalArgumentException if addr is not an Inet4Address
335      * @throws NullPointerException if addr is null
336      */
337     public final @NonNull Ipv4Prefix ipv4PrefixFor(final @NonNull InetAddress addr) {
338         return prefix4Factory.newInstance(addressStringV4(addr) + "/32");
339     }
340
341     /**
342      * Create a Ipv4Prefix by combining the address with a mask. The address bytes are not masked.
343      *
344      * @param addr An {@link Inet4Address}
345      * @param mask Prefix mask
346      * @return An Ipv4Prefix object
347      * @throws IllegalArgumentException if addr is not an Inet4Address or if mask is not in range 0-32
348      * @throws NullPointerException if addr is null
349      */
350     public final @NonNull Ipv4Prefix ipv4PrefixFor(final @NonNull InetAddress addr, final int mask) {
351         return newIpv4Prefix(addressStringV4(addr), mask);
352     }
353
354     public final @NonNull Ipv4Prefix ipv4PrefixFor(final @NonNull Ipv4Address addr) {
355         return prefix4Factory.newInstance(stripZone(ipv4AddressString(requireAddress(addr))) + "/32");
356     }
357
358     public final @NonNull Ipv4Prefix ipv4PrefixFor(final @NonNull Ipv4Address addr, final int mask) {
359         return newIpv4Prefix(stripZone(ipv4AddressString(requireAddress(addr))), mask);
360     }
361
362     public final @NonNull Ipv4Prefix ipv4PrefixForNoZone(final @NonNull Ipv4AddressNoZone addr) {
363         return prefix4Factory.newInstance(ipv4AddressString(requireAddress(addr)) + "/32");
364     }
365
366     public final @NonNull Ipv4Prefix ipv4PrefixForNoZone(final @NonNull Ipv4AddressNoZone addr, final int mask) {
367         return newIpv4Prefix(ipv4AddressString(requireAddress(addr)), mask);
368     }
369
370     public final @NonNull Ipv4Prefix ipv4PrefixForShort(final byte @NonNull[] address, final int mask) {
371         if (mask == 0) {
372             // Easy case, reuse the template
373             return prefix4Factory.getTemplate();
374         }
375
376         return v4PrefixForShort(address, 0, mask / Byte.SIZE + (mask % Byte.SIZE == 0 ? 0 : 1), mask);
377     }
378
379     public final @NonNull Ipv4Prefix ipv4PrefixForShort(final byte @NonNull[] array, final int startOffset,
380             final int mask) {
381         if (mask == 0) {
382             // Easy case, reuse the template
383             return prefix4Factory.getTemplate();
384         }
385
386         return v4PrefixForShort(array, startOffset, mask / Byte.SIZE + (mask % Byte.SIZE == 0 ? 0 : 1), mask);
387     }
388
389     private static String stripZone(final String str) {
390         final int percent = str.indexOf('%');
391         return percent == -1 ? str : str.substring(0, percent);
392     }
393
394     private @NonNull Ipv4Prefix newIpv4Prefix(final String addr, final int mask) {
395         checkArgument(mask >= 0 && mask <= 32, "Invalid mask %s", mask);
396         return prefix4Factory.newInstance(addr + '/' + mask);
397     }
398
399     public final @NonNull Entry<Ipv4AddressNoZone, Integer> splitIpv4Prefix(final @NonNull Ipv4Prefix prefix) {
400         return splitPrefix(address4NoZoneFactory, ipv4PrefixString(prefix));
401     }
402
403     public final byte @NonNull[] ipv4PrefixToBytes(final @NonNull Ipv4Prefix prefix) {
404         final String str = ipv4PrefixString(prefix);
405         final int slash = str.lastIndexOf('/');
406
407         final byte[] bytes = new byte[Ipv4Utils.INET4_LENGTH + 1];
408         Ipv4Utils.fillIpv4Bytes(bytes, 0, str, 0, slash);
409         bytes[Ipv4Utils.INET4_LENGTH] = (byte)Integer.parseInt(str.substring(slash + 1), 10);
410         return bytes;
411     }
412
413     /**
414      * Create an Ipv6Address by interpreting input bytes as an IPv6 address.
415      *
416      * @param bytes 16-byte array
417      * @return An Ipv6Address object
418      * @throws IllegalArgumentException if bytes has length different from 16
419      * @throws NullPointerException if bytes is null
420      */
421     public final @NonNull Ipv6AddressNoZone ipv6AddressFor(final byte @NonNull[] bytes) {
422         return address6NoZoneFactory.newInstance(addressStringV6(bytes));
423     }
424
425     /**
426      * Create an Ipv6Address by interpreting an {@link Inet6Address}.
427      *
428      * @param addr An {@link Inet6Address}
429      * @return An Ipv6Address object
430      * @throws IllegalArgumentException if addr is not an {@link Inet6Address}
431      * @throws NullPointerException if addr is null
432      */
433     public final @NonNull Ipv6AddressNoZone ipv6AddressFor(final @NonNull InetAddress addr) {
434         return address6NoZoneFactory.newInstance(addressStringV6(addr));
435     }
436
437     /**
438      * Create an Ipv6AddressNoZone by interpreting an Ipv6Address.
439      *
440      * @param addr An Ipv6Address
441      * @return An Ipv6AddressNoZone object
442      * @throws NullPointerException if addr is null
443      */
444     public final @NonNull Ipv6AddressNoZone ipv6AddressNoZoneFor(final @NonNull Ipv6Address addr) {
445         requireAddress(addr);
446         return addr instanceof Ipv6AddressNoZone noZone ? noZone
447                 : address6NoZoneFactory.newInstance(stripZone(ipv6AddressString(addr)));
448     }
449
450     public final @NonNull Ipv6AddressNoZone ipv6AddressFrom(final @NonNull Ipv6Prefix prefix) {
451         return prefixToAddress(address6NoZoneFactory, ipv6PrefixString(prefix));
452     }
453
454     public final byte @NonNull[] ipv6AddressBytes(final @NonNull Ipv6Address addr) {
455         final String str = ipv6AddressString(addr);
456         final int percent = str.indexOf('%');
457         return ipv6StringBytes(str, percent == -1 ? str.length() : percent);
458     }
459
460     public final byte @NonNull[] ipv6AddressNoZoneBytes(final @NonNull Ipv6Address addr) {
461         final String str = ipv6AddressString(addr);
462         return ipv6StringBytes(str, str.length());
463     }
464
465     private static byte @NonNull[] ipv6StringBytes(final @NonNull String str, final int limit) {
466         final byte[] bytes = new byte[Ipv6Utils.INET6_LENGTH];
467         Ipv6Utils.fillIpv6Bytes(bytes, str, limit);
468         return bytes;
469     }
470
471     /**
472      * Create a /128 Ipv6Prefix by interpreting input bytes as an IPv6 address.
473      *
474      * @param bytes four-byte array
475      * @return An Ipv6Prefix object
476      * @throws IllegalArgumentException if bytes has length different from 16
477      * @throws NullPointerException if bytes is null
478      */
479     public final @NonNull Ipv6Prefix ipv6PrefixFor(final byte @NonNull[] bytes) {
480         return prefix6Factory.newInstance(addressStringV6(bytes) + "/128");
481     }
482
483     /**
484      * Create a Ipv6Prefix by combining the address with a mask. The address
485      * bytes are interpreted as an address and the specified mask is concatenated to
486      * it. The address bytes are not masked.
487      *
488      * @param address Input address as a 16-byte array
489      * @param mask Prefix mask
490      * @return An Ipv6Prefix object
491      * @throws IllegalArgumentException if bytes has length different from 16 or if mask is not in range 0-128
492      * @throws NullPointerException if bytes is null
493      */
494     public final @NonNull Ipv6Prefix ipv6PrefixFor(final byte @NonNull[] address, final int mask) {
495         checkArgument(mask >= 0 && mask <= 128, "Invalid mask %s", mask);
496         return prefix6Factory.newInstance(addressStringV6(address) + '/' + mask);
497     }
498
499     /**
500      * Create a /128 Ipv6Prefix by interpreting input bytes as an IPv6 address.
501      *
502      * @param addr an {@link Inet6Address}
503      * @return An Ipv6Prefix object
504      * @throws IllegalArgumentException if addr is not an Inet6Address
505      * @throws NullPointerException if addr is null
506      */
507     public final @NonNull Ipv6Prefix ipv6PrefixFor(final @NonNull InetAddress addr) {
508         return prefix6Factory.newInstance(addressStringV6(addr) + "/128");
509     }
510
511     /**
512      * Create a Ipv6Prefix by combining the address with a mask. The address
513      * bytes are interpreted as an address and the specified mask is concatenated to
514      * it. The address bytes are not masked.
515      *
516      * @param addr Input address
517      * @param mask Prefix mask
518      * @return An Ipv6Prefix object
519      * @throws IllegalArgumentException if addr is not an Inet6Address or if mask is not in range 0-128
520      * @throws NullPointerException if addr is null
521      */
522     public final @NonNull Ipv6Prefix ipv6PrefixFor(final @NonNull InetAddress addr, final int mask) {
523         checkArgument(mask >= 0 && mask <= 128, "Invalid mask %s", mask);
524         return prefix6Factory.newInstance(addressStringV6(addr) + '/' + mask);
525     }
526
527     public final @NonNull Ipv6Prefix ipv6PrefixFor(final @NonNull Ipv6Address addr) {
528         return prefix6Factory.newInstance(stripZone(ipv6AddressString(requireAddress(addr))) + "/128");
529     }
530
531     public final @NonNull Ipv6Prefix ipv6PrefixFor(final @NonNull Ipv6Address addr, final int mask) {
532         return newIpv6Prefix(stripZone(ipv6AddressString(requireAddress(addr))), mask);
533     }
534
535     public final @NonNull Ipv6Prefix ipv6PrefixForNoZone(final @NonNull Ipv6AddressNoZone addr) {
536         return prefix6Factory.newInstance(ipv6AddressString(requireAddress(addr)) + "/128");
537     }
538
539     public final @NonNull Ipv6Prefix ipv6PrefixForNoZone(final @NonNull Ipv6AddressNoZone addr, final int mask) {
540         return newIpv6Prefix(ipv6AddressString(requireAddress(addr)), mask);
541     }
542
543     public final @NonNull Ipv6Prefix ipv6PrefixForShort(final byte @NonNull[] address, final int mask) {
544         return ipv6PrefixForShort(address, 0, mask);
545     }
546
547     public final @NonNull Ipv6Prefix ipv6PrefixForShort(final byte @NonNull[] array, final int startOffset,
548             final int mask) {
549         if (mask == 0) {
550             // Easy case, reuse the template
551             return prefix6Factory.getTemplate();
552         }
553
554         checkArgument(mask > 0 && mask <= 128, "Invalid mask %s", mask);
555         final int size = mask / Byte.SIZE + (mask % Byte.SIZE == 0 ? 0 : 1);
556
557         // Until we can instantiate an IPv6 address for a partial array, use a temporary buffer
558         byte[] tmp = new byte[Ipv6Utils.INET6_LENGTH];
559         System.arraycopy(array, startOffset, tmp, 0, size);
560         return ipv6PrefixFor(tmp, mask);
561     }
562
563     private Ipv6Prefix newIpv6Prefix(final String addr, final int mask) {
564         checkArgument(mask >= 0 && mask <= 128, "Invalid mask %s", mask);
565         return prefix6Factory.newInstance(addr + '/' + mask);
566     }
567
568     public final @NonNull Entry<Ipv6AddressNoZone, Integer> splitIpv6Prefix(final @NonNull Ipv6Prefix prefix) {
569         return splitPrefix(address6NoZoneFactory, ipv6PrefixString(prefix));
570     }
571
572     private static <T> @NonNull T prefixToAddress(final StringValueObjectFactory<T> factory, final String str) {
573         return factory.newInstance(str.substring(0, str.lastIndexOf('/')));
574     }
575
576     private static <T> @NonNull Entry<T, Integer> splitPrefix(final StringValueObjectFactory<T> factory,
577             final String str) {
578         final int slash = str.lastIndexOf('/');
579         return new SimpleImmutableEntry<>(factory.newInstance(str.substring(0, slash)),
580                 Integer.valueOf(str.substring(slash + 1)));
581     }
582
583     public final byte @NonNull[] ipv6PrefixToBytes(final @NonNull Ipv6Prefix prefix) {
584         final String str = ipv6PrefixString(prefix);
585         final byte[] bytes = new byte[Ipv6Utils.INET6_LENGTH + 1];
586         final int slash = str.lastIndexOf('/');
587         Ipv6Utils.fillIpv6Bytes(bytes, str, slash);
588         bytes[Ipv6Utils.INET6_LENGTH] = (byte)Integer.parseInt(str.substring(slash + 1), 10);
589         return bytes;
590     }
591
592     private static @NonNull String addressStringV4(final InetAddress addr) {
593         requireAddress(addr);
594         checkArgument(addr instanceof Inet4Address, "Address has to be an Inet4Address");
595         return addr.getHostAddress();
596     }
597
598     private static String addressStringV6(final byte @NonNull[] bytes) {
599         checkArgument(bytes.length == Ipv6Utils.INET6_LENGTH, "IPv6 address length is 16 bytes");
600
601         try {
602             return addressStringV6(Inet6Address.getByAddress(null, bytes, null));
603         } catch (UnknownHostException e) {
604             throw new IllegalArgumentException(String.format("Invalid input %s", bytes), e);
605         }
606     }
607
608     private static String addressStringV6(final InetAddress addr) {
609         requireAddress(addr);
610         checkArgument(addr instanceof Inet6Address, "Address has to be an Inet6Address");
611         return addressStringV6((Inet6Address) addr);
612     }
613
614     private static String addressStringV6(final Inet6Address addr) {
615         return InetAddresses.toAddrString(addr);
616     }
617
618     private static String prefixStringV4(final byte @NonNull[] bytes) {
619         final StringBuilder sb = new StringBuilder(18);
620         Ipv4Utils.appendIpv4String(sb, bytes);
621         return sb.append("/32").toString();
622     }
623
624     private static String prefixStringV4(final byte @NonNull[] bytes, final int mask) {
625         checkArgument(mask >= 0 && mask <= 32, "Invalid mask %s", mask);
626
627         final StringBuilder sb = new StringBuilder(18);
628         Ipv4Utils.appendIpv4String(sb, bytes);
629         return sb.append('/').append(mask).toString();
630     }
631
632     private @NonNull Ipv4Prefix v4PrefixForShort(final byte @NonNull[] array, final int startOffset, final int size,
633             final int mask) {
634         if (startOffset == 0 && size == Ipv4Utils.INET4_LENGTH && array.length == Ipv4Utils.INET4_LENGTH) {
635             // Easy case, fall back to non-short
636             return ipv4PrefixFor(array, mask);
637         }
638
639         final StringBuilder sb = new StringBuilder(18);
640
641         // Add from address
642         sb.append(Byte.toUnsignedInt(array[startOffset]));
643         for (int i = 1; i < size; i++) {
644             sb.append('.').append(Byte.toUnsignedInt(array[startOffset + i]));
645         }
646
647         // Add zeros
648         for (int i = size; i < Ipv4Utils.INET4_LENGTH; i++) {
649             sb.append(".0");
650         }
651
652         // Add mask
653         checkArgument(mask > 0 && mask <= 32, "Invalid mask %s", mask);
654         sb.append('/').append(mask);
655
656         return prefix4Factory.newInstance(sb.toString());
657     }
658
659     private @NonNull Ipv6Address coerceIpv6Address(final @NonNull IpAddress addr) {
660         final var ret = maybeIpv6Address(addr);
661         checkArgument(ret != null, "Address %s is neither IPv4 nor IPv6", addr);
662         return ret;
663     }
664
665     private @NonNull Ipv6AddressNoZone coerceIpv6AddressNoZone(final @NonNull IpAddressNoZone addr) {
666         final var ret = maybeIpv6AddressNoZone(addr);
667         checkArgument(ret != null, "Address %s is neither IPv4 nor IPv6", addr);
668         return ret;
669     }
670 }