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