All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.nerd4j.net.IPv4Address Maven / Gradle / Ivy

There is a newer version: 1.1.3
Show newest version
/*
 * #%L
 * Nerd4j Core
 * %%
 * Copyright (C) 2011 - 2013 Nerd4j
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as 
 * published by the Free Software Foundation, either version 3 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public 
 * License along with this program.  If not, see
 * .
 * #L%
 */
package org.nerd4j.net;

import java.io.Serializable;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * This class handles the representation of objects of type IP and can be
 * used in all those cases where is the need to handle IPs without the need
 * of all the features provided by the class {@link InetAddress}.
 * 

* It's possible ho access all the formats through the methods: * {@link #intValue()}, {@link #stringValue()} and {@link #rawValue()}. * The IP raw format is the same returned by the method {@link InetAddress#getAddress()} * in order to simplify the passage from one class to the other. *

*

* This class provides immutable instances that can be created using the * available factory methods: {@link #valueOf(int)}, {@link #valueOf(String)}, * {@link #valueOf(byte[])}. *

*

* The implementation of the {@link Comparable} interface can be misleading * because internally the IP value is represented using an {@code int}. * The IP {@code 255.255.255.255} is expected to be greater than {@code 0.0.0.0} * but the first IP is the {@code int} value {@code -1} while the second * is the {@code int} value {@code 0}. Another example is given by the IP * {@code 127.255.255.255} and the IP {@code 128.0.0.0} the first is the * {@code int} value {@code 2147483647} while the second is the {@code int} * value {@code -2147483648}.
* In other words the method {@link #compareTo(IPv4Address)} compares the * IP address as they were two unsigned {@code int} values. *

* * @author Nerd4j Team */ public final class IPv4Address implements Serializable, Comparable { /** Serial Version UID. */ private static final long serialVersionUID = -9003117636518262197L; /* *************** */ /* STATIC DATA */ /* *************** */ /** Number of bytes of the IP address. */ private static final int BYTE_NUMBER = 4; /** RegExp to recognize the '.' */ private static final String DOT = "\\."; /** RegExp with groupo definition to recongize a byte as a decimal value. */ private static final String DEC_BYTE = "(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})"; /** RegExp with groupo definition to recongize a byte as a hex-decimal value. */ private static final String HEX_BYTE = "([A-Fa-f0-9]{1,2})"; /** Complete RegExp to recongize an IP in decimal notation. */ private static final Pattern DEC_IP_PATTERN = Pattern.compile( DEC_BYTE + DOT + DEC_BYTE + DOT + DEC_BYTE + DOT + DEC_BYTE ); /** Complete RegExp to recongize an IP in hex-decimal notation. */ private static final Pattern HEX_IP_PATTERN = Pattern.compile( HEX_BYTE + DOT + HEX_BYTE + DOT + HEX_BYTE + DOT + HEX_BYTE ); /** Bit mask to isolate the less significant byte from an integer. */ private static final int LOW_BYTE_MASK = 0xFF; /* *************** */ /* INSTANCE DATA */ /* *************** */ /** The {@code int} representation if the IP. */ private int intIp; /** String representation of the IP. Is transient and populated when needed. */ private transient String strIp; /** Byte array representation of the IP. Is transient and populated when needed. */ private transient byte[] rawIp; /** * Private constructor. *

* It's not possible to access the constructor, use the factory * methods instead. *

* * @param ip the IP represented as an integer. */ private IPv4Address( int ip ) { super(); this.intIp = ip; } /* ***************** */ /* FACTORY METHODS */ /* ***************** */ /** * Returns an {@link IPv4Address} using the given integer representation. * * @param ip the IP represented as an integer. * @return IP address. */ public static IPv4Address valueOf( int ip ) { return new IPv4Address(ip); } /** * Returns an {@link IPv4Address} using the given {@link String} representation. *

* The textual representation can be in decimal (ie. {@code 255.255.255.255}) * or hex-decimal ({@code FF.FF.FF.FF}) notation. *

* * @param ip the IP represented as a {@link String}. * @return IP address. * * @throws IllegalArgumentException if the {@link String} does not represent a valid IP . */ public static IPv4Address valueOf( String ip ) throws IllegalArgumentException { int intIp; IPv4Address ipAddress; try { intIp = computeIntValue(ip, DEC_IP_PATTERN, 10); ipAddress = new IPv4Address(intIp); /* Stores the textual representation for further use. */ ipAddress.strIp = ip; } catch ( IllegalArgumentException e ) { intIp = computeIntValue(ip, HEX_IP_PATTERN, 16); ipAddress = new IPv4Address(intIp); /* * In this case the textual representation will not be * stored because this class only produces decimal representations. */ } return ipAddress; } /** * Returns an {@link IPv4Address} using the given {@code raw} representation. *

* The raw format is the same provided by {@link InetAddress#getAddress()}. *

* * @param ip the IP represented as a byre array. * @return IP address. * * @throws IllegalArgumentException if the byte array does not represent a valid IP . */ public static IPv4Address valueOf( byte[] ip ) throws IllegalArgumentException { final int intIp = computeIntValue(ip); final IPv4Address ipAddress = new IPv4Address(intIp); /* Stores the raw representation for further use. */ ipAddress.rawIp = new byte[BYTE_NUMBER]; System.arraycopy(ip, 0, ipAddress.rawIp, 0, ip.length); return ipAddress; } /** * Returns an {@link IPv4Address} using the given {@link InetAddress} representation. *

* This method supports conversions from {@link Inet6Address} if * {@code ((Inet6Address)inet).isIPv4CompatibleAddress() == true} holds. *

* * @param ip the IP represented as a {@link IPv4Address}. * @return IP address. * * @throws IllegalArgumentException if the {@link Inet6Address} is not convertible. */ public static IPv4Address valueOf( InetAddress inet ) throws IllegalArgumentException { if ( inet == null ) throw new IllegalArgumentException( "Null isn't a valid inet address." ); byte[] raw; if ( inet instanceof Inet6Address ) { final Inet6Address inet6 = (Inet6Address) inet; if(!inet6.isIPv4CompatibleAddress()) throw new IllegalArgumentException( "Not a compatible IPv4 IPv6 address: " + inet ); raw = new byte[4]; System.arraycopy(inet6.getAddress(), 12, raw, 0, 4); } else { raw = inet.getAddress(); } return valueOf( raw ); } /* ******************** */ /* CONVERSION METHODS */ /* ******************** */ /** * Converts the given textual representation into the internal {@code int} * representation using the given numerical base. * * @param ip textual representation of the IP. * @param pattern evaluation pattern. The pattern is expected to produce 4 blocks. * @param base the numerical base to use in conversion. * * @return internal {@code int} representation of the IP. * * @throws IllegalArgumentException if the provided text can't be parsed. */ private static int computeIntValue( String ip, Pattern pattern, int base ) throws IllegalArgumentException { final Matcher matcher = pattern.matcher(ip); if( matcher.matches() ) { int intIp = 0; String strBlock; int intBlock; /* If the pattern mathes the blocks are ensured to be four. */ for( int i = 1; i < BYTE_NUMBER + 1; ++i ) { strBlock = matcher.group(i); intBlock = Integer.valueOf( strBlock, base ); intIp = intIp << 8 | intBlock; } return intIp; } else { throw new IllegalArgumentException("Not a valid IP string: \"" + ip + "\"."); } } /** * Converts the given byte array representation into the internal {@code int} * representation using the given numerical base. * * @param ip raw representation of the IP. * @return internal {@code int} representation of the IP. * * @throws IllegalArgumentException if the provided byte array does not represent a valid IP. */ private static int computeIntValue( byte[] ip ) { if( ip == null || ip.length != BYTE_NUMBER ) throw new IllegalArgumentException("Not a valid raw IP."); int intIp = 0; for( byte byteBlock : ip ) { /* * The JVM converts the byte into an integer before putting it * in OR with the ip value. Therefore we need to use the LOW_BYTE_MASK * to avoid two's complement to interfere with the operation. * Indeed values of type 254 (byte value -2) are converted from * byte representation 0xFE to integer representation 0xFFFFFFFE. */ intIp = intIp << 8 | (byteBlock & LOW_BYTE_MASK) ; } return intIp; } /** * Converts the given internal representation into the decimal textual representation. * * @param ip internal representation of the IP. * @return decimal textual representation of the IP. */ private String computeStringValue( int ip ) { StringBuilder sb = new StringBuilder(16); for( int i = 0; i < BYTE_NUMBER; ++i ) { /* Inizia ad appendere dal blocco di byte più alto. */ int intBlock = ( ip >> 8 * (BYTE_NUMBER -1 -i) ) & LOW_BYTE_MASK; sb.append(intBlock).append('.'); } final int end = sb.length() - 1; return sb.substring(0, end); } /** * Converts the given internal representation into the raw format representation. * * @param ip internal representation of the IP. * @return raw format representation of the IP. */ private byte[] computeRawValue( int ip ) { byte[] rawIp = new byte[BYTE_NUMBER]; for( int i = 0; i < BYTE_NUMBER; ++i ) { /* Starts reading the most significant byte. */ rawIp[i] = (byte) ( ip >> 8 * (BYTE_NUMBER -1 -i) ); } return rawIp; } /* ************** */ /* DATA METHODS */ /* ************** */ /** * Returns the internal representation of the IP. * * @return internal representation of the IP. */ public int intValue() { return intIp; } /** * Returns the textual representation in decimal format (ie. {@code 255.255.255.255}). * * @return textual representation if the IP. */ public String stringValue() { if (strIp == null) strIp = computeStringValue( intIp ); return strIp; } /** * Returns the raw representation as provided by {@link InetAddress#getAddress()}. * * @return waw representation if the IP. */ public byte[] rawValue() { if (rawIp == null) rawIp = computeRawValue( intIp ); return rawIp; } /* ***************************** */ /* OBJECT & COMPARABLE METHODS */ /* ***************************** */ /** * Compares this instance whit the given one. *

* The ordering is the same obtained by ordering two unsigned integers. *

* * @param other the instance to compare. * * @return a negative value if {@code this < other}, zero if {@code this == other} * and a positive value if {@code this > other}. */ @Override public int compareTo( IPv4Address other ) { /* The comparison is made depending on the sign of the values. */ if ( (intIp ^ other.intIp) >>> 31 == 1 ) /* Different sighs. */ return other.intIp - intIp; else /* Same sign. */ return intIp - other.intIp; } /** * {@inheritDoc} */ @Override public int hashCode() { return intIp; } /** * {@inheritDoc} */ @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; IPv4Address other = (IPv4Address) obj; if (intIp != other.intIp) return false; return true; } /** * Returns the textual representation of this IP address. * * @see {@link #stringValue()}. */ @Override public String toString() { return stringValue(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy