inet.ipaddr.IPAddressString Maven / Gradle / Ivy
Show all versions of ipaddress Show documentation
/*
* Copyright 2016-2018 Sean C Foley
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* or at
* https://github.com/seancfoley/IPAddress/blob/master/LICENSE
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package inet.ipaddr;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import inet.ipaddr.IPAddress.IPVersion;
import inet.ipaddr.format.IPAddressDivisionSeries;
import inet.ipaddr.format.validate.HostIdentifierStringValidator;
import inet.ipaddr.format.validate.IPAddressProvider;
import inet.ipaddr.format.validate.Validator;
import inet.ipaddr.ipv4.IPv4Address;
import inet.ipaddr.ipv6.IPv6Address;
import inet.ipaddr.mac.MACAddress;
/**
* Parses the string representation of an IP address. Such a string can represent just a single address like 1.2.3.4 or 1:2:3:4:6:7:8, or a subnet like 1.2.0.0/16 or 1.*.1-3.1-4 or 1111:222::/64.
*
* This supports a much wider range of address string formats than InetAddress.getByName. It supports subnet formats, provides specific error messages, and allows more specific configuration.
*
* You can control all of the supported formats using {@link IPAddressStringParameters.Builder} to build a parameters instance of {@link IPAddressStringParameters}.
* When not using the constructor that takes a {@link IPAddressStringParameters}, a default instance of {@link IPAddressStringParameters} is used that is generally permissive.
*
*
Supported formats
* Both IPv4 and IPv6 are supported.
*
* Subnets are supported:
*
* - wildcards '*' and ranges '-' (for example 1.*.2-3.4), useful for working with subnets
* - the wildcard '*' can span multiple segments, so you can represent all addresses with '*', all IPv4 with '*.*', or all IPv6 with '*:*'
* - SQL wildcards '%' and '_', although '%' is considered an SQL wildcard only when it is not considered an IPv6 zone indicator
* - CIDR network prefix length addresses, like 1.2.0.0/16, which is equivalent to 1.2.*.* (all-zero hosts are the full subnet, non-zero hosts are single addresses)
* - address/mask pairs, in which the mask is applied to the address, like 1.2.3.4/255.255.0.0, which is also equivalent to 1.2.*.*
*
*
* You can combine these variations, such as 1.*.2-3.4/255.255.255.0
*
* IPv6 is fully supported:
*
* - IPv6 addresses like ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
* - IPv6 zones or scope identifiers, like ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%zone
* - IPv6 mixed addresses are supported, which are addresses for which the last two IPv6 segments are represented as IPv4, like ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255
* - IPv6 compressed addresses like ::1
* - A single value of 32 hex digits like 00aa00bb00cc00dd00ee00ff00aa00bb with or without a preceding hex delimiter 0x
* - A base 85 address comprising 20 base 85 digits like 4)+k&C#VzJ4br>0wv%Yp as in rfc 1924 https://tools.ietf.org/html/rfc1924
* - Binary, preceded by 0b, either with binary segments that comprise all 16 bits like ::0b0000111100001111 or a single segment address of 0b followed by 128 binary bits.
*
*
* All of the above subnet variations work for IPv6, whether network prefix lengths, masks, ranges or wildcards.
* Similarly, all the above subnet variations work for any supported IPv4 format, such as the standard dotted-decimal IPv4 format as well as the inet_aton formats listed below.
*
* This class support all address formats of the C routine inet_pton and the Java method java.net.InetAddress.getByName.
* This class supports all IPv4 address formats of the C routine inet_aton as follows:
*
* - IPv4 hex: 0x1.0x2.0x3.0x4 (0x prefix)
* - IPv4 octal: 01.02.03.0234. Note this clashes with the same address interpreted as dotted decimal
* - 3-part IPv4: 1.2.3 (which is interpreted as 1.2.0.3 (ie the third part covers the last two)
* - 2-part IPv4: 1.2 (which is interpreted as 1.0.0.2 (ie the 2nd part covers the last 3)
* - 1-part IPv4: 1 (which is interpreted as 0.0.0.1 (ie the number represents all 4 segments, and can be any number of digits less than the 32 digits which would be interpreted as IPv6)
* - hex or octal variants of 1, 2, and 3 part, such as 0xffffffff (which is interpreted as 255.255.255.255)
*
* Also supported are binary segments of a 0b followed by binary digits like 0b1.0b1010.2.3, or a single segment address of 0b followed by all 32 bits.
*
* inet_aton (and this class) allows mixing octal, hex and decimal (e.g. 0xa.11.013.11 which is equivalent to 11.11.11.11).
* String variations using prefixes, masks, ranges, and wildcards also work for inet_aton style.
* The same can be said of binary segments, they can be mixed with all other formats.
*
* Note that there is ambiguity when supporting both inet_aton octal and dotted-decimal leading zeros, like 010.010.010.010 which can
* be interpreted as octal or decimal, thus it can be either 8.8.8.8 or 10.10.10.10, with the default behaviour using the former interpretation
* This behaviour can be controlled by {@link IPAddressStringParameters.Builder#getIPv4AddressParametersBuilder()} and
* {@link inet.ipaddr.ipv4.IPv4AddressStringParameters.Builder#allowLeadingZeros(boolean)}
*
* Some additional formats:
*
* - null or empty strings are interpreted as the loopback, in the same way as InetAddress.getByName interprets null or empty strings
* - as noted previously, the single wildcard address "*" represents all addresses both ipv4 and ipv6,
* although you need to give it some help when converting to IPAddress by specifying the IP version in {@link #getAddress(IPVersion)} or {@link #toAddress(IPVersion)}
* - specifying CIDR prefix lengths with no corresponding addresses are interpreted as the corresponding network mask. For instance,
* /64 is interpreted as the 64 bit network mask (ie 64 ones followed by 64 zeros)
*
*
* If you have an address in which segments have been delimited with commas, such as "1,2.3.4,5.6", you can parse this with {@link #parseDelimitedSegments(String)}
* which gives an iterator of strings. For "1,2.3.4,5.6" you will iterate through "1.3.4.6", "1.3.5.6", "2.3.4.6" and "2.3.5.6".
* You can count the number of elements in such an iterator with {@link #countDelimitedAddresses(String)}.
* Each string can then be used to construct an IPAddressString.
*
*
Usage
* Once you have constructed an IPAddressString object, you can convert it to an IPAddress object with various methods.
* It is as simple as:
*
* {@link IPAddress} address = new {@link IPAddressString}("1.2.3.4").{@link #getAddress()};
*
*
* If your application takes user input IP addresses, you can validate with:
*
* try {
* {@link IPAddress} address = new IPAddressString("1.2.3.4").{@link #toAddress()};
* } catch({@link AddressStringException} e) {
* //e.getMessage() provides description of validation failure
* }
*
* Most address strings can be converted to an IPAddress object using {@link #getAddress()} or {@link #toAddress()}. In most cases the IP version is determined by the string itself.
*
* There are a few exceptions, cases in which the version is unknown or ambiguous, for which {@link #getAddress()} returns null:
*
* - strings which do not represent valid addresses (eg "bla")
* - ambiguous address strings (eg "/32" is a prefix that could be IPv4 or IPv6). For such strings you can provide the IPv4/IPv6 version to {@link #getAddress(IPVersion)} to get an address.
* - the "all" address "*" which represents all IPv4 and IPv6 addresses. For this string you can provide the IPv4/IPv6 version to {@link #getAddress(IPVersion)} to get an address representing either all IPv4 or all IPv6 addresses.
* - empty string "" is interpreted as the default loopback address. You can provide the ipv4/ipv6 version to{@link #getAddress(IPVersion)}to get the loopback version of your choice.
*
*
* The other exception is a subnet in which the range of values in a segment of the subnet are not sequential, for which {@link #getAddress()} throws {@link IncompatibleAddressException} because there is no single IPAddress value, there would be many.
* An IPAddress instance requires that all segments can be represented as a range of values.
* There are only two unusual circumstances when this can occur:
*
* - using masks on subnets specified with wildcard or range characters causing non-sequential segments such as the final IPv4 segment of 0.0.0.* with mask 0.0.0.128,
* this example translating to the two addresses 0.0.0.0 and 0.0.0.128, so the last IPv4 segment cannot be represented as a sequential range of values.
* - using wildcards or range characters in the IPv4 section of an IPv6 mixed address causing non-sequential segments such as the last IPv6 segment of ::ffff:0.0.*.0,
* this example translating to the addresses ::ffff:0:100, ::ffff:0:200, , ::ffff:0:300, ..., so the last IPv6 segment cannot be represented as a sequential range of values.
*
* These exceptions do not occur with non-subnets (ie individual addresses), nor can they occur with standard CIDR prefix-based subnets.
*
* This class is thread-safe. In fact, IPAddressString objects are immutable.
* An IPAddressString object represents a single IP address representation that cannot be changed after construction.
* Some of the derived state is created upon demand and cached, such as the derived IPAddress instances.
*
* This class has a few methods with analogs in IPAddress, such as {@link #contains(IPAddressString)}, {@link #getSequentialRange()},
* {@link #prefixEquals(IPAddressString)}, {@link #isIPv4()}, and {@link #isIPv6()}.
* Such methods are provided to make creating the IPAddress instance unnecessary when no such IPAddress instance is needed for other reasons.
*
* For some methods, like {@link #getSequentialRange()} and {@link #getDivisionGrouping()},
* there might not even be an associated IPAddress due to IncompatibleAddressException.
* However, this is generally only the case with subnets that have non-standard and unusual formats or masks.
*
* @custom.core
* @author sfoley
*
*/
/*
* The test class IPAddressTest and other test classes can be used to validate any changes to this class and others.
*
* A nice summary exists at http://www.gestioip.net/docu/ipv6_address_examples.html
*
* Some discussion of formats is https://tools.ietf.org/html/draft-main-ipaddr-text-rep-00
* Discussion of theses formats: http://tools.ietf.org/html/draft-main-ipaddr-text-rep-02
* RFCs of interest are 2732, 2373, 3986, 4291, 5952, 2765, 1918, 3513 (IPv4 rfcs 1123 0953) 1883 1884 (original spec of 3 string representations of IPv6), 4007 6874 for IPv6 zone identifier or scope id
* Early ones: 2460, 2553, 1122, 1812
*
* Nice cheat sheet for IPv6: http://www.roesen.org/files/ipv6_cheat_sheet.pdf
*
* Nice summary on zones and parsing http://veithen.github.io/2013/12/30/how-to-correctly-parse-ipv6-addresses.html
*
* Nice resource on IPv6 vs IPv4 and lots of stuff including MAC:
* https://communities.bmc.com/docs/DOC-19235
* Another: https://www.midnightfreddie.com/ipv6-ipv4-similar.html
*
* Some parsing code for various languages: https://rosettacode.org/wiki/Parse_an_IP_Address
* http://www.cisco.com/c/en/us/support/docs/ip/routing-information-protocol-rip/13788-3.html
*/
public class IPAddressString implements HostIdentifierString, Comparable {
private static final long serialVersionUID = 4L;
/*
* Generally permissive, settings are the default constants in IPAddressStringParameters.
* % denotes a zone, not an SQL wildcard (allowZone is true),
* and leading zeros are considered decimal, not octal (allow_inet_aton_octal is false).
*/
public static final IPAddressStringParameters DEFAULT_VALIDATION_OPTIONS = new IPAddressStringParameters.Builder().toParams();
private static final IPAddressString ipv4MappedPrefix = new IPAddressString("::ffff:0:0/96");
final IPAddressStringParameters validationOptions;
/* the full original string address */
final String fullAddr;
// fields for validation state
/* exceptions and booleans for validation - for type INVALID both of ipv6Exception and ipv4Exception are non-null */
private AddressStringException validateException;
// an object created by parsing that will provide the associated IPAddress(es)
private IPAddressProvider addressProvider = IPAddressProvider.NO_TYPE_PROVIDER;
/**
* Constructs an IPAddressString instance using the given String instance.
*
* @param addr the address in string format, either IPv4 like a.b.c.d or IPv6 like a:b:c:d:e:f:g:h or a:b:c:d:e:f:h.i.j.k or a::b or some other valid IPv4 or IPv6 form.
* IPv6 addresses are allowed to terminate with a scope id which starts with a % symbol.
* Both types of addresses can terminate with a network prefix value like a.b.c.d/24 or ::/24
* Optionally, you can specify just a network prefix value like /24, which represents the associated masks 255.255.255.0/24 or ffff:ff00::/24.
*
* Both IPv4 and IPv6 addresses can terminate with a mask instead of a prefix length, like a.b.c.d/255.0.0.0 or ::/ffff::
* If a terminating mask is equivalent to a network prefix, then it will be the same as specifying the prefix, so a.b.c.d/16 is the same as a.b.c.d/255.255.0.0
* If a terminating mask is not equivalent to a network prefix, then the mask will simply be applied to the address to produce a single address.
*
* You can also alter the addresses to include ranges using the wildcards * and -, such as 1.*.1-2.3.
*/
public IPAddressString(String addr) {
this(addr, DEFAULT_VALIDATION_OPTIONS);
}
/**
* @param addr the address in string format
*
* This constructor allows you to alter the default validation options.
*/
public IPAddressString(String addr, IPAddressStringParameters valOptions) {
if(addr == null) {
fullAddr = addr = "";
} else {
addr = addr.trim();
fullAddr = addr;
}
this.validationOptions = valOptions;
}
/**
* Provides an address string instance for an existing address.
*
* Not all valid address strings can be converted to address objects,
* such as the all address"*", or empty strings "", or prefix only addresses like "/32",
* so it can be useful to maintain a set of address strings instances.
*
* Even though the address exists already, the options provide the networks in use by the address, as well as options for creating new addresses from this address.
*
* @param address
* @param network
*/
IPAddressString(String addrString, IPAddress address, IPAddressStringParameters valOptions) {
validationOptions = valOptions;
fullAddr = addrString;
addressProvider = address.getProvider();
}
void cacheAddress(IPAddress address) {
if(addressProvider.isUninitialized()) {
addressProvider = address.getProvider();
}
}
public IPAddressStringParameters getValidationOptions() {
return validationOptions;
}
/**
* Returns whether this address string has an associated prefix length.
* If so, the prefix length is given by {@link #getNetworkPrefixLength()}
*
* @return whether this address string has an associated prefix length
*/
public boolean isPrefixed() {
return getNetworkPrefixLength() != null;
}
/**
* If this address is a valid address with an associated network prefix length then this returns that prefix length, otherwise returns null.
* The prefix length may be expressed explicitly with the notation "\xx" where xx is a decimal value, or it may be expressed implicitly as a network mask such as /255.255.0.0
*
* @return the prefix length or null
*/
public Integer getNetworkPrefixLength() {
if(isValid()) {
return addressProvider.getProviderNetworkPrefixLength();
}
return null;
}
/**
* If a mask was provided with this address string, this returns the resulting mask value.
*
* @return
*/
public IPAddress getMask() {
if(isValid()) {
return addressProvider.getProviderMask();
}
return null;
}
/**
* Returns whether the address represents a valid specific IP address or subnet, either IPv4 or IPv6,
* as opposed to an empty string, the address representing all addresses of all types, a prefix length, or an invalid format.
*
* @return whether the address represents a valid specific IP address.
*/
public boolean isIPAddress() {
return isValid() && addressProvider.isProvidingIPAddress();
}
/**
* Returns true if the string represents all IP addresses, such as the string "*"
* You can denote all IPv4 addresses with *.*, or all IPv6 addresses with *:*
*
* @return whether the address represents the set all all valid IP addresses (as opposed to an empty string, a specific address, a prefix length, or an invalid format).
*/
public boolean isAllAddresses() {
return isValid() && addressProvider.isProvidingAllAddresses();
}
/**
* Returns whether this address string represents only a prefix length with no associated address value,
* as opposed to an empty string, an address with or without a prefix length, or an invalid format.
*
* @return whether the address represents a valid IP address network prefix length
*/
public boolean isPrefixOnly() {
return isValid() && addressProvider.isProvidingPrefixOnly();
}
/**
* Returns true if the address string is empty (zero-length).
* @return
*/
public boolean isEmpty() {
return isValid() && addressProvider.isProvidingEmpty();
}
/**
* Returns true if the address is IPv4 (with or without a network prefix, with or without wildcard segments).
* @return
*/
public boolean isIPv4() {
return isValid() && addressProvider.isProvidingIPv4();
}
/**
* Returns true if the address is IPv6 (with or without a network prefix, with or without wildcard segments).
* @return
*/
public boolean isIPv6() {
return isValid() && addressProvider.isProvidingIPv6();
}
/**
* Returns true if the address is an IPv6 IPv4-mapped address.
* @return
*/
public boolean isIPv4Mapped() {
return isIPv6() && ipv4MappedPrefix.prefixEquals(this);
}
/**
* If this address string represents an IPv6 address, returns whether the lower 4 bytes were represented as IPv4
* @return
*/
public boolean isMixedIPv6() {
return isIPv6() && addressProvider.isProvidingMixedIPv6();
}
/**
* If this address string represents an IPv6 address, returns whether the string was base 85
* @return
*/
public boolean isBase85IPv6() {
return isIPv6() && addressProvider.isProvidingBase85IPv6();
}
/**
* Returns the IP address version if {@link #isIPAddress()} returns true, otherwise returns null
*
* @return the version
*/
public IPVersion getIPVersion() {
if(isValid()) {
return addressProvider.getProviderIPVersion();// this can also be null
}
return null;
}
/**
* Returns whether this string represents a loopback IP address.
*
* @see java.net.InetAddress#isLoopbackAddress()
*/
public boolean isLoopback() {
IPAddress val = getAddress();
return val != null && val.isLoopback();
}
/**
* Returns whether this string represents an IP address whose value is zero.
*
*/
public boolean isZero() {
IPAddress value = getAddress();
return value != null && value.isZero();
}
/**
* Returns whether this is a valid address string format.
*
* The accepted IP address formats are:
* an IPv4 address, an IPv6 address, a network prefix alone, the address representing all addresses of all types, or an empty string.
* If this method returns false, and you want more details, call validate() and examine the thrown exception.
*
* see {@link #validate()} or {@link #getAddressStringException()}
*
* @return whether this is a valid address string format
*/
public boolean isValid() {
if(addressProvider.isUninitialized()) {
try {
validate();
return true;
} catch(AddressStringException e) {
return false;
}
}
return !addressProvider.isInvalid();
}
/**
* Returns the parse exception thrown by validate, rather than throwing it.
* If there is no AddressStringException, then the string is a valid format.
* However, IncompatibleAddressException can be thrown if the format cannot be translated into the desired result,
* whether that is an address, a sequential range, or a division grouping.
* The translation fails only for subnets with non-standard formats or non-standard masks.
*
* See {@link #validate()} or {@link #isValid()}
*
* @return the parsing exception, if there is one
*/
public AddressStringException getAddressStringException() {
if(!addressProvider.isInvalid()) { // Avoid throwing the exception the second time with this check
try {
validate();
} catch(AddressStringException e) {
return e;/* note that this exception is cached by validate */
}
}
return validateException;
}
/**
* Validates that this string is a valid IPv4 address, and if not, throws an exception with a descriptive message indicating why it is not.
* @throws AddressStringException
*/
public void validateIPv4() throws AddressStringException {
validate(IPVersion.IPV4);
checkIPv4Exception();
}
/**
* Validates that this string is a valid IPv6 address, and if not, throws an exception with a descriptive message indicating why it is not.
* @throws AddressStringException
*/
public void validateIPv6() throws AddressStringException {
validate(IPVersion.IPV6);
checkIPv6Exception();
}
/**
* Validates that this string is a valid address, and if not, throws an exception with a descriptive message indicating why it is not.
* @throws AddressStringException
*/
@Override
public void validate() throws AddressStringException {
validate(null);
}
private void checkIPv4Exception() throws AddressStringException {
IPVersion version = addressProvider.getProviderIPVersion();
if(version != null && version.isIPv6()) {
throw new AddressStringException("ipaddress.error.address.is.ipv6");
} else if(validateException != null) {
throw validateException;
}
}
private void checkIPv6Exception() throws AddressStringException {
IPVersion version = addressProvider.getProviderIPVersion();
if(version != null && version.isIPv4()) {
throw new AddressStringException("ipaddress.error.address.is.ipv4");
} else if(validateException != null) {
throw validateException;
}
}
private boolean isValidated(IPVersion version) throws AddressStringException {
if(!addressProvider.isUninitialized()) {
if(version == null) {
if(validateException != null) {
throw validateException; // the two exceptions are the same, so we can choose either one
}
} else if(version.isIPv4()) {
checkIPv4Exception();
} else if(version.isIPv6()) {
checkIPv6Exception();
}
return true;
}
return false;
}
protected HostIdentifierStringValidator getValidator() {
return Validator.VALIDATOR;
}
private void validate(IPVersion version) throws AddressStringException {
if(isValidated(version)) {
return;
}
synchronized(this) {
if(isValidated(version)) {
return;
}
//we know nothing about this address. See what it is.
try {
addressProvider = getValidator().validateAddress(this);
} catch(AddressStringException e) {
validateException = e;
addressProvider = IPAddressProvider.INVALID_PROVIDER;
throw e;
}
}
}
/**
* Validates that the string has the format "/x" for a valid prefix length x.
* @param ipVersion IPv4, IPv6, or null if you do not know in which case it will be assumed that it can be either
* @param networkPrefixLength the network prefix length integer as a string, eg "24"
* @return the network prefix length
* @throws IncompatibleAddressException if invalid with an appropriate message
*/
public static int validateNetworkPrefixLength(IPVersion ipVersion, CharSequence networkPrefixLength) throws PrefixLenException {
try {
return Validator.VALIDATOR.validatePrefix(networkPrefixLength, ipVersion);
} catch(AddressStringException e) {
throw new PrefixLenException(networkPrefixLength, ipVersion, e);
}
}
public static void validateNetworkPrefix(IPVersion ipVersion, int networkPrefixLength, boolean allowPrefixesBeyondAddressSize) throws PrefixLenException {
boolean asIPv4 = (ipVersion != null && ipVersion.isIPv4());
if(networkPrefixLength > (asIPv4 ? IPv4Address.BIT_COUNT : IPv6Address.BIT_COUNT)) {
throw new PrefixLenException(networkPrefixLength, ipVersion);
}
}
@Override
public int hashCode() {
if(isValid()) {
try {
return addressProvider.providerHashCode();
} catch(IncompatibleAddressException e) {}
}
return toString().hashCode();
}
/**
* All address strings are comparable. If two address strings are invalid, their strings are compared.
* Otherwise, address strings are compared according to which type or version of string, and then within each type or version
* they are compared using the comparison rules for addresses.
*
* @param other
* @return
*/
@Override
public int compareTo(IPAddressString other) {
if(this == other) {
return 0;
}
boolean isValid = isValid();
boolean otherIsValid = other.isValid();
if(isValid || otherIsValid) {
try {
return addressProvider.providerCompare(other.addressProvider);
} catch(IncompatibleAddressException e) {}
}
return toString().compareTo(other.toString());
}
/**
* Similar to {@link #equals(Object)}, but instead returns whether the prefix of this address matches the same of the given address,
* using the prefix length of this address.
*
* In other words, determines if the other address is in the same prefix subnet using the prefix length of this address.
*
* It this address has no prefix length, returns false. The other address need not have an associated prefix length for this method to return true.
*
* If this address string or the given address string is invalid, returns false.
*
* @param other
* @return
*/
public boolean prefixEquals(IPAddressString other) {
// getting the prefix
if(other == this && !isPrefixOnly()) {
return true;
}
if(!isValid()) {
return false;
}
if(other.addressProvider.isUninitialized()) { // other not yet validated - if other is validated no need for this quick contains
// do the quick check that uses only the String of the other, matching til the end of the prefix length, for performance
Boolean directResult = addressProvider.prefixEquals(other.fullAddr);
if(directResult != null) {
return directResult.booleanValue();
}
}
if(other.isValid()) {
Boolean directResult = addressProvider.prefixEquals(other.addressProvider);
if(directResult != null) {
return directResult.booleanValue();
}
IPAddress thisAddress = getAddress();
if(thisAddress != null) {
IPAddress otherAddress = other.getAddress();
if(otherAddress != null) {
return thisAddress.prefixEquals(otherAddress);
}
}
// one or both addresses are null, so there is no prefix to speak of
}
return false;
}
/**
* Similar to {@link #prefixEquals(IPAddressString)}, but instead returns whether the prefix of this address contains the same of the given address,
* using the prefix length of this address.
*
* In other words, determines if the other address is in one of the same prefix subnets using the prefix length of this address.
*
* It this address has no prefix length, returns false. The other address need not have an associated prefix length for this method to return true.
*
* If this address string or the given address string is invalid, returns false.
*
* @param other
* @return
*/
public boolean prefixContains(IPAddressString other) {
if(other == this && !isPrefixOnly()) {
return true;
}
if(!isValid()) {
return false;
}
if(other.addressProvider.isUninitialized()) { // other not yet validated - if other is validated no need for this quick contains
// do the quick check that uses only the String of the other, matching til the end of the prefix length, for performance
Boolean directResult = addressProvider.prefixContains(other.fullAddr);
if(directResult != null) {
return directResult.booleanValue();
}
}
if(other.isValid()) {
Boolean directResult = addressProvider.prefixContains(other.addressProvider);
if(directResult != null) {
return directResult.booleanValue();
}
IPAddress thisAddress = getAddress();
if(thisAddress != null) {
IPAddress otherAddress = other.getAddress();
if(otherAddress != null) {
return thisAddress.prefixContains(otherAddress);
}
}
// one or both addresses are null, so there is no prefix to speak of
}
return false;
}
/**
* Two IPAddressString 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.
*
* If an IPAddressString is invalid, it is equal to another address only if the other address was constructed from the same string.
*
*/
@Override
public boolean equals(Object o) {
if(o == this) {
return true;
}
if(o instanceof IPAddressString) {
IPAddressString other = (IPAddressString) o;
// if they have the same string, they must be the same,
// but the converse is not true, if they have different strings, they can
// still be the same because IPv6 addresses have many representations
// and additional things like leading zeros can have an effect for IPv4
// Also note that we do not call equals() on the validation options, this is intended as an optimization,
// and probably better to avoid going through all the validation objects here
boolean stringsMatch = toString().equals(other.toString());
if(stringsMatch && validationOptions == other.validationOptions) {
return true;
}
if(isValid()) {
if(other.isValid()) {
Boolean directResult = addressProvider.parsedEquals(other.addressProvider);
if(directResult != null) {
return directResult.booleanValue();
}
try {
// When a value provider produces no value, equality and comparison are based on the enum IPType,
// which can be null.
return addressProvider.providerEquals(other.addressProvider);
} catch(IncompatibleAddressException e) {
return stringsMatch;
}
}
} else if(!other.isValid()) {
return stringsMatch; // Two invalid addresses are not equal unless strings match, regardless of validation options
}
}
return false;
}
/**
* Returns whether the address subnet identified by this address string contains the address identified by the given string.
*
* If this address string or the given address string is invalid then returns false.
*
* @param other
* @return
*/
public boolean contains(IPAddressString other) {
if(isValid()) {
if(other == this) {
return true;
}
if(other.addressProvider.isUninitialized()) { // other not yet validated - if other is validated no need for this quick contains
//do the quick check that uses only the String of the other
Boolean directResult = addressProvider.contains(other.fullAddr);
if(directResult != null) {
return directResult.booleanValue();
}
}
if(other.isValid()) {
// note the quick result also handles the case of "all addresses"
Boolean directResult = addressProvider.contains(other.addressProvider);
if(directResult != null) {
return directResult.booleanValue();
}
IPAddress addr = getAddress();
if(addr != null) {
IPAddress otherAddress = other.getAddress();
if(otherAddress != null) {
return addr.contains(otherAddress);
}
}
}
}
return false;
}
/**
* If this address string was constructed from a host address with prefix length,
* then this provides just the host address, rather than the address
* provided by {@link #getAddress()} that incorporates the prefix.
*
* Otherwise this returns the same object as {@link #getAddress()}.
*
* This method returns null for invalid formats, the equivalent method {@link #toHostAddress()} throws exceptions for invalid formats.
*
* @return
*/
public IPAddress getHostAddress() {
if(!addressProvider.isInvalid()) { // Avoid the exception the second time with this check
try {
return toHostAddress();
} catch(AddressStringException e) { /* note that this exception is cached, it is not lost forever */
} catch(IncompatibleAddressException e) { /* this will be rethrown each time attempting to construct address */ }
}
return null;
}
/**
* Similar to {@link #toAddress(inet.ipaddr.IPAddress.IPVersion)}, but returns null rather than throwing an exception with the address is invalid or does not match the supplied version.
*
*/
public IPAddress getAddress(IPVersion version) {
if(!addressProvider.isInvalid()) { // Avoid the exception the second time with this check
try {
return toAddress(version);
} catch(AddressStringException e) { /* note that this exception is cached, it is not lost forever */
} catch(IncompatibleAddressException e) { /* this will be rethrown each time attempting to construct address */ }
}
return null;
}
/**
* If this represents an ip address, returns that address. Otherwise, returns null.
*
* This method will return null for invalid formats. Use {@link #toAddress()} for an equivalent method that throws exceptions for invalid formats.
*
* If you have a prefix address and you wish to get only the host without the prefix, use {@link #getHostAddress()}
*
* @return the address
*/
@Override
public IPAddress getAddress() {
if(!addressProvider.isInvalid()) { // Avoid the exception the second time with this check
try {
return toAddress();
} catch(AddressStringException e) { /* note that this exception is cached, it is not lost forever */
} catch(IncompatibleAddressException e) { /* this will be rethrown each time attempting to construct address */ }
}
return null;
}
/**
* Returns whether the addresses returned by this IPAddressString are sequential,
* meaning that if any address has a numerical value that lies in between the numerical values of two addresses represented by this IPAddressString,
* then that address is also represented by this IPAddressString. In other words, the represented range of address values is sequential.
*
* When the IPAddressString is sequential, it can be represented exactly by the IPAddressSeqRange returned from {@link #getSequentialRange()}.
* In some cases, no IPAddress instance can be obtained from {@link #getAddress()} or {@link #toAddress()}, in the cases where {@link #toAddress()} throws IncompatibleAddressException,
* but if the IPAddressString is sequential, you can obtain a IPAddressSeqRange to represent the IPAddressString instead.
*
* @return
*/
public boolean isSequential() {
return isValid() && addressProvider.isSequential();
}
/**
* Returns a representation of the address string, the address string represented "as-is", converted to value ranges with bit sizes matching the original string.
* The returned series has the same division count and division bit sizes as in the original string.
* The method does not attempt to convert to the standard segment counts or bit sizes of IPv4 or IPv6.
* For the IPv4 or IPv6 representation, use {@link #getAddress()}.
*
* Examples of strings that do not have the standard segment counts and bit lengths include
* IPv6 addresses in mixed IPv6/IPv4 format, compressed IPv6 addresses, IPv6 addresses expressed as a single segment,
* IPv4 addresses in inet_aton form with fewer than 4 segments, and IPv4 or IPv6 addresses in which multiple segments are covered by the '*' wildcard.
*
* The returned types is either IPAddressDivisionGrouping or IPAddressLargeDivisionGrouping in cases where one of the divisions is 64 bits or large.
* This does not return instances of IPAddress, for that you should call {@link #getAddress()} or {@link #toAddress()}
*
* This can be useful for parsing formats that do not convert directly to a single instance of IPAddress,
* such as ranges of non-segmented IPv6 address values like aaaabbbbccccddddeeeeffffaaaabbb-ffffeeeeddddccccbbbbaaaabbbbaaaa,
* which in most cases cannot be converted to 8 ipv6 segment ranges and thus cannot be converted to a single IPAddress instance.
*
* If the string used to construct this object is not a known format (empty string, address, range of addresses, or prefix) then null is returned.
*
* If the string used to construct this object is a valid subnet format with a non-standard mask, and the masked result has divisions that are not sequential ranges, then null is returned.
*
* An equivalent method that throws exceptions for invalid or incompatible formats is {@link #toDivisionGrouping()}
*
* @return
*/
public IPAddressDivisionSeries getDivisionGrouping() {
if(!addressProvider.isInvalid()) { // Avoid the exception the second time with this check
try {
validate();
return addressProvider.getDivisionGrouping();
} catch(AddressStringException e) { /* note that this exception is cached, it is not lost forever */
} catch(IncompatibleAddressException e) { /* this will be rethrown each time attempting to construct address */ }
}
return null;
}
/**
* Returns the range of sequential addresses from the lowest address specified in this address string to the highest.
*
* Since not all IPAddressString instances describe a sequential series of addresses,
* this does not necessarily match the exact set of addresses specified by the string.
* For example, 1-2.3.4.1-2 produces the sequential range 1.3.4.1 to 2.3.4.2 that includes the address 1.255.255.2 not specified by the string.
*
* The sequential range matches the same set of addresses as the address string or the address when {@link #isSequential()} is true.
* Otherwise, the range includes addresses not specified by the address string.
*
* This method can also produce a range for a string for which no IPAddress instance can be created,
* those cases where {@link #isValid()} returns true but {@link #toAddress()} throws IncompatibleAddressException and {@link #getAddress()} returns null.
* The range cannot be produced for the other cases where {@link #getAddress()} returns null, those that are version-ambiguous and do not throw IncompatibleAddressException,
* such as the all address '*' or the version-ambiguous prefix length '/32'.
*
* This is similar to {@link #toSequentialRange()} except that for invalid address strings, null is returned rather than throwing an exception.
* @return
*/
public IPAddressSeqRange getSequentialRange() {
if(!addressProvider.isInvalid()) { // Avoid the exception the second time with this check
try {
validate();
return addressProvider.getProviderSeqRange();
} catch(AddressStringException e) { /* note that this exception is cached, it is not lost forever */ }
// catching IncompatibleAddressException not necessary since it is not thrown, once parsed there is always an upper and lower
}
return null;
}
/**
* Returns a representation of the address string, the address string represented "as-is", converted to value ranges with bit sizes matching the original string.
* The returned series has the same division count and division bit sizes as in the original string.
* The method does not attempt to convert to the standard segment counts or bit sizes of IPv4 or IPv6.
* For the IPv4 or IPv6 representation, use {@link #getAddress()}.
*
* If the string used to construct this object is not a known format (empty string, address, range of addresses, or prefix) then this method throws {@link AddressStringException}.
*
* If the string used to construct this object is a valid subnet format with a non-standard mask, and the masked result has divisions that are not sequential ranges, then this method throws {@link IncompatibleAddressException}.
*
* An equivalent method that does not throw exceptions for invalid or incompatible formats is {@link #getDivisionGrouping()}
*
* Examples of strings that do not have the standard segment counts and bit lengths include
* IPv6 addresses in mixed IPv6/IPv4 format, compressed IPv6 addresses, IPv6 addresses expressed as a single segment,
* IPv4 addresses in inet_aton form with fewer than 4 segments, and IPv4 or IPv6 addresses in which multiple segments are covered by the '*' wildcard.
*
* The returned type is either IPAddressDivisionGrouping or IPAddressLargeDivisionGrouping. It is IPAddressLargeDivisionGrouping in cases where one of the divisions is 64 bits or large.
* This does not return instances of IPAddress, for that you should call {@link #getAddress()} or {@link #toAddress()}
*
* This can be useful for parsing formats that do not convert directly to a single instance of IPAddress,
* such as ranges of non-segmented IPv6 address values like aaaabbbbccccddddeeeeffffaaaabbb-ffffeeeeddddccccbbbbaaaabbbbaaaa,
* which in most cases cannot be converted to 8 ipv6 segment ranges and thus cannot be converted to a single IPAddress instance.
*
* @return
*/
public IPAddressDivisionSeries toDivisionGrouping() throws AddressStringException, IncompatibleAddressException {
validate();
return addressProvider.getDivisionGrouping();
}
/**
* Returns the range of sequential addresses from the lowest address specified in this address string to the highest.
*
* Since not all IPAddressString instances describe a sequential series of addresses,
* this does not necessarily match the exact set of addresses listed by the string.
* For example, 1-2.3.4.1-2 produces the sequential range 1.3.4.1 to 2.3.4.2 that includes the address 1.255.255.2 not specified by the string.
*
* The sequential range matches the same set of addresses as the address string or the address when {@link #isSequential()} is true.
* Otherwise, the range includes addresses not specified by the address string.
*
* This method can also produce a range for a string for which no IPAddress instance can be created. This method does not throw IncompatibleAddressException.
* This method does not throw for those cases where {@link #isValid()} returns true but {@link #toAddress()} throws IncompatibleAddressException and {@link #getAddress()} returns null.
*
* There are some cases where this method returns null. The range cannot be produced for the other cases where {@link #getAddress()} returns null, those that are version-ambiguous and do not throw IncompatibleAddressException,
* such as the all address '*' or the version-ambiguous prefix '/32'.
*
* Keep in mind that all single addresses, all subnets using written in the canonical address formats,
* and all subnets with standard network or host masks, all of these have an associated IPAddress instance.
*
* The exceptional cases are those subnets represented in formats supported by IPAddressString that cannot be represented in the canonical formats but can be .
* This includes IPv6 mixed address subnets that cannot be converted to canonical IPv6 format like ::0-1.2.0-1.4,
* subnets with non-standard masks like 0-2.2.3.4/2.0.0.0, and subnets represented with non-canonical segments like the IPv4 subnet 1.5000-6000
* or the IPv6 subnet 1234567890abcdef1234567890abcdef-1234567890abcdef1234567890abcdef.
*
* This method is equivalent to {@link #getSequentialRange()} except that for invalid address string formats, AddressStringException is thrown by this method.
*
* @return
*/
public IPAddressSeqRange toSequentialRange() throws AddressStringException {
validate();
return addressProvider.getProviderSeqRange();
}
/**
* If this address string was constructed from a string comprising of a host address with prefix length or mask,
* then this provides just the host address, rather than the address with the prefix or mask applied that is
* provided by {@link #toAddress()}.
*
* Otherwise this returns the same object as {@link #toAddress()}.
*
* This method throws exceptions for invalid formats, the equivalent method {@link #getHostAddress()} will simply return null in such cases.
*
* If this instance of IPAddressString did not originate from a string, but from an IPAddress,
* then this will return an address that parses from a string to the same IPAddress (with prefix length added to the string if necessary to match).
*
* This method is is intended to operate on the string that is wrapped by IPAddressString (visible from {@link #toString()})
*
* @return
*/
public IPAddress toHostAddress() throws AddressStringException, IncompatibleAddressException {
validate(); // call validate so that we throw consistently, cover type == INVALID, and ensure the addressProvider exists
return addressProvider.getProviderHostAddress();
}
/**
* Produces the {@link IPAddress} of the specified address version corresponding to this IPAddressString.
*
* In most cases the string indicates the address version and calling {@link #toAddress()} is sufficient, with a few exceptions.
*
* When this object represents only a network prefix length,
* specifying the address version allows the conversion to take place to the associated mask for that prefix length.
*
* When this object represents all addresses, specifying the address version allows the conversion to take place
* to the associated representation of all IPv4 or all IPv6 addresses.
*
* When this object represents the empty string and that string is interpreted as a loopback, then it returns
* the corresponding loopback address. If empty strings are not interpreted as loopback, null is returned.
*
* When this object represents an ipv4 or ipv6 address, it returns that address if and only if that address matches the provided version.
*
* If the string used to construct this object is an invalid format,
* or a format that does not match the provided version, then this method throws {@link AddressStringException}.
*
* @param version the address version that this address should represent.
* @return the address or null if the parsed address version does not match
* @throws AddressStringException
* @throws IncompatibleAddressException address in proper format cannot be converted to an address: for masks inconsistent with associated address range, or ipv4 mixed segments that cannot be joined into ipv6 segments
*/
public IPAddress toAddress(IPVersion version) throws AddressStringException, IncompatibleAddressException {
validate(); // call validate so that we throw consistently, cover type == INVALID, and ensure the addressProvider exists
return addressProvider.getProviderAddress(version);
}
/**
* Produces the {@link IPAddress} corresponding to this IPAddressString.
*
* If this object does not represent a specific IPAddress or a ranged IPAddress, null is returned,
* which may be the case if this object represents only a network prefix or if it represents the empty address string.
*
* If the string used to construct this object is not a known format (empty string, address, range of addresses, or prefix) then this method throws {@link AddressStringException}.
*
* An equivalent method that does not throw exception for invalid formats is {@link #getAddress()}
*
* If you have a prefixed address and you wish to get only the host rather than the address with the prefix, use {@link #toHostAddress()}
*
*
* As long as this object represents a valid address (but not necessarily a specific address), this method does not throw.
*
* @throws AddressStringException if the address format is invalid
* @throws IncompatibleAddressException if a valid address string representing multiple addresses cannot be represented
* This happens only for masks inconsistent with the associated address ranges, or ranges in ipv4 mixed segments that cannot be joined into ipv6 segments
*
*/
@Override
public IPAddress toAddress() throws AddressStringException, IncompatibleAddressException {
validate(); //call validate so that we throw consistently, cover type == INVALID, and ensure the addressProvider exists
return addressProvider.getProviderAddress();
}
/**
* Increases or decreases prefix length to the next segment boundary of the given address version's standard segment boundaries.
*
* This acts on address strings with an associated prefix length, whether or not there is also an associated address value, see {@link IPAddressString#isPrefixOnly()}.
* If there is no associated address value then the segment boundaries are considered to be at each byte, much like IPv4.
*
* If the address string has prefix length 0 and represents all addresses of the same version,
* and the prefix length is being decreased, then the address representing all addresses of any version is returned.
*
* Follows the same rules as {@link #adjustPrefixLength(int)} when there is an associated address value:
* When prefix length is increased, the bits moved within the prefix become zero.
* When a prefix length is decreased, the bits moved outside the prefix become zero.
*
* Also see {@link IPAddress#adjustPrefixBySegment(boolean)}
* @param nextSegment whether to move prefix to previous or following segment boundary
* @return
*/
public IPAddressString adjustPrefixBySegment(boolean nextSegment) {
if(isPrefixOnly()) {
// Use IPv4 segment boundaries
int bitsPerSegment = IPv4Address.BITS_PER_SEGMENT;
int existingPrefixLength = getNetworkPrefixLength();
int newBits;
if(nextSegment) {
int adjustment = existingPrefixLength % bitsPerSegment;
newBits = Math.min(IPv6Address.BIT_COUNT, existingPrefixLength + bitsPerSegment - adjustment);
} else {
int adjustment = ((existingPrefixLength - 1) % bitsPerSegment) + 1;
newBits = Math.max(0, existingPrefixLength - adjustment);
}
return new IPAddressString(IPAddressNetwork.getPrefixString(newBits), validationOptions);
}
IPAddress address = getAddress();
if(address == null) {
return null;
}
Integer prefix = address.getNetworkPrefixLength();
if(!nextSegment && prefix != null && prefix == 0 && address.isMultiple() && address.isPrefixBlock()) {
return new IPAddressString(IPAddress.SEGMENT_WILDCARD_STR, validationOptions);
}
return address.adjustPrefixBySegment(nextSegment).toAddressString();
}
/**
* Increases or decreases prefix length by the given increment.
*
* This acts on address strings with an associated prefix length, whether or not there is also an associated address value.
*
* If the address string has prefix length 0 and represents all addresses of the same version,
* and the prefix length is being decreased, then the address representing all addresses of any version is returned.
*
* When there is an associated address value and the prefix length is increased, the bits moved within the prefix become zero,
* and if prefix length is extended beyond the segment series boundary, it is removed.
* When there is an associated address value
* and the prefix length is decreased, the bits moved outside the prefix become zero.
*
* Also see {@link IPAddress#adjustPrefixLength(int)}
* @param adjustment
* @return
*/
public IPAddressString adjustPrefixLength(int adjustment) {
if(isPrefixOnly()) {
int newBits = adjustment > 0 ? Math.min(IPv6Address.BIT_COUNT, getNetworkPrefixLength() + adjustment) : Math.max(0, getNetworkPrefixLength() + adjustment);
return new IPAddressString(IPAddressNetwork.getPrefixString(newBits), validationOptions);
}
IPAddress address = getAddress();
if(address == null) {
return null;
}
if(adjustment == 0 && isPrefixed()) {
return this;
}
Integer prefix = address.getNetworkPrefixLength();
if(prefix != null && prefix + adjustment < 0 && address.isPrefixBlock()) {
return new IPAddressString(IPAddress.SEGMENT_WILDCARD_STR, validationOptions);
}
return address.adjustPrefixLength(adjustment).toAddressString();
}
/**
* Given a string with comma delimiters to denote segment elements, this method will count the possible combinations.
*
* For example, given "1,2.3.4,5.6" this method will return 4 for the possible combinations: "1.3.4.6", "1.3.5.6", "2.3.4.6" and "2.3.5.6"
*
* @param str
* @return
*/
public static int countDelimitedAddresses(String str) {
int segDelimitedCount = 0;
int result = 1;
for(int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
if(isDelimitedBoundary(c)) {
if(segDelimitedCount > 0) {
result *= segDelimitedCount + 1;
segDelimitedCount = 0;
}
} else if(c == SEGMENT_VALUE_DELIMITER) {
segDelimitedCount++;
}
}
if(segDelimitedCount > 0) {
result *= segDelimitedCount + 1;
}
return result;
}
private static boolean isDelimitedBoundary(char c) {
return c == IPv4Address.SEGMENT_SEPARATOR ||
c == IPv6Address.SEGMENT_SEPARATOR ||
c == Address.RANGE_SEPARATOR ||
c == MACAddress.DASHED_SEGMENT_RANGE_SEPARATOR;
}
/**
* Given a string with comma delimiters to denote segment elements, this method will provide an iterator to iterate through the possible combinations.
*
* For example, given "1,2.3.4,5.6" this will iterate through "1.3.4.6", "1.3.5.6", "2.3.4.6" and "2.3.5.6"
*
* Another example: "1-2,3.4.5.6" will iterate through "1-2.4.5.6" and "1-3.4.5.6".
*
* This method will not validate strings. Each string produced can be validated using an instance of IPAddressString.
*
* @param str
* @return
*/
public static Iterator parseDelimitedSegments(String str) {
List> parts = null;
int lastSegmentStartIndex = 0;
int lastPartIndex = 0;
int lastDelimiterIndex = 0;
boolean anyDelimited = false;
List delimitedList = null;
for(int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
if(isDelimitedBoundary(c)) {
if(delimitedList != null) {
if(parts == null) {
parts = new ArrayList>(8);
}
addParts(str, parts, lastSegmentStartIndex, lastPartIndex, lastDelimiterIndex, delimitedList, i);
lastPartIndex = i;
delimitedList = null;
}
lastSegmentStartIndex = lastDelimiterIndex = i + 1;
} else if(c == SEGMENT_VALUE_DELIMITER) {
anyDelimited = true;
if(delimitedList == null) {
delimitedList = new ArrayList();
}
String sub = str.substring(lastDelimiterIndex, i);
delimitedList.add(sub);
lastDelimiterIndex = i + 1;
}
}
if(anyDelimited) {
if(delimitedList != null) {
if(parts == null) {
parts = new ArrayList>(8);
}
addParts(str, parts, lastSegmentStartIndex, lastPartIndex, lastDelimiterIndex, delimitedList, str.length());
} else {
parts.add(Arrays.asList(new String[] {str.substring(lastPartIndex, str.length())}));
}
return iterator(parts);
}
return new Iterator() {
boolean done;
@Override
public boolean hasNext() {
return !done;
}
@Override
public String next() {
if(done) {
throw new NoSuchElementException();
}
done = true;
return str;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
private static Iterator iterator(List> parts) {
return new Iterator() {
private boolean done;
final int partCount = parts.size();
@SuppressWarnings("unchecked")
private final Iterator variations[] = new Iterator[partCount];
private String nextSet[] = new String[partCount]; {
updateVariations(0);
}
private void updateVariations(int start) {
for(int i = start; i < partCount; i++) {
variations[i] = parts.get(i).iterator();
nextSet[i] = variations[i].next();
}
}
@Override
public boolean hasNext() {
return !done;
}
@Override
public String next() {
if(done) {
throw new NoSuchElementException();
}
StringBuilder result = new StringBuilder();
for(int i = 0; i < partCount; i++) {
result.append(nextSet[i]);
}
increment();
return result.toString();
}
private void increment() {
for(int j = partCount - 1; j >= 0; j--) {
if(variations[j].hasNext()) {
nextSet[j] = variations[j].next();
updateVariations(j + 1);
return;
}
}
done = true;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
private static void addParts(String str, List> parts, int lastSegmentStartIndex, int lastPartIndex,
int lastDelimiterIndex, List delimitedList, int i) {
String sub = str.substring(lastDelimiterIndex, i);
delimitedList.add(sub);
if(lastPartIndex != lastSegmentStartIndex) {
parts.add(Arrays.asList(new String[] {str.substring(lastPartIndex, lastSegmentStartIndex)}));
}
parts.add(delimitedList);
}
/**
* Converts this address to a prefix length
*
* @return the prefix of the indicated IP type represented by this address or null if this address is valid but cannot be represented by a network prefix length
* @throws AddressStringException if the address is invalid
*/
public String convertToPrefixLength() throws AddressStringException {
IPAddress address = getAddress();
Integer prefix;
if(address == null) {
prefix = getNetworkPrefixLength(); // handles prefix-only, but also handles cases of IncompatibleAddressException in which there is a prefix length
if(prefix == null) {
return null;
}
} else {
prefix = address.getBlockMaskPrefixLength(true);
if(prefix == null) {
return null;
}
}
return IPAddressSegment.toUnsignedString(prefix, 10,
new StringBuilder(IPAddressSegment.toUnsignedStringLength(prefix, 10) + 1).append(IPAddress.PREFIX_LEN_SEPARATOR)).toString();
}
private static String toNormalizedString(IPAddressProvider addressProvider) throws IncompatibleAddressException {
String result;
if(addressProvider.isProvidingAllAddresses()) {
result = IPAddress.SEGMENT_WILDCARD_STR;
} else if(addressProvider.isProvidingEmpty()) {
result = "";
} else if(addressProvider.isProvidingPrefixOnly()) {
result = IPAddressNetwork.getPrefixString(addressProvider.getProviderNetworkPrefixLength());
} else if(addressProvider.isProvidingIPAddress()) {
result = addressProvider.getProviderAddress().toNormalizedString();
} else {
result = null;
}
return result;
}
@Override
public String toNormalizedString() {
if(isValid()) {
try {
return toNormalizedString(addressProvider);
} catch(IncompatibleAddressException e) {}
}
return toString();
}
/**
* Gives us the original string provided to the constructor.
* For variations on this string, call {@link #getAddress()}/{@link #toAddress()} and then use string methods on the address object.
*/
@Override
public String toString() {
return fullAddr;
}
}