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