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 16-bit network address.
020 * 
021 * <p>This address is only applicable for:</p>
022 * <ul>
023 *   <li>802.15.4</li>
024 *   <li>ZigBee</li>
025 *   <li>ZNet 2.5</li>
026 *   <li>XTend (Legacy)</li>
027 * </ul>
028 * 
029 * <p>DigiMesh and Point-to-Multipoint protocols don't support 16-bit 
030 * addressing.</p>
031 * 
032 * <p>Each device has its own 16-bit address which is unique in the network. 
033 * It is automatically assigned when the radio joins the network for ZigBee 
034 * and ZNet 2.5, and manually configured in 802.15.4 radios.</p>
035 * 
036 */
037public final class XBee16BitAddress {
038
039        // Constants
040        /**
041         * 16-bit address reserved for the coordinator (value: 0000).
042         */
043        public static final XBee16BitAddress COORDINATOR_ADDRESS = new XBee16BitAddress("0000");
044        /**
045         * 16-bit broadcast address (value: FFFF).
046         */
047        public static final XBee16BitAddress BROADCAST_ADDRESS = new XBee16BitAddress("FFFF");
048        /**
049         * 16-bit unknown address (value: FFFE).
050         */
051        public static final XBee16BitAddress UNKNOWN_ADDRESS = new XBee16BitAddress("FFFE");
052        
053        /**
054         * Pattern for the 16-bit address string: {@value}.
055         */
056        private static final String XBEE_16_BIT_ADDRESS_PATTERN = "(0[xX])?[0-9a-fA-F]{1,4}";
057        
058        private static final int HASH_SEED = 23;
059        
060        // Variables
061        private final byte[] address;
062        
063        /**
064         * Class constructor. Instantiates a new object of type 
065         * {@code XBee16BitAddress} with the given parameters.
066         * 
067         * @param hsb High significant byte of the address.
068         * @param lsb Low significant byte of the address.
069         * 
070         * @throws IllegalArgumentException if {@code hsb > 255} or
071         *                                  if {@code hsb < 0} or
072         *                                  if {@code lsb > 255} or
073         *                                  if {@code lsb < 0}.
074         */
075        public XBee16BitAddress(int hsb, int lsb) {
076                if (hsb > 255 || hsb < 0)
077                        throw new IllegalArgumentException("HSB must be between 0 and 255.");
078                if (lsb > 255 || lsb < 0)
079                        throw new IllegalArgumentException("LSB must be between 0 and 255.");
080                
081                address = new byte[2];
082                address[0] = (byte) hsb;
083                address[1] = (byte) lsb;
084        }
085        
086        /**
087         * Class constructor. Instantiates a new object of type 
088         * {@code XBee16BitAddress} with the given parameters.
089         * 
090         * @param address The 16-bit address as byte array.
091         * 
092         * @throws IllegalArgumentException if {@code address.length < 1} or
093         *                                  if {@code address.length > 2}.
094         * @throws NullPointerException if {@code address == null}.
095         */
096        public XBee16BitAddress(byte[] address) {
097                if (address == null)
098                        throw new NullPointerException("Address cannot be null.");
099                if (address.length < 1)
100                        throw new IllegalArgumentException("Address must contain at least 1 byte.");
101                if (address.length > 2)
102                        throw new IllegalArgumentException("Address cannot contain more than 2 bytes.");
103                
104                // Check array size.
105                this.address = new byte[2];
106                int diff = this.address.length - address.length;
107                for (int i = 0; i < diff; i++)
108                        this.address[i] = 0;
109                for (int i = diff; i < this.address.length; i++)
110                        this.address[i] = address[i - diff];
111        }
112        
113        /**
114         * Class constructor. Instantiates a new object of type 
115         * {@code XBee16BitAddress} with the given parameters.
116         * 
117         * <p>The string must be the hexadecimal representation of a 16-bit 
118         * address.</p> 
119         * 
120         * @param address String containing the 16-bit address.
121         * 
122         * @throws IllegalArgumentException if {@code address.length() < 1} or
123         *                                  if {@code address} contains 
124         *                                  non-hexadecimal characters and is longer
125         *                                  than 8 bytes.
126         * @throws NullPointerException if {@code address == null}.
127         */
128        public XBee16BitAddress(String address) {
129                if (address == null)
130                        throw new NullPointerException("Address cannot be null.");
131                if (address.length() < 1)
132                        throw new IllegalArgumentException("Address must contain at least 1 character.");
133                if (!address.matches(XBEE_16_BIT_ADDRESS_PATTERN))
134                        throw new IllegalArgumentException("Address must follow this pattern: (0x)XXXX.");
135                
136                // Convert the string into a byte array.
137                byte[] byteAddress = HexUtils.hexStringToByteArray(address);
138                // Check array size.
139                this.address = new byte[2];
140                int diff = this.address.length - byteAddress.length;
141                for (int i = 0; i < diff; i++)
142                        this.address[i] = 0;
143                for (int i = diff; i < this.address.length; i++)
144                        this.address[i] = byteAddress[i - diff];
145        }
146        
147        /**
148         * Returns the address high significant byte.
149         * 
150         * @return Address high significant byte.
151         */
152        public int getHsb() {
153                return address[0];
154        }
155        
156        /**
157         * Returns the address low significant byte.
158         * 
159         * @return Address low significant byte.
160         */
161        public int getLsb() {
162                return address[1];
163        }
164        
165        /**
166         * Returns the 16-bit address value in byte array format.
167         * 
168         * @return Address value as byte array.
169         */
170        public byte[] getValue() {
171                return Arrays.copyOf(address, address.length);
172        }
173        
174        /*
175         * (non-Javadoc)
176         * @see java.lang.Object#equals(java.lang.Object)
177         */
178        @Override
179        public boolean equals(Object obj) {
180                if (!(obj instanceof XBee16BitAddress))
181                        return false;
182                XBee16BitAddress addr = (XBee16BitAddress)obj;
183                return Arrays.equals(addr.getValue(), getValue());
184        }
185        
186        /*
187         * (non-Javadoc)
188         * @see java.lang.Object#hashCode()
189         */
190        @Override
191        public int hashCode() {
192                int hash = HASH_SEED;
193                for (byte b:getValue())
194                        hash = hash * (hash + b);
195                return hash;
196        }
197        
198        /*
199         * (non-Javadoc)
200         * @see java.lang.Object#toString()
201         */
202        @Override
203        public String toString() {
204                return HexUtils.byteArrayToHexString(address);
205        }
206}