Remove deprecated ietf-type-util methods
[mdsal.git] / model / ietf / ietf-type-util / src / main / java / org / opendaylight / mdsal / model / ietf / util / AbstractIetfYangUtil.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
12 import com.google.common.annotations.Beta;
13 import java.util.Arrays;
14 import java.util.UUID;
15 import org.eclipse.jdt.annotation.NonNull;
16 import org.opendaylight.mdsal.binding.spec.reflect.StringValueObjectFactory;
17
18 /**
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.
21  *
22  * @param <M> mac-address type
23  * @param <P> phys-address type
24  */
25 @Beta
26 public abstract class AbstractIetfYangUtil<M, P, H, Q, U> {
27     private static final int MAC_BYTE_LENGTH = 6;
28     private static final char[] HEX_CHARS = "0123456789abcdef".toCharArray();
29     private static final byte @NonNull[] EMPTY_BYTES = new byte[0];
30     private static final byte @NonNull[] HEX_VALUES;
31
32     static {
33         final byte[] b = new byte['f' + 1];
34         Arrays.fill(b, (byte)-1);
35
36         for (char c = '0'; c <= '9'; ++c) {
37             b[c] = (byte)(c - '0');
38         }
39         for (char c = 'A'; c <= 'F'; ++c) {
40             b[c] = (byte)(c - 'A' + 10);
41         }
42         for (char c = 'a'; c <= 'f'; ++c) {
43             b[c] = (byte)(c - 'a' + 10);
44         }
45
46         HEX_VALUES = b;
47     }
48
49     private final StringValueObjectFactory<M> macFactory;
50     private final StringValueObjectFactory<P> physFactory;
51     private final StringValueObjectFactory<H> hexFactory;
52     private final StringValueObjectFactory<Q> quadFactory;
53     private final StringValueObjectFactory<U> uuidFactory;
54
55     protected AbstractIetfYangUtil(final Class<M> macClass, final Class<P> physClass, final Class<H> hexClass,
56             final Class<Q> quadClass, final Class<U> uuidClass) {
57         this.macFactory = StringValueObjectFactory.create(macClass, "00:00:00:00:00:00");
58         this.physFactory = StringValueObjectFactory.create(physClass, "00:00");
59         this.hexFactory = StringValueObjectFactory.create(hexClass, "00");
60         this.quadFactory = StringValueObjectFactory.create(quadClass, "0.0.0.0");
61         this.uuidFactory = StringValueObjectFactory.create(uuidClass, "f81d4fae-7dec-11d0-a765-00a0c91e6bf6");
62     }
63
64     /**
65      * Convert the value of a MacAddress into the canonical representation.
66      *
67      * @param macAddress Input MAC address
68      * @return A MacAddress containing the canonical representation.
69      * @throws NullPointerException if macAddress is null
70      */
71     public final @NonNull M canonizeMacAddress(final @NonNull M macAddress) {
72         final char[] input = getValue(macAddress).toCharArray();
73         return ensureLowerCase(input) ? macFactory.newInstance(String.valueOf(input)) : macAddress;
74     }
75
76     /**
77      * Create a MacAddress object holding the canonical representation of the 6 bytes
78      * passed in as argument.
79      * @param bytes 6-byte input array
80      * @return MacAddress with canonical string derived from input bytes
81      * @throws NullPointerException if bytes is null
82      * @throws IllegalArgumentException if length of input is not 6 bytes
83      */
84     public final @NonNull M macAddressFor(final byte @NonNull[] bytes) {
85         checkArgument(bytes.length == MAC_BYTE_LENGTH, "MAC address should have 6 bytes, not %s",
86                 bytes.length);
87         return macFactory.newInstance(bytesToString(bytes, 17));
88     }
89
90     public final byte @NonNull[] macAddressBytes(final @NonNull M macAddress) {
91         return stringToBytes(getValue(macAddress), MAC_BYTE_LENGTH);
92     }
93
94     /**
95      * Convert the value of a PhysAddress into the canonical representation.
96      *
97      * @param physAddress Input MAC address
98      * @return A PhysAddress containing the canonical representation.
99      * @throws NullPointerException if physAddress is null
100      */
101     public final @NonNull P canonizePhysAddress(final @NonNull P physAddress) {
102         final char[] input = getPhysValue(physAddress).toCharArray();
103         return ensureLowerCase(input) ? physFactory.newInstance(String.valueOf(input)) : physAddress;
104     }
105
106     /**
107      * Create a PhysAddress object holding the canonical representation of the bytes passed in as argument.
108      *
109      * @param bytes input array
110      * @return PhysAddress with canonical string derived from input bytes
111      * @throws NullPointerException if bytes is null
112      * @throws IllegalArgumentException if length of input is not at least 1 byte
113      */
114     public final @NonNull P physAddressFor(final byte @NonNull[] bytes) {
115         checkArgument(bytes.length > 0, "Physical address should have at least one byte");
116         return physFactory.newInstance(bytesToString(bytes, bytes.length * 3 - 1));
117     }
118
119     public final byte @NonNull[] physAddressBytes(final @NonNull P physAddress) {
120         final String str = getPhysValue(physAddress);
121         return str.isEmpty() ? EMPTY_BYTES : stringToBytes(str, str.length() / 3 + 1);
122     }
123
124     public final @NonNull H hexStringFor(final byte @NonNull[] bytes) {
125         checkArgument(bytes.length > 0, "Hex string should have at least one byte");
126         return hexFactory.newInstance(bytesToString(bytes, bytes.length * 3 - 1));
127     }
128
129     public final byte @NonNull[] hexStringBytes(final @NonNull H hexString) {
130         final String str = getHexValue(hexString);
131         return stringToBytes(str, str.length() / 3 + 1);
132     }
133
134     public final @NonNull Q dottedQuadFor(final byte @NonNull[] bytes) {
135         checkArgument(bytes.length == 4, "Dotted-quad should have 4 bytes");
136         return quadFactory.newInstance(AbstractIetfInetUtil.addressStringV4(bytes));
137     }
138
139     public final @NonNull Q dottedQuadFor(final int bits) {
140         return quadFactory.newInstance(Ipv4Utils.addressString(bits));
141     }
142
143     public final int dottedQuadBits(final @NonNull Q dottedQuad) {
144         final String str = getQuadValue(dottedQuad);
145         return Ipv4Utils.addressBits(str, str.length());
146     }
147
148     public final byte @NonNull[] dottedQuadBytes(final @NonNull Q dottedQuad) {
149         final String str = getQuadValue(dottedQuad);
150         return Ipv4Utils.addressBytes(str, str.length());
151     }
152
153     public final @NonNull U uuidFor(final @NonNull UUID uuid) {
154         return uuidFactory.newInstance(uuid.toString());
155     }
156
157     protected abstract String getValue(M macAddress);
158
159     protected abstract String getPhysValue(P physAddress);
160
161     protected abstract String getHexValue(H hexString);
162
163     protected abstract String getQuadValue(Q dottedQuad);
164
165     static byte hexValue(final char ch) {
166         byte value;
167         try {
168             // Performance optimization: access the array and rely on the VM for catching
169             // illegal access (which boils down to illegal character, which should never happen.
170             value = HEX_VALUES[ch];
171         } catch (IndexOutOfBoundsException e) {
172             value = -1;
173         }
174
175         if (value < 0) {
176             throw new IllegalArgumentException("Invalid character '" + ch + "' encountered");
177         }
178
179         return value;
180     }
181
182     /**
183      * Make sure an array of characters does not include capital letters. This method assumes input conforms to
184      * MAC address format, e.g. it is composed of 6 groups of hexadecimal digits separated by colons. Behavior is
185      * undefined if the input does not meet this criteria.
186      *
187      * @param chars Input characters, may not be null
188      * @return True if the array has been modified
189      * @throws NullPointerException if input is null
190      */
191     private static boolean ensureLowerCase(final char @NonNull[] chars) {
192         boolean ret = false;
193
194         for (int i = 0; i < chars.length; ++i) {
195             final char c = chars[i];
196             if (c >= 'A' && c <= 'F') {
197                 chars[i] = Character.toLowerCase(c);
198                 ret = true;
199             }
200         }
201
202         return ret;
203     }
204
205     /**
206      * Convert an array of 6 bytes into canonical MAC address representation, that is 6 groups of two hexadecimal
207      * lower-case digits each, separated by colons.
208      *
209      * @param bytes Input bytes, may not be null
210      * @param charHint Hint at how many characters are needed
211      * @return Canonical MAC address string
212      * @throws NullPointerException if input is null
213      * @throws IllegalArgumentException if length of input is not 6 bytes
214      */
215     private static @NonNull String bytesToString(final byte @NonNull[] bytes, final int charHint) {
216         final StringBuilder sb = new StringBuilder(charHint);
217         appendHexByte(sb, bytes[0]);
218         for (int i = 1; i < bytes.length; ++i) {
219             appendHexByte(sb.append(':'), bytes[i]);
220         }
221
222         return sb.toString();
223     }
224
225     private static void appendHexByte(final StringBuilder sb, final byte byteVal) {
226         final int intVal = Byte.toUnsignedInt(byteVal);
227         sb.append(HEX_CHARS[intVal >>> 4]).append(HEX_CHARS[intVal & 15]);
228     }
229
230     private static byte @NonNull[] stringToBytes(final String str, final int length) {
231         final byte[] ret = new byte[length];
232         for (int i = 0, base = 0; i < length; ++i, base += 3) {
233             ret[i] = (byte) (hexValue(str.charAt(base)) << 4 | hexValue(str.charAt(base + 1)));
234         }
235         return ret;
236     }
237 }