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