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