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         requireNonNull(addr, "Address must not be null");
253         checkArgument(addr instanceof Inet4Address, "Address has to be an Inet4Address");
254         return address4Factory.newInstance(addr.getHostAddress());
255     }
256
257     /**
258      * Create an Ipv4Address by interpreting input 32 bits as an IPv4 address in big-endian format.
259      *
260      * @param bits 32 bits, big endian
261      * @return An Ipv4Address object
262      */
263     public final @NonNull A4 ipv4AddressFor(final int bits) {
264         return address4Factory.newInstance(Ipv4Utils.addressString(bits));
265     }
266
267     /**
268      * Create an Ipv4AddressNoZone by interpreting input bytes as an IPv4 address.
269      *
270      * @param bytes 4-byte array
271      * @return An Ipv4AddressNoZone object
272      * @throws IllegalArgumentException if bytes has length different from 4
273      * @throws NullPointerException if bytes is null
274      */
275     public final @NonNull A4NZ ipv4AddressNoZoneFor(final byte @NonNull[] bytes) {
276         return address4NoZoneFactory.newInstance(addressStringV4(bytes));
277     }
278
279     /**
280      * Create an Ipv4AddressNoZone by interpreting an {@link Inet4Address}.
281      *
282      * @param addr An {@link Inet4Address}
283      * @return An Ipv4AddressNoZone object
284      * @throws IllegalArgumentException if addr is not an {@link Inet4Address}
285      * @throws NullPointerException if addr is null
286      */
287     public final @NonNull A4NZ ipv4AddressNoZoneFor(final @NonNull InetAddress addr) {
288         requireNonNull(addr, "Address must not be null");
289         checkArgument(addr instanceof Inet4Address, "Address has to be an Inet4Address");
290         return address4NoZoneFactory.newInstance(addr.getHostAddress());
291     }
292
293     /**
294      * Create an Ipv4AddressNoZone by interpreting input 32 bits as an IPv4 address in big-endian format.
295      *
296      * @param bits 32 bits, big endian
297      * @return An Ipv4AddressNoZone object
298      */
299     public final @NonNull A4NZ ipv4AddressNoZoneFor(final int bits) {
300         return address4NoZoneFactory.newInstance(Ipv4Utils.addressString(bits));
301     }
302
303     /**
304      * Create an Ipv4AddressNoZone by interpreting an Ipv4Address.
305      *
306      * @param addr An Ipv4Address
307      * @return An Ipv4AddressNoZone object
308      * @throws NullPointerException if addr is null
309      */
310     public final @NonNull A4NZ ipv4AddressNoZoneFor(final @NonNull A4 addr) {
311         requireNonNull(addr, "Address must not be null");
312         return addr4nzClass.isInstance(addr) ? addr4nzClass.cast(addr)
313                 : address4NoZoneFactory.newInstance(stripZone(ipv4AddressString(addr)));
314     }
315
316     public final @NonNull A4 ipv4AddressFrom(final @NonNull P4 prefix) {
317         return prefixToAddress(address4Factory, ipv4PrefixString(prefix));
318     }
319
320     public final @NonNull A4NZ ipv4AddressNoZoneFrom(final @NonNull P4 prefix) {
321         return prefixToAddress(address4NoZoneFactory, ipv4PrefixString(prefix));
322     }
323
324     public final byte @NonNull[] ipv4AddressBytes(final @NonNull A4 addr) {
325         /*
326          * This implementation relies heavily on the input string having been validated to comply with
327          * the Ipv4Address pattern, which may include a zone index.
328          */
329         final String str = ipv4AddressString(addr);
330         final int percent = str.indexOf('%');
331         return Ipv4Utils.addressBytes(str, percent == -1 ? str.length() : percent);
332     }
333
334     public final int ipv4AddressBits(final @NonNull A4 addr) {
335         final String str = ipv4AddressString(addr);
336         final int percent = str.indexOf('%');
337         return Ipv4Utils.addressBits(str, percent == -1 ? str.length() : percent);
338     }
339
340     public final byte @NonNull[] ipv4AddressNoZoneBytes(final @NonNull A4NZ addr) {
341         /*
342          * This implementation relies heavily on the input string having been validated to comply with
343          * the Ipv4AddressNoZone pattern, which must not include a zone index.
344          */
345         final String str = ipv4AddressString(addr);
346         return Ipv4Utils.addressBytes(str, str.length());
347     }
348
349     public final int ipv4AddressNoZoneBits(final @NonNull A4NZ addr) {
350         final String str = ipv4AddressString(addr);
351         return Ipv4Utils.addressBits(str, str.length());
352     }
353
354     /**
355      * Create a /32 Ipv4Prefix by interpreting input bytes as an IPv4 address.
356      *
357      * @param bytes four-byte array
358      * @return An Ipv4Prefix object
359      * @throws IllegalArgumentException if bytes has length different from 4
360      * @throws NullPointerException if bytes is null
361      */
362     public final @NonNull P4 ipv4PrefixFor(final byte @NonNull[] bytes) {
363         return prefix4Factory.newInstance(prefixStringV4(bytes));
364     }
365
366     /**
367      * Create a Ipv4Prefix by combining the address with a mask. The address
368      * bytes are interpreted as an address and the specified mask is concatenated to
369      * it. The address bytes are not masked, hence input <code>address = { 1, 2, 3, 4 }</code>
370      * and <code>mask=24</code> will result in <code>1.2.3.4/24</code>.
371      *
372      * @param address Input address as a 4-byte array
373      * @param mask Prefix mask
374      * @return An Ipv4Prefix object
375      * @throws IllegalArgumentException if bytes has length different from 4 or if mask is not in range 0-32
376      * @throws NullPointerException if bytes is null
377      */
378     public final @NonNull P4 ipv4PrefixFor(final byte @NonNull[] address, final int mask) {
379         return prefix4Factory.newInstance(prefixStringV4(address, mask));
380     }
381
382     /**
383      * Create a /32 Ipv4Prefix for an {@link Inet4Address}.
384      *
385      * @param addr An {@link Inet4Address}
386      * @return An Ipv4Prefix object
387      * @throws IllegalArgumentException if addr is not an Inet4Address
388      * @throws NullPointerException if addr is null
389      */
390     public final @NonNull P4 ipv4PrefixFor(final @NonNull InetAddress addr) {
391         requireNonNull(addr, "Address must not be null");
392         checkArgument(addr instanceof Inet4Address, "Address has to be an Inet4Address");
393         return prefix4Factory.newInstance(addr.getHostAddress() + "/32");
394     }
395
396     /**
397      * Create a Ipv4Prefix by combining the address with a mask. The address bytes are not masked.
398      *
399      * @param addr An {@link Inet4Address}
400      * @param mask Prefix mask
401      * @return An Ipv4Prefix object
402      * @throws IllegalArgumentException if addr is not an Inet4Address or if mask is not in range 0-32
403      * @throws NullPointerException if addr is null
404      */
405     public final @NonNull P4 ipv4PrefixFor(final @NonNull InetAddress addr, final int mask) {
406         requireNonNull(addr, "Address must not be null");
407         checkArgument(addr instanceof Inet4Address, "Address has to be an Inet4Address");
408         return newIpv4Prefix(addr.getHostAddress(), mask);
409     }
410
411     public final @NonNull P4 ipv4PrefixFor(final @NonNull A4 addr) {
412         requireNonNull(addr, "Address must not be null");
413         return prefix4Factory.newInstance(stripZone(ipv4AddressString(addr)) + "/32");
414     }
415
416     public final @NonNull P4 ipv4PrefixFor(final @NonNull A4 addr, final int mask) {
417         requireNonNull(addr, "Address must not be null");
418         return newIpv4Prefix(stripZone(ipv4AddressString(addr)), mask);
419     }
420
421     public final @NonNull P4 ipv4PrefixForNoZone(final @NonNull A4NZ addr) {
422         requireNonNull(addr, "Address must not be null");
423         return prefix4Factory.newInstance(ipv4AddressString(addr) + "/32");
424     }
425
426     public final @NonNull P4 ipv4PrefixForNoZone(final @NonNull A4NZ addr, final int mask) {
427         requireNonNull(addr, "Address must not be null");
428         return newIpv4Prefix(ipv4AddressString(addr), mask);
429     }
430
431     public final @NonNull P4 ipv4PrefixForShort(final byte @NonNull[] address, final int mask) {
432         if (mask == 0) {
433             // Easy case, reuse the template
434             return prefix4Factory.getTemplate();
435         }
436
437         return v4PrefixForShort(address, 0, mask / Byte.SIZE + (mask % Byte.SIZE == 0 ? 0 : 1), mask);
438     }
439
440     public final @NonNull P4 ipv4PrefixForShort(final byte @NonNull[] array, final int startOffset, final int mask) {
441         if (mask == 0) {
442             // Easy case, reuse the template
443             return prefix4Factory.getTemplate();
444         }
445
446         return v4PrefixForShort(array, startOffset, mask / Byte.SIZE + (mask % Byte.SIZE == 0 ? 0 : 1), mask);
447     }
448
449     private static String stripZone(final String str) {
450         final int percent = str.indexOf('%');
451         return percent == -1 ? str : str.substring(0, percent);
452     }
453
454     private @NonNull P4 newIpv4Prefix(final String addr, final int mask) {
455         checkArgument(mask >= 0 && mask <= 32, "Invalid mask %s", mask);
456         return prefix4Factory.newInstance(addr + '/' + mask);
457     }
458
459     public final @NonNull Entry<A4, Integer> splitIpv4Prefix(final @NonNull P4 prefix) {
460         return splitPrefix(address4Factory, ipv4PrefixString(prefix));
461     }
462
463     public final @NonNull Entry<A4NZ, Integer> splitIpv4PrefixNoZone(final @NonNull P4 prefix) {
464         return splitPrefix(address4NoZoneFactory, ipv4PrefixString(prefix));
465     }
466
467     public final byte @NonNull[] ipv4PrefixToBytes(final @NonNull P4 prefix) {
468         final String str = ipv4PrefixString(prefix);
469         final int slash = str.lastIndexOf('/');
470
471         final byte[] bytes = new byte[INET4_LENGTH + 1];
472         Ipv4Utils.fillIpv4Bytes(bytes, 0, str, 0, slash);
473         bytes[INET4_LENGTH] = (byte)Integer.parseInt(str.substring(slash + 1), 10);
474         return bytes;
475     }
476
477     /**
478      * Create an Ipv6Address by interpreting input bytes as an IPv6 address.
479      *
480      * @param bytes 16-byte array
481      * @return An Ipv6Address object
482      * @throws IllegalArgumentException if bytes has length different from 16
483      * @throws NullPointerException if bytes is null
484      */
485     public final @NonNull A6 ipv6AddressFor(final byte @NonNull[] bytes) {
486         return address6Factory.newInstance(addressStringV6(bytes));
487     }
488
489     /**
490      * Create an Ipv6Address by interpreting an {@link Inet6Address}.
491      *
492      * @param addr An {@link Inet6Address}
493      * @return An Ipv6Address object
494      * @throws IllegalArgumentException if addr is not an {@link Inet6Address}
495      * @throws NullPointerException if addr is null
496      */
497     public final @NonNull A6 ipv6AddressFor(final @NonNull InetAddress addr) {
498         return address6Factory.newInstance(addressStringV6(addr));
499     }
500
501     /**
502      * Create an Ipv6AddressNoZone by interpreting input bytes as an IPv6 address.
503      *
504      * @param bytes 16-byte array
505      * @return An Ipv6Address object
506      * @throws IllegalArgumentException if bytes has length different from 16
507      * @throws NullPointerException if bytes is null
508      */
509     public final @NonNull A6NZ ipv6AddressNoZoneFor(final byte @NonNull[] bytes) {
510         return address6NoZoneFactory.newInstance(addressStringV6(bytes));
511     }
512
513     /**
514      * Create an Ipv6AddressNoZone by interpreting an {@link Inet6Address}.
515      *
516      * @param addr An {@link Inet6Address}
517      * @return An Ipv6AddressNoZone object
518      * @throws IllegalArgumentException if addr is not an {@link Inet6Address}
519      * @throws NullPointerException if addr is null
520      */
521     public final @NonNull A6NZ ipv6AddressNoZoneFor(final @NonNull InetAddress addr) {
522         return address6NoZoneFactory.newInstance(addressStringV6(addr));
523     }
524
525     /**
526      * Create an Ipv6AddressNoZone by interpreting an Ipv6Address.
527      *
528      * @param addr An Ipv6Address
529      * @return An Ipv6AddressNoZone object
530      * @throws NullPointerException if addr is null
531      */
532     public final @NonNull A6NZ ipv6AddressNoZoneFor(final @NonNull A6 addr) {
533         requireNonNull(addr, "Address must not be null");
534         return addr6nzClass.isInstance(addr) ? addr6nzClass.cast(addr)
535                 : address6NoZoneFactory.newInstance(stripZone(ipv6AddressString(addr)));
536     }
537
538     public final @NonNull A6 ipv6AddressFrom(final @NonNull P6 prefix) {
539         return prefixToAddress(address6Factory, ipv6PrefixString(prefix));
540     }
541
542     public final @NonNull A6NZ ipv6AddressNoZoneFrom(final @NonNull P6 prefix) {
543         return prefixToAddress(address6NoZoneFactory, ipv6PrefixString(prefix));
544     }
545
546     public final byte @NonNull[] ipv6AddressBytes(final @NonNull A6 addr) {
547         final String str = ipv6AddressString(addr);
548         final int percent = str.indexOf('%');
549         return ipv6StringBytes(str, percent == -1 ? str.length() : percent);
550     }
551
552     public final byte @NonNull[] ipv6AddressNoZoneBytes(final @NonNull A6NZ addr) {
553         final String str = ipv6AddressString(addr);
554         return ipv6StringBytes(str, str.length());
555     }
556
557     private static byte @NonNull[] ipv6StringBytes(final @NonNull String str, final int limit) {
558         final byte[] bytes = new byte[INET6_LENGTH];
559         Ipv6Utils.fillIpv6Bytes(bytes, str, limit);
560         return bytes;
561     }
562
563     /**
564      * Create a /128 Ipv6Prefix by interpreting input bytes as an IPv6 address.
565      *
566      * @param bytes four-byte array
567      * @return An Ipv6Prefix object
568      * @throws IllegalArgumentException if bytes has length different from 16
569      * @throws NullPointerException if bytes is null
570      */
571     public final @NonNull P6 ipv6PrefixFor(final byte @NonNull[] bytes) {
572         return prefix6Factory.newInstance(addressStringV6(bytes) + "/128");
573     }
574
575     /**
576      * Create a Ipv6Prefix by combining the address with a mask. The address
577      * bytes are interpreted as an address and the specified mask is concatenated to
578      * it. The address bytes are not masked.
579      *
580      * @param address Input address as a 16-byte array
581      * @param mask Prefix mask
582      * @return An Ipv6Prefix object
583      * @throws IllegalArgumentException if bytes has length different from 16 or if mask is not in range 0-128
584      * @throws NullPointerException if bytes is null
585      */
586     public final @NonNull P6 ipv6PrefixFor(final byte @NonNull[] address, final int mask) {
587         checkArgument(mask >= 0 && mask <= 128, "Invalid mask %s", mask);
588         return prefix6Factory.newInstance(addressStringV6(address) + '/' + mask);
589     }
590
591     /**
592      * Create a /128 Ipv6Prefix by interpreting input bytes as an IPv6 address.
593      *
594      * @param addr an {@link Inet6Address}
595      * @return An Ipv6Prefix object
596      * @throws IllegalArgumentException if addr is not an Inet6Address
597      * @throws NullPointerException if addr is null
598      */
599     public final @NonNull P6 ipv6PrefixFor(final @NonNull InetAddress addr) {
600         return prefix6Factory.newInstance(addressStringV6(addr) + "/128");
601     }
602
603     /**
604      * Create a Ipv6Prefix by combining the address with a mask. The address
605      * bytes are interpreted as an address and the specified mask is concatenated to
606      * it. The address bytes are not masked.
607      *
608      * @param addr Input address
609      * @param mask Prefix mask
610      * @return An Ipv6Prefix object
611      * @throws IllegalArgumentException if addr is not an Inet6Address or if mask is not in range 0-128
612      * @throws NullPointerException if addr is null
613      */
614     public final @NonNull P6 ipv6PrefixFor(final @NonNull InetAddress addr, final int mask) {
615         checkArgument(mask >= 0 && mask <= 128, "Invalid mask %s", mask);
616         return prefix6Factory.newInstance(addressStringV6(addr) + '/' + mask);
617     }
618
619     public final @NonNull P6 ipv6PrefixFor(final @NonNull A6 addr) {
620         requireNonNull(addr, "Address must not be null");
621         return prefix6Factory.newInstance(stripZone(ipv6AddressString(addr)) + "/128");
622     }
623
624     public final @NonNull P6 ipv6PrefixFor(final @NonNull A6 addr, final int mask) {
625         requireNonNull(addr, "Address must not be null");
626         return newIpv6Prefix(stripZone(ipv6AddressString(addr)), mask);
627     }
628
629     public final @NonNull P6 ipv6PrefixForNoZone(final @NonNull A6NZ addr) {
630         requireNonNull(addr, "Address must not be null");
631         return prefix6Factory.newInstance(ipv6AddressString(addr) + "/128");
632     }
633
634     public final @NonNull P6 ipv6PrefixForNoZone(final @NonNull A6NZ addr, final int mask) {
635         requireNonNull(addr, "Address must not be null");
636         return newIpv6Prefix(ipv6AddressString(addr), mask);
637     }
638
639     public final @NonNull P6 ipv6PrefixForShort(final byte @NonNull[] address, final int mask) {
640         return ipv6PrefixForShort(address, 0, mask);
641     }
642
643     public final @NonNull P6 ipv6PrefixForShort(final byte @NonNull[] array, final int startOffset, final int mask) {
644         if (mask == 0) {
645             // Easy case, reuse the template
646             return prefix6Factory.getTemplate();
647         }
648
649         checkArgument(mask > 0 && mask <= 128, "Invalid mask %s", mask);
650         final int size = mask / Byte.SIZE + (mask % Byte.SIZE == 0 ? 0 : 1);
651
652         // Until we can instantiate an IPv6 address for a partial array, use a temporary buffer
653         byte[] tmp = new byte[INET6_LENGTH];
654         System.arraycopy(array, startOffset, tmp, 0, size);
655         return ipv6PrefixFor(tmp, mask);
656     }
657
658     private P6 newIpv6Prefix(final String addr, final int mask) {
659         checkArgument(mask >= 0 && mask <= 128, "Invalid mask %s", mask);
660         return prefix6Factory.newInstance(addr + '/' + mask);
661     }
662
663     public final @NonNull Entry<A6, Integer> splitIpv6Prefix(final @NonNull P6 prefix) {
664         return splitPrefix(address6Factory, ipv6PrefixString(prefix));
665     }
666
667     public final @NonNull Entry<A6NZ, Integer> splitIpv6PrefixNoZone(final @NonNull P6 prefix) {
668         return splitPrefix(address6NoZoneFactory, ipv6PrefixString(prefix));
669     }
670
671     private static <T> @NonNull T prefixToAddress(final StringValueObjectFactory<T> factory, final String str) {
672         return factory.newInstance(str.substring(0, str.lastIndexOf('/')));
673     }
674
675     private static <T> @NonNull Entry<T, Integer> splitPrefix(final StringValueObjectFactory<T> factory,
676             final String str) {
677         final int slash = str.lastIndexOf('/');
678         return new SimpleImmutableEntry<>(factory.newInstance(str.substring(0, slash)),
679                 Integer.valueOf(str.substring(slash + 1)));
680     }
681
682     public final byte @NonNull[] ipv6PrefixToBytes(final @NonNull P6 prefix) {
683         final String str = ipv6PrefixString(prefix);
684         final byte[] bytes = new byte[INET6_LENGTH + 1];
685         final int slash = str.lastIndexOf('/');
686         Ipv6Utils.fillIpv6Bytes(bytes, str, slash);
687         bytes[INET6_LENGTH] = (byte)Integer.parseInt(str.substring(slash + 1), 10);
688         return bytes;
689     }
690
691     private static void appendIpv4String(final StringBuilder sb, final byte @NonNull[] bytes) {
692         checkArgument(bytes.length == INET4_LENGTH, "IPv4 address length is 4 bytes");
693
694         sb.append(Byte.toUnsignedInt(bytes[0]));
695         for (int i = 1; i < INET4_LENGTH; ++i) {
696             sb.append('.').append(Byte.toUnsignedInt(bytes[i]));
697         }
698     }
699
700     static String addressStringV4(final byte @NonNull[] bytes) {
701         final StringBuilder sb = new StringBuilder(15);
702         appendIpv4String(sb, bytes);
703         return sb.toString();
704     }
705
706     private static String addressStringV6(final byte @NonNull[] bytes) {
707         checkArgument(bytes.length == INET6_LENGTH, "IPv6 address length is 16 bytes");
708
709         try {
710             return addressStringV6(Inet6Address.getByAddress(null, bytes, null));
711         } catch (UnknownHostException e) {
712             throw new IllegalArgumentException(String.format("Invalid input %s", bytes), e);
713         }
714     }
715
716     private static String addressStringV6(final InetAddress addr) {
717         requireNonNull(addr, "Address must not be null");
718         checkArgument(addr instanceof Inet6Address, "Address has to be an Inet6Address");
719         return addressStringV6((Inet6Address) addr);
720     }
721
722     private static String addressStringV6(final Inet6Address addr) {
723         return InetAddresses.toAddrString(addr);
724     }
725
726     private static String prefixStringV4(final byte @NonNull[] bytes) {
727         final StringBuilder sb = new StringBuilder(18);
728         appendIpv4String(sb, bytes);
729         return sb.append("/32").toString();
730     }
731
732     private static String prefixStringV4(final byte @NonNull[] bytes, final int mask) {
733         checkArgument(mask >= 0 && mask <= 32, "Invalid mask %s", mask);
734
735         final StringBuilder sb = new StringBuilder(18);
736         appendIpv4String(sb, bytes);
737         return sb.append('/').append(mask).toString();
738     }
739
740     private P4 v4PrefixForShort(final byte @NonNull[] array, final int startOffset, final int size, final int mask) {
741         if (startOffset == 0 && size == INET4_LENGTH && array.length == INET4_LENGTH) {
742             // Easy case, fall back to non-short
743             return ipv4PrefixFor(array, mask);
744         }
745
746         final StringBuilder sb = new StringBuilder(18);
747
748         // Add from address
749         sb.append(Byte.toUnsignedInt(array[startOffset]));
750         for (int i = 1; i < size; i++) {
751             sb.append('.').append(Byte.toUnsignedInt(array[startOffset + i]));
752         }
753
754         // Add zeros
755         for (int i = size; i < INET4_LENGTH; i++) {
756             sb.append(".0");
757         }
758
759         // Add mask
760         checkArgument(mask > 0 && mask <= 32, "Invalid mask %s", mask);
761         sb.append('/').append(mask);
762
763         return prefix4Factory.newInstance(sb.toString());
764     }
765 }