Source code: com/voytechs/jnetstream/primitive/address/Address.java
1 /*
2 * File: Address.java
3 * Auth: Mark Bednarczyk
4 * Date: 2001-08-05
5 * Id: $Id: Address.java,v 1.1.1.1 2003/09/22 16:32:11 voytechs Exp $
6 ********************************************
7 Copyright (C) 2003 Mark Bednarczyk
8
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 ********************************************
23 * $Log: Address.java,v $
24 * Revision 1.1.1.1 2003/09/22 16:32:11 voytechs
25 * Initial import.
26 *
27 * Revision 1.1 2001/09/04 15:33:55 markbe
28 * Initial revision
29 *
30 */
31 package com.voytechs.jnetstream.primitive.address;
32
33 import com.voytechs.jnetstream.util.NumberUtils;
34
35 import java.lang.IllegalArgumentException;
36 import java.net.InetAddress;
37 import java.util.*;
38 import java.io.Serializable;
39
40 /**
41 * A Class for storing IP Addresses
42 * Currently is only designed to utilize IPv4 (32bit) addresses.
43 * There are various functions for converting the IP address to a LONG.
44 * Why would you want to use a long for storage of an IP address, in my
45 * case I store IP address in a database as an UNSIGNED INT, java does not
46 * have unsigned numbers so you have to go to next bigger primitive type to store
47 * it or do like other implementations do store the address in a byte array.
48 */
49 public class Address {
50
51 public static final int DEFAULT_NUMBER_BASE = 10; // Base is the default.
52 private int defaultNumberBase = DEFAULT_NUMBER_BASE;
53
54 /* Internal attributes */
55 public static final boolean debug = false;
56
57 /**
58 * Array which stores the IP address bytes.
59 * This is a variable array so that both v4 and v6 addresses.
60 */
61 protected byte [] address = null;
62
63 protected char separator = '.';
64 protected char[] format = null;
65
66 /**
67 * Special internal use only constructor that does not initialize the
68 * Address right away but later.
69 * @param stringAddress takes the IP address as a string.
70 */
71 protected Address() {
72 }
73
74 /**
75 * Main constructor taking the array of bytes as the address
76 */
77 public Address(byte [] address) {
78
79 this.address = address;
80
81 }
82
83 /**
84 * Main constructor taking the array of bytes as the address
85 */
86 public Address(Address ad) {
87
88 this.address = ad.address;
89
90 }
91
92 public void setAddress(byte[] address) { this.address = address; }
93 public void setSeparator(char separator) { this.separator = separator; format = null;}
94 public void setFormat(String format) { this.format = format.toCharArray(); }
95 public void setFormat(char[] format) { this.format = format; }
96
97 public void setRadix(int radix) {
98 defaultNumberBase = radix;
99 }
100
101 public int getRadix() {
102 return(defaultNumberBase);
103 }
104
105 /**
106 * This method INVERTs (Java statment <CODE>~</CODE>) Address object to another Address object.
107 * Every byte is ANDed with every other byte
108 * in the corresponding object. If number of bytes in the first number
109 * does not match the number in the second object then an IllegalArgumentException
110 * is thrown.
111 */
112 public byte [] INVERT() {
113 return(INVERT(address));
114 }
115
116 /**
117 * This method ORs Address object to another Address object.
118 * Every byte is ANDed with every other byte
119 * in the corresponding object. If number of bytes in the first number
120 * does not match the number in the second object then an IllegalArgumentException
121 * is thrown.
122 * @return A new byte array is returned with mask applied to the ip number object.
123 */
124 public static byte [] INVERT(byte[] a) {
125
126 byte [] v = new byte [a.length];
127
128 for(int i = 0; i < v.length; i ++) {
129 int A = (a[i] < 0 ? a[i] + 256 : a[i]);
130 v[i] = (byte)(~A); // A ~ (tilde) INVERTs the bits inside the integer. A 0 becomes a 1 and 1 a 0.
131 }
132
133 return(v);
134 }
135
136 /**
137 * This method EORs (Exclusive OR) Address object to another Address object.
138 * Every byte is ANDed with every other byte
139 * in the corresponding object. If number of bytes in the first number
140 * does not match the number in the second object then an IllegalArgumentException
141 * is thrown.
142 * @return A new byte array is returned with mask applied to the ip number object.
143 */
144 public byte [] EOR(Address b) {
145 return(EOR(address, b.toByteArray()));
146 }
147
148 /**
149 * This method ORs Address object to another Address object.
150 * Every byte is ANDed with every other byte
151 * in the corresponding object. If number of bytes in the first number
152 * does not match the number in the second object then an IllegalArgumentException
153 * is thrown.
154 * @return A new byte array is returned with mask applied to the ip number object.
155 */
156 public static byte [] EOR(byte[] a, byte[] b) {
157 if(a.length != b.length)
158 throw new IllegalArgumentException("Can AND 2 Address objects. " +
159 "Their byte counts do not match. " +
160 "Excecting Equal number of bytes in each Address object.");
161
162 byte [] v = new byte [a.length];
163
164 for(int i = 0; i < v.length; i ++) {
165 int A = (a[i] < 0 ? a[i] + 256 : a[i]);
166 int B = (b[i] < 0 ? b[i] + 256 : b[i]);
167 v[i] = (byte)(B ^ A);
168 }
169
170 return(v);
171 }
172
173 /**
174 * This method ORs Address object to another Address object.
175 * Every byte is ANDed with every other byte
176 * in the corresponding object. If number of bytes in the first number
177 * does not match the number in the second object then an IllegalArgumentException
178 * is thrown.
179 * @return A new byte array is returned with mask applied to the ip number object.
180 */
181 public byte [] OR(Address b) {
182 return(OR(address, b.toByteArray()));
183 }
184
185 /**
186 * This method ORs Address object to another Address object.
187 * Every byte is ANDed with every other byte
188 * in the corresponding object. If number of bytes in the first number
189 * does not match the number in the second object then an IllegalArgumentException
190 * is thrown.
191 * @return A new byte array is returned with mask applied to the ip number object.
192 */
193 public static byte [] OR(byte[] a, byte[] b) {
194 if(a.length != b.length)
195 throw new IllegalArgumentException("Can AND 2 Address objects. " +
196 "Their byte counts do not match. " +
197 "Excecting Equal number of bytes in each Address object.");
198
199 byte [] v = new byte [a.length];
200
201 for(int i = 0; i < v.length; i ++) {
202 int A = (a[i] < 0 ? a[i] + 256 : a[i]);
203 int B = (b[i] < 0 ? b[i] + 256 : b[i]);
204 v[i] = (byte)(B | A);
205 }
206
207 return(v);
208 }
209
210
211 /**
212 * This method ANDs Address object to another Address object.
213 * Every byte is ANDed with every other byte
214 * in the corresponding object. If number of bytes in the first number
215 * does not match the number in the second object then an IllegalArgumentException
216 * is thrown.
217 * @return A new byte array is returned with mask applied to the ip number object.
218 */
219 public byte [] AND(Address b) {
220 return(AND(address, b.toByteArray()));
221 }
222
223 /**
224 * This method ANDs Address object to another Address object.
225 * Every byte is ANDed with every other byte
226 * in the corresponding object. If number of bytes in the first number
227 * does not match the number in the second object then an IllegalArgumentException
228 * is thrown.
229 * @return A new byte array is returned with mask applied to the ip number object.
230 */
231 public static byte [] AND(byte[] a, byte[] b) {
232 if(a.length != b.length)
233 throw new IllegalArgumentException("Can AND 2 Address objects. " +
234 "Their byte counts do not match. " +
235 "Excecting Equal number of bytes in each Address object.");
236
237 byte [] v = new byte [a.length];
238
239 for(int i = 0; i < v.length; i ++) {
240 int A = (a[i] < 0 ? a[i] + 256 : a[i]);
241 int B = (b[i] < 0 ? b[i] + 256 : b[i]);
242 v[i] = (byte)(B & A);
243 }
244
245 return(v);
246 }
247
248 protected byte getByte(int index) {
249 byte [] ad = address;
250 return( (byte)(ad[index] < 0 ? ad[index] + 256 : ad[index]) ); // Take care of the pescy sign for bytes.
251 }
252
253 public static byte getByte(byte b) {
254 return( (byte)(b < 0 ? b + 256 : b) ); // Take care of the pescy sign for bytes.
255 }
256
257
258
259 /**
260 * Compare our byte values for IP address to the object's.
261 * If length do not match then the objects are considered unequal.
262 * If byte counts are the same then a byte for byte comparison is made and
263 * appropriate result return.
264 * @param o Address object to campare to.
265 * @return true means both objects are equal. This means both Addresss have the same number
266 * of bytes and each of those bytes is equal (==).
267 * @exception If either object is not of IP v4 or V6. If their byte lengths do not follow the standard.
268 */
269 public boolean equals(Object o) {
270
271 if( o instanceof Address) {
272
273 /**
274 * Let the compare function figure it out.
275 */
276 if(compareTo(o) == 0)
277 return(true);
278 else
279 return(false);
280 }
281
282 return(false);
283 }
284
285 public int compareTo(Object o) {
286
287 if(o instanceof Address) {
288 return(compare(this, (Address)o));
289 }
290
291 throw new ClassCastException();
292 }
293
294 /**
295 * Compare our byte values for IP address to the object's.
296 * If length do not match then the one with smaller number of bytes
297 * is considered less then the one with more bytes (V6 is always > V4).
298 * If byte counts are the same then a byte for byte comparison is made and
299 * appropriate result return.
300 * @param a Address object to campare to.
301 * @param b Address object to campare with.
302 * @return -1 means we are less then a, 0 means we are equal, 1 means we are greater then a.
303 * @exception If either object is not of IP v4 or V6. If their byte lengths do not follow the standard.
304 */
305 public static int compare(Address a, Address b) throws IllegalArgumentException {
306
307 if(debug)
308 System.out.println("Address::compare(" + a + ", " + b);
309
310 /**
311 * First we compare the length of the address. If we
312 * are comparing V6 with V4 type addresses, always return
313 * as V6 is greater then V4, even if the actual data of the shorter
314 * V4 address would indicate otherwise. This will have the affect
315 * of sorters putting V4 address together while V6 separately.
316 *
317 * If length of addresses are the same then do a byte for byte
318 * comparison.
319 */
320 if(a.address.length < b.address.length)
321 return(-1);
322
323 if(a.address.length > b.address.length)
324 return(1);
325
326 /**
327 * Lengths are the same.
328 * If length of addresses are the same then do a byte for byte
329 * comparison. At any point if there is a difference in values
330 * immediately stop and return which one makes that bigger.
331 *
332 * Otherwise if all bytes compared equal then return 0.
333 */
334 for(int i = 0; i < a.address.length; i ++) {
335 if(a.getByte(i) < b.getByte(i))
336 return(-1);
337
338 if(a.getByte(i) > b.getByte(i))
339 return(1);
340 }
341
342 return(0);
343
344 }
345
346 public static int compare(Address a, byte[] b) throws IllegalArgumentException {
347
348 if(debug)
349 System.out.println("Address::compare(" + a + ", " + b);
350
351 /**
352 * First we compare the length of the address. If we
353 * are comparing V6 with V4 type addresses, always return
354 * as V6 is greater then V4, even if the actual data of the shorter
355 * V4 address would indicate otherwise. This will have the affect
356 * of sorters putting V4 address together while V6 separately.
357 *
358 * If length of addresses are the same then do a byte for byte
359 * comparison.
360 */
361 if(a.address.length < b.length)
362 return(-1);
363
364 if(a.address.length > b.length)
365 return(1);
366
367 /**
368 * Lengths are the same.
369 * If length of addresses are the same then do a byte for byte
370 * comparison. At any point if there is a difference in values
371 * immediately stop and return which one makes that bigger.
372 *
373 * Otherwise if all bytes compared equal then return 0.
374 */
375 for(int i = 0; i < a.address.length; i ++) {
376 if(a.getByte(i) < b[i])
377 return(-1);
378
379 if(a.getByte(i) > b[i])
380 return(1);
381 }
382
383 return(0);
384
385 }
386
387
388
389 /**
390 * Convert a long (32 bits) to byte array (4 bytes).
391 * @param address IP address. Only first 32 bits are important
392 */
393 public static byte[] longToByteArray(long longAddress) {
394
395 byte [] ba = new byte [4];
396
397 ba[0] = (byte)((longAddress & 0xFF000000L) >> 24);
398 ba[1] = (byte)((longAddress & 0x00FF0000L) >> 16);
399 ba[2] = (byte)((longAddress & 0x0000FF00L) >> 8);
400 ba[3] = (byte)((longAddress & 0x000000FFL));
401
402 return(ba);
403 }
404
405 /**
406 * Returns a byte[] representing this Address.
407 *
408 * @return address of this number.
409 */
410 public byte[] toByteArray() {
411 return(address);
412 }
413
414 /**
415 * Gets the byte array representation of this address.
416 * @return byte array which is the address. Length determines the type of address.
417 */
418 public byte [] byteArrayValue() {
419 return(address);
420 }
421
422 /**
423 * Get the Address number value in a dot notation.
424 */
425 public String stringValue() {
426 return(toString() );
427 }
428
429 /**
430 * Set the internal address array.
431 */
432 protected void setByteArrayAddress(byte [] address) {
433 this.address = address;
434 }
435
436 /**
437 * Return current IP address as byte array, for V4 that will be 4 bytes
438 * for V6 16.
439 * @param address String representation of IP address. Both V4 and V6 type addresses are accepted.
440 * @return IP Byte array representation of the address. Check Array.length to get the size of the array.
441 */
442 public static byte[] parseByteArray(String address, int size, char separator) throws IllegalArgumentException {
443
444 StringTokenizer st;
445 byte [] v;
446
447 st = new StringTokenizer(address, "" + separator);
448 v = new byte [size];
449
450 for(int i = 0; i < v.length; i ++) {
451 String t = st.nextToken();
452
453 if(t == null && i != v.length) {
454 throw new IllegalArgumentException("Illegal IP address format. String has too few byte elements.");
455 }
456
457 v[i] = (byte)Integer.parseInt(t);
458 }
459
460 return(v);
461 }
462
463 /**
464 * Convert to dot notation string representation of the address
465 * @return IP address in the dot notation.
466 */
467 public String toString() {
468
469 if(format != null)
470 return( toString(address, defaultNumberBase, format) );
471 else
472 return( toString(address, defaultNumberBase, separator) );
473
474 }
475
476 /**
477 * Convert internal address to a string. Use separator
478 * to convert to string.
479 */
480 public static String toString(byte [] address, int radix, char separator) {
481 String s = "";
482
483 for(int i = 0; i < address.length; i ++) {
484 if(i != 0)
485 s += separator;
486
487 s += "" + Integer.toString((((int)address[i]) & 0xFF), radix); // Convert signed byte to unsigned integer
488 }
489
490 return(s);
491 }
492
493 /**
494 * Convert internal address to a string. Use separator
495 * to convert to string.
496 */
497 public static String toString(byte [] address, int radix, char[] format) {
498 String s = "";
499
500 int a = 0;
501 for(int i = 0; i < format.length; i ++) {
502
503 if(format[i] == 'x') {
504 if( a == address.length)
505 throw new IllegalArgumentException("Format length does not match length of byte array.");
506
507 if(radix == 16)
508 s += NumberUtils.toHexString(address[a++]);
509 else
510 s += Integer.toString((((int)address[a++]) & 0xFF), radix);
511 }
512 else if(format[i] == 'X') {
513 if( (a +1) == address.length)
514 throw new IllegalArgumentException("Format length does not match length of byte array.");
515
516 if(radix == 16)
517 s += NumberUtils.toHexString(address[a++]).toUpperCase();
518 else
519 s += Integer.toString((((int)address[a++]) & 0xFF), radix).toUpperCase();
520 }
521 else {
522 s += format[i];
523 }
524 }
525
526 if(a != address.length)
527 throw new IllegalArgumentException("Format length does not match length of address .");
528
529 return(s);
530 }
531
532 /**
533 * Test function for Address
534 * @param args command line arguments
535 */
536 public static void main(String [] args) {
537
538 Address ip = new Address(new byte[] { (byte)192, (byte)168, 1, 2} );
539 Address ip2 = new Address(new byte[] { (byte)192, (byte)168, 1, 3} );
540 Address ether = new Address(new byte[] { (byte)192, (byte)168, 1, 3, 6, 8} );
541 ether.setFormat("x:x:x:x:x:x");
542 ether.setRadix(16);
543
544 System.out.println("Converted IP=" + ip.toString());
545 System.out.println("Converted ether=" + ether.toString());
546
547 System.out.println(ip.toString() + "=" + ip2.toString() + " : " + ip.equals(ip2) + " " + ip2.equals(ip));
548 System.out.println(ip.toString() + "<=>" + ip2.toString() + " : " + ip.compareTo(ip2) + " " + ip2.compareTo(ip));
549 }
550
551 } /* END OF: Address */