inet.ipaddr.IPAddress Maven / Gradle / Ivy
Show all versions of ipaddress Show documentation
package inet.ipaddr;
import java.io.Serializable;
import java.math.BigInteger;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.UnknownHostException;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.function.IntFunction;
import java.util.function.Supplier;
import inet.ipaddr.IPAddressComparator.CountComparator;
import inet.ipaddr.IPAddressConverter.DefaultAddressConverter;
import inet.ipaddr.IPAddressSection.IPStringBuilderOptions;
import inet.ipaddr.IPAddressSection.StringOptions;
import inet.ipaddr.IPAddressTypeNetwork.IPAddressCreator;
import inet.ipaddr.IPAddressTypeNetwork.IPAddressSegmentCreator;
import inet.ipaddr.format.IPAddressPart;
import inet.ipaddr.format.util.IPAddressPartStringCollection;
import inet.ipaddr.format.util.sql.IPAddressSQLTranslator;
import inet.ipaddr.format.validate.AddressProvider;
import inet.ipaddr.format.validate.ParsedHost;
import inet.ipaddr.ipv4.IPv4Address;
import inet.ipaddr.ipv4.IPv4AddressNetwork.IPv4AddressCreator;
import inet.ipaddr.ipv4.IPv4AddressSegment;
import inet.ipaddr.ipv6.IPv6Address;
import inet.ipaddr.ipv6.IPv6AddressNetwork.IPv6AddressCreator;
import inet.ipaddr.ipv6.IPv6AddressSegment;
/**
* A single IP address, or a subnet of multiple addresses. Subnets have one or more segments that are a range of values.
*
* IPAddress objects are immutable and cannot change values. This also makes them thread-safe.
*
*
* String creation:
*
* There are several public classes used to customize IP address strings.
* For single strings from an address or address section, you use {@link StringOptions} or {@link inet.ipaddr.ipv6.IPv6AddressSection.IPv6StringOptions} along with {@link #toNormalizedString(IPAddressSection.StringOptions)}.
* Or you use one of the methods like {@link #toCanonicalString()} which does the same.
*
* For string collections from an address or address section, use {@link inet.ipaddr.ipv4.IPv4AddressSection.IPv4StringBuilderOptions}, {@link inet.ipaddr.ipv6.IPv6AddressSection.IPv6StringBuilderOptions}, {@link IPStringBuilderOptions} along with {@link #toStringCollection(IPAddressSection.IPStringBuilderOptions)} or {@link #toStrings(IPAddressSection.IPStringBuilderOptions)}.
* Or you use one of the methods {@link #toStandardStringCollection()}, {@link #toAllStringCollection()}, {@link #toStandardStrings()}, {@link #toAllStrings()} which does the same.
*
* @custom.core
* @author sfoley
*
*/
/*
* Internal details of how this works:
*
* 1. Building single strings steps:
* StringOptions, IPv6StringOptions provides options for a user to specify a single string to be produced for a given address or section of an address.
* When calling toNormalizedString, each is mapped to a single IPv4StringParams/IPv6StringParams/StringParams object used to construct the string.
* Each IPv4StringParams/IPv6StringParams/StringParams constructs a single string with its toString method
*
*
* 2. Building string collection steps:
* IPv4StringBuilderOptions, IPv6StringBuilderOptions, IPStringBuilderOptions provides options to create a set of multiple strings from a single IP Address or section of an address.
* toStringCollection constructs a IPv6StringBuilder/IPv4StringBuilder/IPAddressStringBuilder for that address section
* The builder translates the options to a series of IPv4StringParams/IPv6StringParams/StringParams in addAllVariations
* When the set is being created, it will use each IPv4StringParams/IPv6StringParams/StringParams object to construct each unique string, using their toString method
*
*
* Non-public classes:
* IPv6StringParams, IPv4StringParams and the base classes StringParams and IPAddressPartStringParams:
* Used by both single string creation or creating collections of strings, these are not public.
* IPv6StringBuilder/IPv4StringBuilder/IPAddressStringBuilder, used to create collections of strings, are not public either
*
*/
public abstract class IPAddress implements Comparable, Serializable {
private static final long serialVersionUID = 1L;
/**
* @custom.core
* @author sfoley
*
*/
public enum IPVersion {
IPV4,
IPV6;
public boolean isIPv4() {
return this == IPV4;
}
public boolean isIPv6() {
return this == IPV6;
}
};
public static final char RANGE_SEPARATOR = '-';
public static final String RANGE_SEPARATOR_STR = String.valueOf(RANGE_SEPARATOR);
public static final char SEGMENT_WILDCARD = '*';
public static final String SEGMENT_WILDCARD_STR = String.valueOf(SEGMENT_WILDCARD);
public static final char SEGMENT_SQL_WILDCARD = '%';
public static final String SEGMENT_SQL_WILDCARD_STR = String.valueOf(SEGMENT_SQL_WILDCARD);
public static final char SEGMENT_SQL_SINGLE_WILDCARD = '_';
public static final String SEGMENT_SQL_SINGLE_WILDCARD_STR = String.valueOf(SEGMENT_SQL_SINGLE_WILDCARD);
public static final char PREFIX_LEN_SEPARATOR = '/';
//The default way by which addresses are converted
public static final IPAddressConverter addressConverter = new DefaultAddressConverter();
//The default way by which addresses are compared
public static final IPAddressComparator addressComparator = new CountComparator();
/* the segments. For IPv4, each element is actually just 1 byte and the array has 4 elements, while for IPv6, each element is 2 bytes and the array has 8 elements. */
final IPAddressSection addressSection;
/* an IPAddressString representing the address, which is the one used to construct the address if the address was constructed from a IPAddressString */
protected IPAddressString fromString;
/* a Host representing the address, which is the one used to construct the address if the address was resolved from a Host.
* Note this is different than if the Host was an address itself, in which case the Host holds a reference to the address
* but there is no backwards reference to the Host.
*/
HostName fromHost;
/* a Host representing the canonical host for this address */
private HostName canonicalHost;
/* the associated InetAddress */
protected transient InetAddress inetAddress;
/**
* Represents an IP address or a set of addresses.
* @param section the address segments
*/
protected IPAddress(IPAddressSection section) {
this.addressSection = section;
}
/**
* If this address was resolved from a host, returns that host. Otherwise, does a reverse name lookup.
*/
public HostName toHostName() {
HostName host = fromHost;
if(host == null) {
fromHost = host = toCanonicalHostName();
}
return host;
}
/**
* Does a reverse name lookup to get the canonical host name.
*/
public HostName toCanonicalHostName() {
HostName host = canonicalHost;
if(host == null) {
if(isMultiple()) {
throw new IPAddressTypeException(this, "ipaddress.error.unavailable.numeric");
}
InetAddress inetAddress = toInetAddress();
//String hostStr1 = inetAddress.getHostName();
String hostStr = inetAddress.getCanonicalHostName();//note: this does not return ipv6 addresses enclosed in brackets []
if(hostStr.equals(inetAddress.getHostAddress())) {
//we got back the address, so the host is me
host = new HostName(hostStr, new ParsedHost(hostStr, AddressProvider.getProviderFor(this)));
host.resolvedAddress = this;
} else {
//the reverse lookup succeeded in finding a host string
//we might not be the default resolved address for the host, so we don't set that field
host = new HostName(hostStr);
}
}
return host;
}
public static IPAddress from(byte bytes[]) {
return from(bytes, null, null, null);
}
public static IPAddress from(byte bytes[], Integer prefixLength) {
return from(bytes, null, prefixLength, null);
}
public static IPAddress from(byte bytes[], byte bytes2[], Integer prefixLength) {
return from(bytes, bytes2, prefixLength, null);
}
protected static IPAddress from(byte bytes[], byte bytes2[], Integer prefixLength, String zone) {
if(bytes.length == IPv4Address.BYTE_COUNT) {
if(zone != null) {
throw new IllegalArgumentException();
}
IPAddressCreator addressCreator = IPv4Address.network().getAddressCreator();
IPv4AddressSegment segments[] = toSegments(bytes, bytes2, IPv4Address.SEGMENT_COUNT, IPv4Address.BYTES_PER_SEGMENT, IPv4Address.BITS_PER_SEGMENT, addressCreator, prefixLength);
return addressCreator.createAddressInternal(segments); /* address creation */
}
if(bytes.length == IPv6Address.BYTE_COUNT) {
IPAddressCreator addressCreator = IPv6Address.network().getAddressCreator();
IPv6AddressSegment segments[] = toSegments(bytes, bytes2, IPv6Address.SEGMENT_COUNT, IPv6Address.BYTES_PER_SEGMENT, IPv6Address.BITS_PER_SEGMENT, addressCreator, prefixLength);
return addressCreator.createAddressInternal(segments, zone); /* address creation */
}
throw new IllegalArgumentException();
}
protected static T[] toSegments(
byte bytes[],
byte bytes2[],
int segmentCount,
int bytesPerSegment,
int bitsPerSegment,
IPAddressSegmentCreator creator,
Integer networkPrefixLength) {
T segments[] = creator.createSegmentArray(segmentCount);
for(int i = 0, segmentIndex = 0; i < bytes.length; i += bytesPerSegment, segmentIndex++) {
Integer segmentPrefixLength = IPAddressSection.getSegmentPrefixLength(bitsPerSegment, networkPrefixLength, segmentIndex);
if(segmentPrefixLength != null && segmentPrefixLength == 0) {
segments[segmentIndex] = creator.createSegment(0, 0);
continue;
}
int value = 0, value2 = 0;
int k = bytesPerSegment + i;
for(int j = i; j < k; j++) {
int byteValue = bytes[j];
value <<= 8;
value |= 0xff & byteValue;
if(bytes2 != null) {
byteValue = bytes2[j];
value2 <<= 8;
value2 |= 0xff & byteValue;
}
}
segments[segmentIndex] = (bytes2 != null) ? creator.createSegment(value, value2, segmentPrefixLength) : creator.createSegment(value, segmentPrefixLength);
}
return segments;
}
protected static String toNormalizedString(byte bytes[], byte bytes2[], Integer prefixLength, String zone) {
if(bytes.length == IPv4Address.BYTE_COUNT) {
return toNormalizedString(bytes, bytes2, prefixLength,
IPv4Address.SEGMENT_COUNT, IPv4Address.BYTES_PER_SEGMENT, IPv4Address.BITS_PER_SEGMENT, IPv4Address.MAX_VALUE_PER_SEGMENT, IPv4Address.SEGMENT_SEPARATOR, IPv4Address.DEFAULT_TEXTUAL_RADIX,
null, IPv4Address.network());
}
if(bytes.length == IPv6Address.BYTE_COUNT) {
return toNormalizedString(bytes, bytes2, prefixLength,
IPv6Address.SEGMENT_COUNT, IPv6Address.BYTES_PER_SEGMENT, IPv6Address.BITS_PER_SEGMENT, IPv6Address.MAX_VALUE_PER_SEGMENT, IPv6Address.SEGMENT_SEPARATOR, IPv6Address.DEFAULT_TEXTUAL_RADIX,
zone, IPv6Address.network());
}
throw new IllegalArgumentException();
}
private static String toNormalizedString(
byte bytes[],
byte bytes2[],
Integer prefixLength,
int segmentCount,
int bytesPerSegment,
int bitsPerSegment,
int segmentMaxValue,
char separator,
int radix,
String zone,
IPAddressNetwork network) {
StringBuilder builder = new StringBuilder(IPv4Address.MAX_STRING_LEN + 8);
for(int i = 0; i < bytes.length; i += bytesPerSegment) {
Integer segmentPrefixLength = IPAddressSection.getSegmentPrefixLength(bitsPerSegment, prefixLength, i);
if(segmentPrefixLength != null && segmentPrefixLength == 0) {
builder.append('0').append(separator);
continue;
}
int value = 0, value2 = 0;
int k = bytesPerSegment + i;
for(int j = i; j < k; j++) {
int byteValue = bytes[j];
value <<= 8;
value |= 0xff & byteValue;
if(bytes2 != null) {
byteValue = bytes2[j];
value2 <<= 8;
value2 |= 0xff & byteValue;
}
}
if(bytes2 == null) {
if(segmentPrefixLength != null) {
value &= network.getSegmentNetworkMask(segmentPrefixLength);
}
IPAddressSegment.toUnsignedStringFast(value, radix, builder);
} else {
if(segmentPrefixLength != null) {
int mask = network.getSegmentNetworkMask(segmentPrefixLength);
value &= mask;
value2 &= mask;
}
if(value == value2) {
IPAddressSegment.toUnsignedStringFast(value, radix, builder);
} else {
if(value > value2) {
int tmp = value2;
value2 = value;
value = tmp;
}
if(value == 0 && value2 == segmentMaxValue) {
builder.append(IPAddress.SEGMENT_WILDCARD_STR);
} else {
IPAddressSegment.getRangeString(value, value2, radix, builder);
}
}
}
builder.append(separator);
}
builder.setLength(builder.length() - 1);
if(zone != null && zone.length() > 0) {
builder.append(IPv6Address.ZONE_SEPARATOR).append(zone);
}
if(prefixLength != null) {
builder.append(IPAddress.PREFIX_LEN_SEPARATOR).append(prefixLength);
}
return builder.toString();
}
public abstract IPAddressNetwork getNetwork();
public static IPAddressNetwork network(IPVersion version) {
return version.isIPv4() ? IPv4Address.network() : IPv6Address.network();
}
public static IPAddress getLoopback(IPVersion version) {
return network(version).getLoopback();
}
public static IPAddress getLocalHost() throws UnknownHostException {
return from(InetAddress.getLocalHost().getAddress());
}
public static String[] getStandardLoopbackStrings(IPVersion version) {
return network(version).getStandardLoopbackStrings();
}
/**
* Returns the address as an address section comprising all segments in the address.
* @return
*/
public IPAddressSection getSection() {
return addressSection;
}
/**
* Returns all the ways of breaking this address down into segments, as selected.
* @return
*/
public IPAddressPart[] getParts(IPStringBuilderOptions options) {
return new IPAddressPart[] { getSection() };
}
public int getMaxSegmentValue() {
return IPAddressSegment.getMaxSegmentValue(getIPVersion());
}
public static int maxSegmentValue(IPVersion version) {
return IPAddressSegment.getMaxSegmentValue(version);
}
public int getBytesPerSegment() {
return IPAddressSegment.getByteCount(getIPVersion());
}
public int getBitsPerSegment() {
return IPAddressSegment.getBitCount(getIPVersion());
}
public static int bitsPerSegment(IPVersion version) {
return IPAddressSegment.getBitCount(version);
}
public abstract int getByteCount();
public static int byteCount(IPVersion version) {
return version.isIPv4() ? IPv4Address.BYTE_COUNT : IPv6Address.BYTE_COUNT;
}
public abstract int getSegmentCount();
public int getSegmentIndex(Integer networkPrefixLength) {
return addressSection.getSegmentIndex(networkPrefixLength);
}
static int getSegmentIndex(Integer networkPrefixLength, int byteLength, int bytesPerSegment) {
return IPAddressSection.getSegmentIndex(networkPrefixLength, byteLength, bytesPerSegment);
}
public int getByteIndex(Integer networkPrefixLength) {
return addressSection.getByteIndex(networkPrefixLength);
}
static int getByteIndex(Integer networkPrefixLength, int byteLength) {
return IPAddressSection.getByteIndex(networkPrefixLength, byteLength);
}
public static int segmentCount(IPVersion version) {
return version.isIPv4() ? IPv4Address.SEGMENT_COUNT : IPv6Address.SEGMENT_COUNT;
}
public abstract int getBitCount();
public static int bitCount(IPVersion version) {
return version.isIPv4() ? IPv4Address.BIT_COUNT : IPv6Address.BIT_COUNT;
}
public boolean isMultipleByNetworkPrefix() {
return addressSection.isMultipleByNetworkPrefix();
}
/**
* @return whether this address represents more than one address.
* Such addresses include CIDR/IP addresses (eg 1.2.3.4/11) or wildcard addresses (eg 1.2.*.4) or range addresses (eg 1.2.3-4.5)
*/
public boolean isMultiple() {
return addressSection.isMultiple();
}
/**
* @return whether this address represents a network prefix or the set of all addresses with the same network prefix
*/
public boolean isPrefixed() {
return addressSection.isPrefixed();
}
public Integer getNetworkPrefixLength() {
return addressSection.getNetworkPrefixLength();
}
public IPAddressSegment getSegment(int index) {
return addressSection.getSegment(index);
}
/**
* Gets the count of addresses that this address may represent.
*
* If this address is not a CIDR network prefix and it has no range, then there is only one such address.
*
* @return
*/
public BigInteger getCount() {
if(!isMultiple()) {
return BigInteger.ONE;
}
return addressSection.getCount();
}
/**
* If this represents an address with ranging values, returns an address representing the lower values of the range.
* If this represents an address with a single value in each segment, returns this.
*
* @return
*/
public abstract IPAddress getLower();
/**
* If this represents an address with ranging values, returns an address representing the upper values of the range
* If this represents an address with a single value in each segment, returns this.
*
* @return
*/
public abstract IPAddress getUpper();
protected static S[] createSingle(R original, IPAddressSegmentCreator segmentCreator, IntFunction segProducer) {
return IPAddressSection.createSingle(original, segmentCreator, segProducer);
}
protected static T getSingle(
T original,
Supplier singleFromMultipleCreator) {
if(!original.isPrefixed() && !original.isMultiple()) {
return original;
}
return singleFromMultipleCreator.get();
}
public abstract Iterator extends IPAddress> iterator();
/**
* @return an object to iterate over the individual addresses represented by this object.
*/
public abstract Iterable extends IPAddress> getAddresses();
protected static Iterator iterator(
T original,
IPAddressCreator creator,
Supplier segs,
IntFunction> segIteratorProducer) {
return new Iterator() {
private boolean doThis = !original.isMultiple() && !original.isPrefixed(); //note that a non-multiple address can have a prefix (either /32 or /128)
private Iterator iterator = original.addressSection.iterator(creator, doThis, segs, segIteratorProducer);
@Override
public boolean hasNext() {
return iterator.hasNext() || doThis;
}
@Override
public T next() {
if(!hasNext()) {
throw new NoSuchElementException();
}
if(doThis) {
doThis = false;
return original;
}
S[] next = iterator.next();
return creator.createAddressInternal(next); /* address creation */
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
public boolean isIPv4() {
return addressSection.isIPv4();
}
public boolean isIPv6() {
return addressSection.isIPv6();
}
public IPVersion getIPVersion() {
return addressSection.getIPVersion();
}
/**
* If this address is IPv4, or can be converted to IPv4, returns that {@link IPv4Address}. Otherwise, returns null.
*
* @see #isIPv4Convertible()
* @return the address
*/
public IPv4Address toIPv4() {
return null;
}
/**
*
* @return If this address is IPv6, or can be converted to IPv6, returns that {@link IPv6Address}. Otherwise, returns null.
*/
public IPv6Address toIPv6() {
return null;
}
/**
* Determines whether this address can be converted to IPv4, if not IPv4 already.
* Override this method to convert in your own way, or call setAddressConverter with your own converter object.
*
* You should also override {@link #toIPv4()} to match the conversion.
*
* This method returns true for all IPv4 addresses.
*
* @return
*/
public abstract boolean isIPv4Convertible();
/**
* Determines whether an address can be converted to IPv6, if not IPv6 already.
* Override this method to convert in your own way, or call setAddressConverter with your own converter object.
*
* You should also override {@link #toIPv6()} to match the conversion.
*
* This method returns true for all IPv6 addresses.
*
* @return
*/
public abstract boolean isIPv6Convertible();
/**
* @see java.net.InetAddress#isLinkLocalAddress()
*/
public abstract boolean isLinkLocal();
/**
* @see java.net.InetAddress#isSiteLocalAddress()
*/
public abstract boolean isSiteLocal();
/**
* @see java.net.InetAddress#isMulticastAddress()
*/
public abstract boolean isMulticast();
/**
* @see java.net.InetAddress#isAnyLocalAddress()
*/
public boolean isAnyLocal() {
return isZero();
}
/**
* @see java.net.InetAddress#isLoopbackAddress()
*/
public abstract boolean isLoopback();
/**
* @throws IPAddressTypeException if this address does not map to a single address.
* If you want to get subnet bytes or mask bytes, call getLowestBytes
*/
public byte[] getBytes() {
if(isMultiple()) {
throw new IPAddressTypeException(this, "ipaddress.error.unavailable.numeric");
}
return getLowestBytes();
}
/**
* Gets the bytes for the lowest address in the range represented by this address.
*
* @return
*/
public byte[] getLowestBytes() {
return addressSection.getLowestBytes();
}
/**
* @throws IPAddressTypeException if this address does not map to a single address, ie it is a subnet
*/
public InetAddress toInetAddress() {
if(inetAddress == null) {
synchronized(this) {
if(inetAddress == null) {
byte bytes[] = getBytes();
try {
inetAddress = InetAddress.getByAddress(bytes);
} catch(UnknownHostException e) { /* will never reach here */ }
}
}
}
return inetAddress;
}
public boolean isZero() {
if(isMultipleByNetworkPrefix()) {
return false;
}
return addressSection.isZero();
}
@Override
public int hashCode() {
return addressSection.hashCode();
}
@Override
public int compareTo(IPAddress other) {
if(this == other) {
return 0;
}
return addressComparator.compare(this, other);
}
public boolean matches(IPAddressString otherString) {
//before converting otherString to an address object, check if the strings match
if(isFromSameString(otherString)) {
return true;
}
IPAddress otherAddr = otherString.getAddress();
return otherAddr != null && isSameAddress(otherAddr);
}
protected boolean isFromSameString(IPAddressString otherString) {
return fromString != null && otherString != null &&
(fromString == otherString || fromString.fullAddr.equals(otherString.fullAddr)) &&
fromString.validationOptions == otherString.validationOptions;//we could do equals here but 99% of the time this gives the right answer in less time because the validation options are not expected to change
}
public boolean isSameAddress(IPAddress other) {
return other == this || getSection().equals(other.getSection());
}
/**
* Two IPAddress objects are equal if they represent the same set of addresses.
* Whether one or the other has an associated network prefix length is not considered.
*
* Also, an IPAddressString and IPAddress are considered equal if they represent the same set of addresses.
*/
@Override
public boolean equals(Object o) {
if(o == this) {
return true;
}
if(o instanceof IPAddress) {
IPAddress other = (IPAddress) o;
if(isFromSameString(other.fromString)) {
return true;
}
return isSameAddress(other);
}
return false;
}
/**
*
* @param other
* @return whether this subnet contains the given address
*/
public boolean contains(IPAddress other) {
if(other == this) {
return true;
}
return addressSection.contains(other.addressSection);
}
/**
* Subtract the give subnet from this subnet, returning an array of sections for the result (the subnets will not be contiguous so an array is required).
*
* Computes the subnet difference, the set of addresses in this address section but not in the provided section.
*
* @param other
* @throws IPAddressTypeException if the two sections are not comparable
* @return the difference
*/
public abstract IPAddress[] subtract(IPAddress other);
public static IPAddress from(InetAddress inetAddress) {
byte bytes[] = inetAddress.getAddress();
if(bytes.length == IPv6Address.BYTE_COUNT) {
Inet6Address inet6Address = (Inet6Address) inetAddress;
NetworkInterface networkInterface = inet6Address.getScopedInterface();
String zone = null;
if(networkInterface == null) {
int scopeId = inet6Address.getScopeId();
if(scopeId != 0) {
zone = Integer.toString(scopeId);
}
} else {
zone = networkInterface.getName();
}
IPv6AddressCreator ipv6Creator = IPv6Address.network().getAddressCreator();
return ipv6Creator.createAddressInternal(bytes, null, zone); /* address creation */
} else {
IPv4AddressCreator creator = IPv4Address.network().getAddressCreator();
return creator.createAddressInternal(bytes, null, null); /* address creation */
}
}
//////////////// string creation below ///////////////////////////////////////////////////////////////////////////////////////////
public String[] getSegmentStrings() {
return addressSection.getSegmentStrings();
}
@Override
public String toString() {
return toNormalizedString();
}
/**
* This produces a canonical string.
*
* RFC 5952 describes canonical representations.
* http://en.wikipedia.org/wiki/IPv6_address#Recommended_representation_as_text
* http://tools.ietf.org/html/rfc5952
*
* Each address has a unique canonical string, not counting the prefix, which can give two equal addresses different strings.
*/
public String toCanonicalString() {
return addressSection.toCanonicalString();
}
/**
* This produces a string with no compressed segments and all segments of full length,
* which is 4 characters for IPv6 segments and 3 characters for IPv4 segments.
*
* Each address has a unique full string, not counting CIDR the prefix, which can give two equal addresses different strings.
*/
public String toFullString() {
return addressSection.toFullString();
}
/**
* The normalized string returned by this method is consistent with java.net.Inet4Address and java.net.Inet6Address.
* IPs are not compressed nor mixed in this representation.
*
* The string returned by this method is unique for each address, not counting CIDR the prefix, which can give two equal addresses different strings.
*/
public String toNormalizedString() {
return addressSection.toNormalizedString();
}
protected void cacheNormalizedString(String str) {
addressSection.cacheNormalizedString(str);
}
/**
* This produces the shortest valid string for the address.
*
* Each address has a unique compressed string, not counting the prefix, which can give two equal addresses different strings.
*
* For subnets the string will not have wildcards in host segments (there will be zeros instead), only in network segments.
*/
public String toCompressedString() {
return addressSection.toCompressedString();
}
/**
* Produces a consistent subnet string that looks like 1.2.*.* or 1:2::/16
*
* In the case of IPv4, this means that wildcards are used instead of a network prefix when a network prefix has been supplied.
* In the case of IPv6, when a network prefix has been supplied, the prefix will be shown and the host section will be compressed with ::.
*/
public String toSubnetString() {
return addressSection.toSubnetString();
}
/**
* This produces a string similar to the normalized string but avoids the CIDR prefix.
* CIDR addresses will be shown with wildcards and ranges instead of using the CIDR prefix notation.
*/
public String toNormalizedWildcardString() {
return addressSection.toNormalizedWildcardString();
}
/**
* This produces a string similar to the canonical string but avoids the CIDR prefix.
* Addresses with a network prefix length will be shown with wildcards and ranges instead of using the CIDR prefix length notation.
* IPv6 addresses will be compressed according to the canonical representation.
*/
public String toCanonicalWildcardString() {
return addressSection.toCanonicalWildcardString();
}
/**
* This is similar to toNormalizedWildcardString, avoiding the CIDR prefix, but with compression as well.
*/
public String toCompressedWildcardString() {
return addressSection.toCompressedWildcardString();
}
/**
* This is the same as the string from toNormalizedWildcardString except that
* it uses IPAddress.SEGMENT_SQL_WILDCARD instead of IPAddress.SEGMENT_WILDCARD and also uses IPAddress.SEGMENT_SQL_SINGLE_WILDCARD
*/
public String toSQLWildcardString() {
return addressSection.toSQLWildcardString();
}
/**
* Returns a string with a CIDR network prefix length if this address has a network prefix length.
* For IPv6, the host section will be compressed with ::, for IPv4 the host section will be zeros.
* @return
*/
public String toNetworkPrefixLengthString() {
return addressSection.toNetworkPrefixLengthString();
}
/**
* Returns a mixed string if it represents a convertible IPv4 address, returns the normalized string otherwise.
* @return
*/
public String toConvertedString() {
return toNormalizedString();
}
/**
* Generates the Microsoft UNC path component for this address
*
* @return
*/
public abstract String toUNCHostName();
/**
* Generates the reverse DNS lookup string
* For 8.255.4.4 it is 4.4.255.8.in-addr.arpa
* For 2001:db8::567:89ab it is b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa
*
*
* @throw IPAddressTypeException if this address is a subnet of multiple addresses
* @return
*/
public abstract String toReverseDNSLookupString();
/**
* Writes this address as hexadecimal, with or without a preceding 0x prefix
*/
public String toHexString(boolean withPrefix) {
return addressSection.toHexString(withPrefix);
}
/**
* Constructs a string representing this address according to the given parameters
*
* @throw IPAddressTypeException if this address is a subnet of multiple addresses, you have selected splitDigits, and the address range cannot be represented in split digits
*
* @param params the parameters for the address string
*/
public String toNormalizedString(StringOptions params) {
return addressSection.toNormalizedString(params);
}
/**
* Returns at most a few dozen string representations:
*
* -mixed (1:2:3:4:5:6:1.2.3.4)
* -full compressions (a:0:b:c:d:0:e:f or a::b:c:d:0:e:f or a:0:b:c:d::e:f)
* -full leading zeros (000a:0000:000b:000c:000d:0000:000e:000f)
* -all uppercase and all lowercase (a::a can be A::A)
* -combinations thereof
*
* @return
*/
public String[] toStandardStrings() {
return toStandardStringCollection().toStrings();
}
/**
* Produces almost all possible string variations
*
* Use this method with care... a single IPv6 address can have thousands of string representations.
*
* Examples:
*
* - "::" has 1297 such variations, but only 9 are considered standard
* - "a:b:c:0:d:e:f:1" has 1920 variations, but only 12 are standard
*
*
* Variations included in this method:
*
* - all standard variations from {@link #toStandardStrings()}
* - adding a variable number of leading zeros (::a can be ::0a, ::00a, ::000a)
* - choosing any number of zero-segments to compress (:: can be 0:0:0::0:0)
* - mixed representation of all variations (1:2:3:4:5:6:1.2.3.4)
* - all uppercase and all lowercase (a::a can be A::A)
* - all combinations of such variations
*
* Variations omitted from this method: mixed case of a-f, which you can easily handle yourself with String.equalsIgnoreCase
*
* @return the strings
*/
public String[] toAllStrings() {
return toAllStringCollection().toStrings();
}
/**
* Rather than using toAllStrings or StandardStrings,
* you can use this method to customize the list of strings produced for this address
*/
public String[] toStrings(IPStringBuilderOptions options) {
return toStringCollection(options).toStrings();
}
public IPAddressPartStringCollection toStandardStringCollection() {
return addressSection.toStandardStringCollection();
}
public IPAddressPartStringCollection toAllStringCollection() {
return addressSection.toAllStringCollection();
}
public IPAddressPartStringCollection toStringCollection(IPStringBuilderOptions options) {
return addressSection.toStringCollection(options);
}
/**
* Generates an IPAddressString object for this IPAddress object.
*
* This same IPAddress object can be retrieved from the resulting IPAddressString object using {@link IPAddressString#getAddress()}
*
* In general, users are intended to create IPAddress objects from IPAddressString objects,
* while the reverse direction is generally not all that useful.
*
* However, the reverse direction can be useful under certain circumstances.
*
* Not all IPAddressString objects can be converted to IPAddress objects,
* as is the case with IPAddressString objects corresponding to the types IPType.INVALID and IPType.EMPTY.
*
* Not all IPAddressString objects can be converted to IPAddress objects without specifying the IP version,
* as is the case with IPAddressString objects corresponding to the types IPType.PREFIX and IPType.ALL.
*
* So in the event you wish to store a collection of IPAddress objects with a collection of IPAddressString objects,
* and not all the IPAddressString objects can be converted to IPAddress objects, then you may wish to use a collection
* of only IPAddressString objects, in which case this method is useful.
*
* @return an IPAddressString object for this IPAddress.
*/
public IPAddressString toAddressString() {
if(fromString == null) {
fromString = new IPAddressString(this); /* address string creation */
}
return fromString;
}
public static String toDelimitedSQLStrs(String strs[]) {
if(strs.length == 0) {
return "";
}
StringBuilder builder = new StringBuilder();
for(String str : strs) {
builder.append('\'').append(str).append('\'').append(',');
}
return builder.substring(0, builder.length() - 1);
}
///////////////////// masks and subnets below ///////////////////////
/**
* @return whether this address represents more than one address and the set of addresses is determined entirely by the prefix length.
*/
public boolean isRangeEquivalentToPrefix() {
return addressSection.isRangeEquivalentToPrefix();
}
/**
* Returns the smallest CIDR prefix possible (largest network),
* such that this address paired with that prefix represents the exact same range of addresses.
*
* @see inet.ipaddr.format.IPAddressDivision#getMaskPrefixLength(boolean)
*
* @return
*/
public int getMinPrefix() {
return addressSection.getMinPrefix();
}
/**
* Returns a prefix length for which the range of this address can be specified only using the address lower value and the prefix length
*
* If no such prefix exists, returns null.
*
* Examples:
* 1.2.3.4 returns 32
* 1.2.*.* returns 16
* 1.2.*.0/24 returns 16
* 1.2.*.4 returns null
* 1.2.252-255.* returns 22
* 1.2.3.4/x returns x
*
* @return
*/
public Integer getEquivalentPrefix() {
return addressSection.getEquivalentPrefix();
}
/**
* Returns the equivalent CIDR address for which the range of addresses represented
* is specified using just a single value and a prefix length in the returned section.
*
* Otherwise, returns null.
*
* If this address represents just a single address, this object is returned.
*
* Examples:
* 1.2.3.4 returns 1.2.3.4/32
* 1.2.*.* returns 1.2.0.0/16
* 1.2.*.0/24 returns 1.2.0.0/16
* 1.2.*.4 returns null
* 1.2.252-255.* returns 1.2.252.0/22
* 1.2.3.4/x returns the same address
*
* @return
*/
public IPAddress toPrefixedEquivalent() {
if(!isMultiple()) {
return this;
}
Integer newPrefix = getEquivalentPrefix();
return newPrefix == null ? null : toSubnet(newPrefix);
}
/**
* Constructs an equivalent address with the smallest CIDR prefix possible (largest network),
* such that the address represents the exact same range of addresses.
*
* @return
*/
public IPAddress toPrefixedMin() {
return toSubnet(getMinPrefix());
}
/**
* If this address is equivalent to the mask for a CIDR prefix, it returns that prefix length.
* Otherwise, it returns null.
* A CIDR network mask is all 1s in the network section and then all 0s in the host section.
* A CIDR host mask is all 0s in the network section and then all 1s in the host section.
* The prefix is the length of the network section.
*
* Also, keep in mind that the prefix length returned by this method is not equivalent to the prefix length used to construct this object.
* The prefix length used to construct indicates the network and host portion of this address.
* The prefix length returned here indicates the whether the value of this address can be used as a mask for the network and host
* portion of any other address. Therefore the two values can be different values, or one can be null while the other is not.
*
* @param network whether to check if we are a network mask or a host mask
* @return the prefix length corresponding to this mask, or null if this address is not a CIDR prefix mask
*/
public Integer getMaskPrefixLength(boolean network) {
return addressSection.getMaskPrefixLength(network);
}
/**
* Check that the range in each segment resulting from the mask is contiguous, otherwise we cannot represent it.
*
* For instance, for the range 0 to 3 (bits are 00 to 11), if we mask all 4 numbers from 0 to 3 with 2 (ie bits are 10),
* then we are left with 1 and 3. 2 is not included. So we cannot represent 1 and 3 as a contiguous range.
*
* The underlying rule is that mask bits that are 0 must be above the resulting range in each segment.
*
* Any bit in the mask that is 0 must not fall below any bit in the masked segment range that is different between low and high
*
* Any network mask must eliminate each entire segment range. Any host mask is fine.
*
* @param mask
* @param networkPrefixLength
* @return
*/
public boolean isMaskCompatibleWithRange(IPAddress mask, Integer networkPrefixLength) {
return getSection().isMaskCompatibleWithRange(mask.getSection(), networkPrefixLength);
}
/**
* Creates a subnet address using the given mask.
* Any existing prefix is removed as the mask is applied to all individual addresses.
*
* If this represents multiple addresses, and applying the mask to all addresses creates a set of addresses
* that cannot be represented as a contiguous range, then IPAddressTypeException is thrown.
*
* See {@link #isMaskCompatibleWithRange(IPAddress, Integer)}
*/
public abstract IPAddress toSubnet(IPAddress mask) throws IPAddressTypeException;
/**
* Creates a subnet address using the given mask.
* Any existing prefix is removed as the mask is applied to all individual addresses.
* If networkPrefixLength is non-null, applies that prefix after the mask has been applied.
*
* If this represents multiple addresses, and applying the mask to all addresses creates a set of addresses
* that cannot be represented as a contiguous range, then IPAddressTypeException is thrown.
*
* See {@link #isMaskCompatibleWithRange(IPAddress, Integer)}
*/
public abstract IPAddress toSubnet(IPAddress mask, Integer networkPrefixLength) throws IPAddressTypeException;
/**
* Creates a subnet address using the given CIDR prefix bits.
*
* Since no mask is applied to all of the addresses represented (as with the other toSubnet methods),
* any existing prefix or range remains the same before applying the additional prefix.
*/
public abstract IPAddress toSubnet(int networkPrefixLength);
/**
* Generates the network section of the address. The returned section will have only as many segments as needed
* to hold the network as indicated by networkPrefixLength. If withPrefixLength is true, it will have networkPrefixLength as its associated prefix length,
* unless this address already has a smaller prefix length, in which case the existing prefix length is retained.
*
* @param networkPrefixLength
* @param withPrefixLength whether the resulting section will have networkPrefixLength as the associated prefix length or not
* @return
*/
public abstract IPAddressSection getNetworkSection(int networkPrefixLength, boolean withPrefixLength);
/**
* Generates the network section of the address. The returned section will have only as many segments as needed
* to hold the network as indicated by networkPrefixLength. It will have networkPrefixLength as its associated prefix length,
* unless this address already has a smaller prefix length, in which case the existing prefix length is retained.
*
* @param networkPrefixLength
* @return
*/
public IPAddressSection getNetworkSection(int networkPrefixLength) {
return addressSection.getNetworkSection(networkPrefixLength);
}
/**
* Generates the network section of the address if the address is a CIDR prefix, otherwise it generates the entire address as a prefixed address with prefix matching the address bit length.
* @return
*/
public abstract IPAddressSection getNetworkSection();
/**
* Generates the host section of the address. The returned section will have only as many segments as needed
* to hold the host as indicated by cidrBits.
*
* @param networkPrefixLength
* @return
*/
public abstract IPAddressSection getHostSection(int networkPrefixLength);
/**
* Generates the host section of the address. The returned section will have only as many segments as needed
* as determined by the existing CIDR prefix length. If there is no CIDR prefix length, the host section will have 0 segments.
*
* @return
*/
public abstract IPAddressSection getHostSection();
/**
* Return an address for the network encompassing this address.
* The bits indicate the number of additional network bits in the network address in comparison to this address.
*
* @param prefixLengthDecrement the number to reduce the network bits in order to create a larger network.
* If null, then this method has the same behaviour as toSupernet()
* @return
*/
public IPAddress toSupernet(Integer prefixLengthDecrement) {
int newPrefix = addressSection.getSupernetPrefix(prefixLengthDecrement);
return toSubnet(newPrefix);
}
/**
* Return an address for the network encompassing this address,
* with the network portion of the returned address extending to the furthest segment boundary
* located entirely within but not matching the network portion of this address,
* unless the network portion has no bits in which case the same address is returned.
*
* @return the encompassing network
*/
public IPAddress toSupernet() {
return toSupernet(null);
}
/**
* returns a clause for matching this address.
*
* If this address is a subnet, this method will attempt to match every address in the subnet.
* Therefore it is much more efficient to use getNetworkSection().getStartsWithSQLClause() for a CIDR subnet.
*
* @param builder
* @param sqlExpression
*/
public void getMatchesSQLClause(StringBuilder builder, String sqlExpression) {
addressSection.getStartsWithSQLClause(builder, sqlExpression);
}
/**
* returns a clause for matching this address.
*
* Similar to getMatchesSQLClause(StringBuilder builder, String sqlExpression) but allows you to tailor the SQL produced.
*
* @param builder
* @param sqlExpression
* @param translator
*/
public void getMatchesSQLClause(StringBuilder builder, String sqlExpression, IPAddressSQLTranslator translator) {
addressSection.getStartsWithSQLClause(builder, sqlExpression, translator);
}
}