001/**
002 * Copyright 2017, Digi International Inc.
003 *
004 * This Source Code Form is subject to the terms of the Mozilla Public
005 * License, v. 2.0. If a copy of the MPL was not distributed with this
006 * file, you can obtain one at http://mozilla.org/MPL/2.0/.
007 *
008 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 
009 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 
010 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 
011 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 
012 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
013 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
014 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
015 */
016package com.digi.xbee.api.models;
017
018import java.util.Arrays;
019
020import com.digi.xbee.api.utils.HexUtils;
021
022/**
023 * This class represents a 64-bit address (also known as MAC address). 
024 * 
025 * <p>The 64-bit address is a unique device address assigned during 
026 * manufacturing. This address is unique to each physical device.</p>
027 */
028public final class XBee64BitAddress {
029
030        // Constants
031        /**
032         * 64-bit address reserved for the coordinator (value: 0000000000000000).
033         */
034        public static final XBee64BitAddress COORDINATOR_ADDRESS = new XBee64BitAddress("0000");
035        /**
036         * 64-bit broadcast address (value: 000000000000FFFF).
037         */
038        public static final XBee64BitAddress BROADCAST_ADDRESS = new XBee64BitAddress("FFFF");
039        /**
040         * 64-bit unknown address (value: FFFFFFFFFFFFFFFF).
041         */
042        public static final XBee64BitAddress UNKNOWN_ADDRESS = new XBee64BitAddress("FFFFFFFFFFFFFFFF");
043        
044        private static final String DEVICE_ID_SEPARATOR = "-";
045        private static final String DEVICE_ID_MAC_SEPARATOR = "FF";
046        
047        /**
048         * Pattern for the 64-bit address string: {@value}.
049         */
050        private static final String XBEE_64_BIT_ADDRESS_PATTERN = "(0[xX])?[0-9a-fA-F]{1,16}";
051        
052        private static final int HASH_SEED = 23;
053        
054        // Variables
055        private final byte[] address;
056        
057        /**
058         * Class constructor. Instantiates a new object of type 
059         * {@code XBee64BitAddress} with the given parameters.
060         * 
061         * @param address The XBee 64-bit address as byte array.
062         * 
063         * @throws IllegalArgumentException if {@code address.length > 8} or
064         *                                  if {@code address.length < 1}.
065         * @throws NullPointerException if {@code address == null}.
066         */
067        public XBee64BitAddress(byte[] address) {
068                if (address == null)
069                        throw new NullPointerException("Address cannot be null.");
070                if (address.length < 1)
071                        throw new IllegalArgumentException("Address must contain at least 1 byte.");
072                if (address.length > 8)
073                        throw new IllegalArgumentException("Address cannot contain more than 8 bytes.");
074                
075                this.address = new byte[8];
076                int diff = this.address.length - address.length;
077                for (int i = 0; i < diff; i++)
078                        this.address[i] = 0;
079                for (int i = diff; i < this.address.length; i++)
080                        this.address[i] = address[i - diff];
081        }
082        
083        /**
084         * Class constructor. Instantiates a new object of type 
085         * {@code XBee64BitAddress} with the given parameters.
086         * 
087         * <p>The string must be the hexadecimal representation of a 64-bit 
088         * address.</p>
089         * 
090         * @param address The XBee 64-bit address as string.
091         * 
092         * @throws IllegalArgumentException if {@code address.length() < 1} or
093         *                                  if {@code address} contains 
094         *                                  non-hexadecimal characters and is longer
095         *                                  than 8 bytes.
096         * @throws NullPointerException if {@code address == null}.
097         */
098        public XBee64BitAddress(String address) {
099                if (address == null)
100                        throw new NullPointerException("Address cannot be null.");
101                if (address.length() < 1)
102                        throw new IllegalArgumentException("Address must contain at least 1 character.");
103                if (!address.matches(XBEE_64_BIT_ADDRESS_PATTERN))
104                        throw new IllegalArgumentException("Address must follow this pattern: (0x)0013A20040XXXXXX.");
105                
106                byte[] byteAddress = HexUtils.hexStringToByteArray(address);
107                this.address = new byte[8];
108                int diff = this.address.length - byteAddress.length;
109                for (int i = 0; i < diff; i++)
110                        this.address[i] = 0;
111                for (int i = diff; i < this.address.length; i++)
112                        this.address[i] = byteAddress[i - diff];
113        }
114        
115        /**
116         * Class constructor. Instantiates a new object of type 
117         * {@code XBee64BitAddress} with the given bytes being {@code b0} the 
118         * more significant byte and {@code b7} the less significant byte.
119         * 
120         * @param b0 XBee 64-bit address bit 0.
121         * @param b1 XBee 64-bit address bit 1.
122         * @param b2 XBee 64-bit address bit 2.
123         * @param b3 XBee 64-bit address bit 3.
124         * @param b4 XBee 64-bit address bit 4.
125         * @param b5 XBee 64-bit address bit 5.
126         * @param b6 XBee 64-bit address bit 6.
127         * @param b7 XBee 64-bit address bit 7.
128         * 
129         * @throws IllegalArgumentException if {@code b0 > 255} or
130         *                                  if {@code b0 < 0} or
131         *                                  if {@code b1 > 255} or
132         *                                  if {@code b1 < 0} or
133         *                                  if {@code b2 > 255} or
134         *                                  if {@code b2 < 0} or
135         *                                  if {@code b3 > 255} or
136         *                                  if {@code b3 < 0} or
137         *                                  if {@code b4 > 255} or
138         *                                  if {@code b4 < 0} or
139         *                                  if {@code b5 > 255} or
140         *                                  if {@code b5 < 0} or
141         *                                  if {@code b6 > 255} or
142         *                                  if {@code b6 < 0} or
143         *                                  if {@code b7 > 255} or
144         *                                  if {@code b7 < 0}.
145         */
146        public XBee64BitAddress(int b0, int b1, int b2, int b3, int b4, int b5, int b6, int b7) {
147                if (b0 > 255 || b0 < 0)
148                        throw new IllegalArgumentException("B0 must be between 0 and 255.");
149                if (b1 > 255 || b1 < 0)
150                        throw new IllegalArgumentException("B1 must be between 0 and 255.");
151                if (b2 > 255 || b2 < 0)
152                        throw new IllegalArgumentException("B2 must be between 0 and 255.");
153                if (b3 > 255 || b3 < 0)
154                        throw new IllegalArgumentException("B3 must be between 0 and 255.");
155                if (b5 > 255 || b5 < 0)
156                        throw new IllegalArgumentException("B4 must be between 0 and 255.");
157                if (b4 > 255 || b4 < 0)
158                        throw new IllegalArgumentException("B5 must be between 0 and 255.");
159                if (b6 > 255 || b6 < 0)
160                        throw new IllegalArgumentException("B6 must be between 0 and 255.");
161                if (b7 > 255 || b7 < 0)
162                        throw new IllegalArgumentException("B7 must be between 0 and 255.");
163                
164                address = new byte[8];
165                address[0] = (byte) b0;
166                address[1] = (byte) b1;
167                address[2] = (byte) b2;
168                address[3] = (byte) b3;
169                address[4] = (byte) b4;
170                address[5] = (byte) b5;
171                address[6] = (byte) b6;
172                address[7] = (byte) b7;
173        }
174        
175        /**
176         * Returns the XBee 64-bit address value as byte array.
177         * 
178         * @return XBee 64-bit address value as byte array.
179         */
180        public byte[] getValue() {
181                return Arrays.copyOf(address, address.length);
182        }
183        
184        /**
185         * Generates the Device ID corresponding to this {@code XBee64BitAddress} 
186         * to be used in Device Cloud.
187         * 
188         * @return Device ID corresponding to this address.
189         */
190        public String generateDeviceID() {
191                StringBuilder sb = new StringBuilder();
192                for (int i = 0; i < 2; i++) {
193                        for (int j = 0; j < 4; j++)
194                                sb.append(HexUtils.byteArrayToHexString(new byte[]{0}));
195                        sb.append(DEVICE_ID_SEPARATOR);
196                }
197                // Here we should have "00000000-00000000-"
198                // Append first three bytes of the MAC Address, discard first 2.
199                sb.append(HexUtils.byteArrayToHexString(new byte[]{address[2], address[3], address[4]}));
200                sb.append(DEVICE_ID_MAC_SEPARATOR);
201                sb.append(DEVICE_ID_SEPARATOR);
202                sb.append(DEVICE_ID_MAC_SEPARATOR);
203                // Here we should have "00000000-00000000-XXXXXXFF-FF"
204                // Append second three bytes of the MAC Address.
205                sb.append(HexUtils.byteArrayToHexString(new byte[]{address[5], address[6], address[7]}));
206                return sb.toString();
207        }
208        
209        /*
210         * (non-Javadoc)
211         * @see java.lang.Object#equals(java.lang.Object)
212         */
213        @Override
214        public boolean equals(Object obj) {
215                if (!(obj instanceof XBee64BitAddress))
216                        return false;
217                XBee64BitAddress addr = (XBee64BitAddress)obj;
218                return Arrays.equals(addr.getValue(), getValue());
219        }
220        
221        /*
222         * (non-Javadoc)
223         * @see java.lang.Object#hashCode()
224         */
225        @Override
226        public int hashCode() {
227                int hash = HASH_SEED;
228                for (byte b:getValue())
229                        hash = hash * (hash + b);
230                return hash;
231        }
232        
233        /*
234         * (non-Javadoc)
235         * @see java.lang.Object#toString()
236         */
237        @Override
238        public String toString() {
239                return HexUtils.byteArrayToHexString(address);
240        }
241}