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