    001/**
    002 * Copyright (c) 2014 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: 000000000000FFFE).
    037         */
    038        public static final XBee64BitAddress UNKNOWN_ADDRESS = new XBee64BitAddress("FFFE");
    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}