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.Arrays;
14 import javax.annotation.Nonnull;
15 import org.opendaylight.mdsal.binding.spec.reflect.StringValueObjectFactory;
18 * Abstract utility class for dealing with MAC addresses as defined in the ietf-yang-types model. This class is
19 * used by revision-specific classes.
21 * @param <M> mac-address type
22 * @param <P> phys-address type
25 public abstract class AbstractIetfYangUtil<M, P> {
26 private static final int MAC_BYTE_LENGTH = 6;
27 private static final char[] HEX_CHARS = "0123456789abcdef".toCharArray();
28 private static final byte[] HEX_VALUES;
30 final byte[] b = new byte['f' + 1];
31 Arrays.fill(b, (byte)-1);
33 for (char c = '0'; c <= '9'; ++c) {
34 b[c] = (byte)(c - '0');
36 for (char c = 'A'; c <= 'F'; ++c) {
37 b[c] = (byte)(c - 'A' + 10);
39 for (char c = 'a'; c <= 'f'; ++c) {
40 b[c] = (byte)(c - 'a' + 10);
46 private final StringValueObjectFactory<M> macFactory;
47 private final StringValueObjectFactory<P> physFactory;
49 protected AbstractIetfYangUtil(final Class<M> macClass, final Class<P> physClass) {
50 this.macFactory = StringValueObjectFactory.create(macClass, "00:00:00:00:00:00");
51 this.physFactory = StringValueObjectFactory.create(physClass, "00:00");
56 * Convert the value of a MacAddress into the canonical representation.
58 * @param macAddress Input MAC address
59 * @return A MacAddress containing the canonical representation.
60 * @throws NullPointerException if macAddress is null
62 @Nonnull public final M canonizeMacAddress(@Nonnull final M macAddress) {
63 final char[] input = getValue(macAddress).toCharArray();
64 return ensureLowerCase(input) ? macFactory.newInstance(String.valueOf(input)) : macAddress;
68 * Create a MacAddress object holding the canonical representation of the 6 bytes
69 * passed in as argument.
70 * @param bytes 6-byte input array
71 * @return MacAddress with canonical string derived from input bytes
72 * @throws NullPointerException if bytes is null
73 * @throws IllegalArgumentException if length of input is not 6 bytes
75 @Nonnull public final M macAddressFor(@Nonnull final byte[] bytes) {
76 checkArgument(bytes.length == MAC_BYTE_LENGTH, "MAC address should have 6 bytes, not %s",
78 return macFactory.newInstance(bytesToString(bytes, 17));
82 * Convert the value of a PhysAddress into the canonical representation.
84 * @param physAddress Input MAC address
85 * @return A PhysAddress containing the canonical representation.
86 * @throws NullPointerException if physAddress is null
88 @Nonnull public final P canonizePhysAddress(@Nonnull final P physAddress) {
89 final char[] input = getPhysValue(physAddress).toCharArray();
90 return ensureLowerCase(input) ? physFactory.newInstance(String.valueOf(input)) : physAddress;
94 * Create a PhysAddress object holding the canonical representation of the bytes passed in as argument.
96 * @param bytes input array
97 * @return PhysAddress with canonical string derived from input bytes
98 * @throws NullPointerException if bytes is null
99 * @throws IllegalArgumentException if length of input is not at least 1 byte
101 @Nonnull public final P physAddressFor(@Nonnull final byte[] bytes) {
102 checkArgument(bytes.length > 0, "Physical address should have at least one byte");
103 return physFactory.newInstance(bytesToString(bytes, (bytes.length + 1) / 3));
106 @Nonnull public final byte[] bytesFor(@Nonnull final M macAddress) {
107 final String mac = getValue(macAddress);
108 final byte[] ret = new byte[MAC_BYTE_LENGTH];
110 for (int i = 0, base = 0; i < MAC_BYTE_LENGTH; ++i, base += 3) {
111 ret[i] = (byte) (hexValue(mac.charAt(base)) << 4 | hexValue(mac.charAt(base + 1)));
117 protected abstract String getValue(M macAddress);
119 protected abstract String getPhysValue(P physAddress);
121 static byte hexValue(final char c) {
124 // Performance optimization: access the array and rely on the VM for catching
125 // illegal access (which boils down to illegal character, which should never happen.
127 } catch (IndexOutOfBoundsException e) {
132 throw new IllegalArgumentException("Invalid character '" + c + "' encountered");
139 * Make sure an array of characters does not include capital letters. This method assumes input conforms to
140 * MAC address format, e.g. it is composed of 6 groups of hexadecimal digits separated by colons. Behavior is
141 * undefined if the input does not meet this criteria.
143 * @param chars Input characters, may not be null
144 * @return True if the array has been modified
145 * @throws NullPointerException if input is null
147 private static boolean ensureLowerCase(@Nonnull final char[] chars) {
150 for (int i = 0; i < chars.length; ++i) {
151 final char c = chars[i];
152 if (c >= 'A' && c <= 'F') {
153 chars[i] = Character.toLowerCase(c);
162 * Convert an array of 6 bytes into canonical MAC address representation, that is 6 groups of two hexadecimal
163 * lower-case digits each, separated by colons.
165 * @param bytes Input bytes, may not be null
166 * @param charHint Hint at how many characters are needed
167 * @return Canonical MAC address string
168 * @throws NullPointerException if input is null
169 * @throws IllegalArgumentException if length of input is not 6 bytes
171 @Nonnull private static String bytesToString(@Nonnull final byte[] bytes, final int charHint) {
172 final StringBuilder sb = new StringBuilder(charHint);
173 appendHexByte(sb, bytes[0]);
174 for (int i = 1; i < bytes.length; ++i) {
175 appendHexByte(sb.append(':'), bytes[i]);
178 return sb.toString();
181 private static final void appendHexByte(final StringBuilder sb, final byte b) {
182 final int v = Byte.toUnsignedInt(b);
183 sb.append(HEX_CHARS[v >>> 4]).append(HEX_CHARS[v & 15]);