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