2 * Copyright (c) 2016 Pantheon Technologies s.r.o. and others. All rights reserved.
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
8 package org.opendaylight.mdsal.model.ietf.util;
10 import static com.google.common.base.Preconditions.checkArgument;
12 import com.google.common.annotations.Beta;
13 import java.util.HexFormat;
14 import java.util.UUID;
15 import org.eclipse.jdt.annotation.NonNull;
16 import org.opendaylight.mdsal.binding.spec.reflect.StringValueObjectFactory;
19 * Abstract utility class for dealing with MAC addresses as defined in the ietf-yang-types model. This class is
20 * used by revision-specific classes.
22 * @param <M> mac-address type
23 * @param <P> phys-address type
26 public abstract class AbstractIetfYangUtil<M, P, H, Q, U> {
27 private static final int MAC_BYTE_LENGTH = 6;
28 private static final HexFormat COLON_HEXFORMAT = HexFormat.ofDelimiter(":");
29 private static final byte @NonNull[] EMPTY_BYTES = new byte[0];
31 private final StringValueObjectFactory<M> macFactory;
32 private final StringValueObjectFactory<P> physFactory;
33 private final StringValueObjectFactory<H> hexFactory;
34 private final StringValueObjectFactory<Q> quadFactory;
35 private final StringValueObjectFactory<U> uuidFactory;
37 protected AbstractIetfYangUtil(final Class<M> macClass, final Class<P> physClass, final Class<H> hexClass,
38 final Class<Q> quadClass, final Class<U> uuidClass) {
39 this.macFactory = StringValueObjectFactory.create(macClass, "00:00:00:00:00:00");
40 this.physFactory = StringValueObjectFactory.create(physClass, "00:00");
41 this.hexFactory = StringValueObjectFactory.create(hexClass, "00");
42 this.quadFactory = StringValueObjectFactory.create(quadClass, "0.0.0.0");
43 this.uuidFactory = StringValueObjectFactory.create(uuidClass, "f81d4fae-7dec-11d0-a765-00a0c91e6bf6");
47 * Convert the value of a MacAddress into the canonical representation.
49 * @param macAddress Input MAC address
50 * @return A MacAddress containing the canonical representation.
51 * @throws NullPointerException if macAddress is null
53 public final @NonNull M canonizeMacAddress(final @NonNull M macAddress) {
54 final char[] input = getValue(macAddress).toCharArray();
55 return ensureLowerCase(input) ? macFactory.newInstance(String.valueOf(input)) : macAddress;
59 * Create a MacAddress object holding the canonical representation of the 6 bytes
60 * passed in as argument.
61 * @param bytes 6-byte input array
62 * @return MacAddress with canonical string derived from input bytes
63 * @throws NullPointerException if bytes is null
64 * @throws IllegalArgumentException if length of input is not 6 bytes
66 public final @NonNull M macAddressFor(final byte @NonNull[] bytes) {
67 checkArgument(bytes.length == MAC_BYTE_LENGTH, "MAC address should have 6 bytes, not %s", bytes.length);
68 return macFactory.newInstance(COLON_HEXFORMAT.formatHex(bytes));
71 public final byte @NonNull[] macAddressBytes(final @NonNull M macAddress) {
72 return stringToBytes(getValue(macAddress), MAC_BYTE_LENGTH);
76 * Convert the value of a PhysAddress into the canonical representation.
78 * @param physAddress Input MAC address
79 * @return A PhysAddress containing the canonical representation.
80 * @throws NullPointerException if physAddress is null
82 public final @NonNull P canonizePhysAddress(final @NonNull P physAddress) {
83 final char[] input = getPhysValue(physAddress).toCharArray();
84 return ensureLowerCase(input) ? physFactory.newInstance(String.valueOf(input)) : physAddress;
88 * Create a PhysAddress object holding the canonical representation of the bytes passed in as argument.
90 * @param bytes input array
91 * @return PhysAddress with canonical string derived from input bytes
92 * @throws NullPointerException if bytes is null
93 * @throws IllegalArgumentException if length of input is not at least 1 byte
95 public final @NonNull P physAddressFor(final byte @NonNull[] bytes) {
96 checkArgument(bytes.length > 0, "Physical address should have at least one byte");
97 return physFactory.newInstance(COLON_HEXFORMAT.formatHex(bytes));
100 public final byte @NonNull[] physAddressBytes(final @NonNull P physAddress) {
101 final String str = getPhysValue(physAddress);
102 return str.isEmpty() ? EMPTY_BYTES : stringToBytes(str, str.length() / 3 + 1);
105 public final @NonNull H hexStringFor(final byte @NonNull[] bytes) {
106 checkArgument(bytes.length > 0, "Hex string should have at least one byte");
107 return hexFactory.newInstance(COLON_HEXFORMAT.formatHex(bytes));
110 public final byte @NonNull[] hexStringBytes(final @NonNull H hexString) {
111 final String str = getHexValue(hexString);
112 return stringToBytes(str, str.length() / 3 + 1);
115 public final @NonNull Q dottedQuadFor(final byte @NonNull[] bytes) {
116 checkArgument(bytes.length == 4, "Dotted-quad should have 4 bytes");
117 return quadFactory.newInstance(AbstractIetfInetUtil.addressStringV4(bytes));
120 public final @NonNull Q dottedQuadFor(final int bits) {
121 return quadFactory.newInstance(Ipv4Utils.addressString(bits));
124 public final int dottedQuadBits(final @NonNull Q dottedQuad) {
125 final String str = getQuadValue(dottedQuad);
126 return Ipv4Utils.addressBits(str, str.length());
129 public final byte @NonNull[] dottedQuadBytes(final @NonNull Q dottedQuad) {
130 final String str = getQuadValue(dottedQuad);
131 return Ipv4Utils.addressBytes(str, str.length());
134 public final @NonNull U uuidFor(final @NonNull UUID uuid) {
135 return uuidFactory.newInstance(uuid.toString());
138 protected abstract String getValue(M macAddress);
140 protected abstract String getPhysValue(P physAddress);
142 protected abstract String getHexValue(H hexString);
144 protected abstract String getQuadValue(Q dottedQuad);
147 * Make sure an array of characters does not include capital letters. This method assumes input conforms to
148 * MAC address format, e.g. it is composed of 6 groups of hexadecimal digits separated by colons. Behavior is
149 * undefined if the input does not meet this criteria.
151 * @param chars Input characters, may not be null
152 * @return True if the array has been modified
153 * @throws NullPointerException if input is null
155 private static boolean ensureLowerCase(final char @NonNull[] chars) {
158 for (int i = 0; i < chars.length; ++i) {
159 final char c = chars[i];
160 if (c >= 'A' && c <= 'F') {
161 // Weird notation to ensure constant folding to '(char) (c + 32)', a well-known property of ASCII
162 chars[i] = (char) (c + ('a' - 'A'));
170 private static byte @NonNull[] stringToBytes(final String str, final int length) {
171 final byte[] ret = new byte[length];
172 for (int i = 0, base = 0; i < length; ++i, base += 3) {
173 ret[i] = (byte) ((HexFormat.fromHexDigit(str.charAt(base)) << 4)
174 + HexFormat.fromHexDigit(str.charAt(base + 1)));