![JAR search and dependency download from the Maven repository](/logo.png)
inet.ipaddr.format.AddressDivisionGroupingBase 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.format;
import java.math.BigInteger;
import java.net.InetAddress;
import java.util.Arrays;
import java.util.Iterator;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.ToLongFunction;
import inet.ipaddr.AddressComponent;
import inet.ipaddr.AddressNetwork.PrefixConfiguration;
import inet.ipaddr.HostIdentifierException;
import inet.ipaddr.IPAddress;
import inet.ipaddr.IPAddressSection.IPStringOptions;
import inet.ipaddr.IPAddressSection.WildcardOptions.WildcardOption;
import inet.ipaddr.PrefixLenException;
import inet.ipaddr.format.standard.AddressDivisionGrouping.StringOptions;
import inet.ipaddr.format.standard.AddressDivisionGrouping.StringOptions.Wildcards;
import inet.ipaddr.format.string.AddressStringDivision;
import inet.ipaddr.format.string.AddressStringDivisionSeries;
import inet.ipaddr.format.string.IPAddressStringDivision;
import inet.ipaddr.format.string.IPAddressStringDivisionSeries;
import inet.ipaddr.format.util.AddressComponentSpliterator;
import inet.ipaddr.format.util.AddressDivisionWriter;
import inet.ipaddr.format.util.AddressComponentRangeSpliterator;
import inet.ipaddr.format.util.AddressSegmentParams;
import inet.ipaddr.format.util.IPAddressStringWriter;
import inet.ipaddr.format.validate.ParsedAddressGrouping;
/**
* AddressDivisionGrouping objects consist of a series of AddressDivision objects, each division containing one or more segments.
*
* AddressDivisionGrouping objects are immutable. This also makes them thread-safe.
*
* AddressDivision objects use long to represent their values, so this places a cap on the size of the divisions in AddressDivisionGrouping.
*
* @author sfoley
*/
public abstract class AddressDivisionGroupingBase implements AddressDivisionSeries {
private static final long serialVersionUID = 1L;
protected static final Integer NO_PREFIX_LENGTH = -1;
static final BigInteger ALL_ONES = BigInteger.ZERO.not();
protected static BigInteger LONG_MAX = BigInteger.valueOf(Long.MAX_VALUE);
static ResourceBundle bundle;
static {
//reuse the same properties file
String propertyFileName = "IPAddressResources";
String name = HostIdentifierException.class.getPackage().getName() + '.' + propertyFileName;
try {
bundle = ResourceBundle.getBundle(name);
} catch (MissingResourceException e) {
System.err.println("bundle " + name + " is missing");
}
}
protected static class ValueCache {
/* the address grouping bytes */
public byte[] lowerBytes, upperBytes;
public BigInteger value, upperValue;
/* only used when address section is full section owned by an address */
public InetAddress inetAddress;
}
protected transient ValueCache valueCache;
private final AddressDivisionBase divisions[];
protected Integer cachedPrefixLength; // null indicates this field not initialized, NO_PREFIX indicates the prefix len is null
/* for addresses not multiple, we must check each segment, so we cache */
private transient Boolean isMultiple;
private transient BigInteger cachedCount;
private transient BigInteger cachedPrefixCount;
protected transient int hashCode;
public AddressDivisionGroupingBase(AddressDivisionBase divisions[]) {
this(divisions, true);
}
public AddressDivisionGroupingBase(AddressDivisionBase divisions[], boolean checkDivisions) {
this.divisions = divisions;
if(checkDivisions) {
for(int i = 0; i < divisions.length; i++) {
if(divisions[i] == null) {
throw new NullPointerException(getMessage("ipaddress.error.null.segment"));
}
}
}
}
protected static String getMessage(String key) {
if(bundle != null) {
try {
return bundle.getString(key);
} catch (MissingResourceException e1) {}
}
return key;
}
@Override
public AddressDivisionBase getDivision(int index) {
return getDivisionsInternal()[index];
}
protected void initCachedValues(Integer cachedNetworkPrefixLength, BigInteger cachedCount) {
this.cachedPrefixLength = cachedNetworkPrefixLength == null ? NO_PREFIX_LENGTH : cachedNetworkPrefixLength;
this.cachedCount = cachedCount;
}
@Override
public int getDivisionCount() {
return getDivisionsInternal().length;
}
/**
* Gets the bytes for the lowest address in the range represented by this address.
*
* Since bytes are signed values while addresses are unsigned, values greater than 127 are
* represented as the (negative) two's complement value of the actual value.
* You can get the unsigned integer value i from byte b using i = 0xff & b.
*
* @return
*/
@Override
public byte[] getBytes() {
return getBytesInternal().clone();
}
//gets the bytes, sharing the cached array and does not clone it
protected byte[] getBytesInternal() {
byte cached[];
if(hasNoValueCache() || (cached = valueCache.lowerBytes) == null) {
valueCache.lowerBytes = cached = getBytesImpl(true);
}
return cached;
}
/**
* Gets the value for the lowest address in the range represented by this address division grouping.
*
* If the value fits in the specified array, the same array is returned with the value.
* Otherwise, a new array is allocated and returned with the value.
*
* You can use {@link #getBitCount()} to determine the required array length for the bytes.
*
* Since bytes are signed values while addresses are unsigned, values greater than 127 are
* represented as the (negative) two's complement value of the actual value.
* You can get the unsigned integer value i from byte b using i = 0xff & b.
*
* @return
*/
@Override
public byte[] getBytes(byte bytes[], int index) {
return getBytesCopy(bytes, index, getBytesInternal());
}
/**
* Equivalent to {@link #getBytes(byte[], int)} with index of 0.
*/
@Override
public byte[] getBytes(byte bytes[]) {
return getBytes(bytes, 0);
}
private static byte[] getBytesCopy(byte[] bytes, int startIndex, byte[] cached) {
int byteCount = cached.length;
if(bytes == null || bytes.length < byteCount + startIndex) {
if(startIndex > 0) {
byte bytes2[] = new byte[byteCount + startIndex];
if(bytes != null) {
System.arraycopy(bytes, 0, bytes2, 0, Math.min(startIndex, bytes.length));
}
System.arraycopy(cached, 0, bytes2, startIndex, byteCount);
return bytes2;
}
return cached.clone();
}
System.arraycopy(cached, 0, bytes, startIndex, byteCount);
return bytes;
}
/**
* Gets the bytes for the highest address in the range represented by this address.
*
* @return
*/
@Override
public byte[] getUpperBytes() {
return getUpperBytesInternal().clone();
}
/**
* Gets the bytes for the highest address in the range represented by this address.
*
* @return
*/
protected byte[] getUpperBytesInternal() {
byte cached[];
if(hasNoValueCache()) {
ValueCache cache = valueCache;
cache.upperBytes = cached = getBytesImpl(false);
if(!isMultiple()) {
cache.lowerBytes = cached;
}
} else {
ValueCache cache = valueCache;
if((cached = cache.upperBytes) == null) {
if(!isMultiple()) {
if((cached = cache.lowerBytes) != null) {
cache.upperBytes = cached;
} else {
cache.lowerBytes = cache.upperBytes = cached = getBytesImpl(false);
}
} else {
cache.upperBytes = cached = getBytesImpl(false);
}
}
}
return cached;
}
/**
* Similar to {@link #getBytes(byte[], int)}, but for obtaining the upper value of the range.
* If this division represents a single value, equivalent to {@link #getBytes(byte[], int)}
*/
@Override
public byte[] getUpperBytes(byte bytes[], int index) {
return getBytesCopy(bytes, index, getUpperBytesInternal());
}
/**
* Equivalent to {@link #getBytes(byte[], int)} with index of 0.
*/
@Override
public byte[] getUpperBytes(byte bytes[]) {
return getBytes(bytes, 0);
}
protected abstract byte[] getBytesImpl(boolean low);
//only called in constructors
protected void setBytes(byte bytes[]) {
if(valueCache == null) {
valueCache = new ValueCache();
}
valueCache.lowerBytes = bytes;
}
//only called in constructors
protected void setUpperBytes(byte bytes[]) {
if(valueCache == null) {
valueCache = new ValueCache();
}
valueCache.upperBytes = bytes;
}
@Override
public BigInteger getValue() {
BigInteger cached;
if(hasNoValueCache() || (cached = valueCache.value) == null) {
valueCache.value = cached = new BigInteger(1, getBytesInternal());
}
return cached;
}
@Override
public BigInteger getUpperValue() {
BigInteger cached;
if(hasNoValueCache()) {
ValueCache cache = valueCache;
cache.upperValue = cached = new BigInteger(1, getUpperBytesInternal());
if(!isMultiple()) {
cache.value = cached;
}
} else {
ValueCache cache = valueCache;
if((cached = cache.upperValue) == null) {
if(!isMultiple()) {
if((cached = cache.value) != null) {
cache.upperValue = cached;
} else {
cache.value = cache.upperValue = cached = new BigInteger(1, getUpperBytesInternal());
}
} else {
cache.upperValue = cached = new BigInteger(1, getUpperBytesInternal());
}
}
}
return cached;
}
protected boolean hasNoValueCache() {
if(valueCache == null) {
synchronized(this) {
if(valueCache == null) {
valueCache = new ValueCache();
return true;
}
}
}
return false;
}
// only called from constructors, no locking is necessary
protected void setInetAddress(InetAddress addr) {
if(valueCache == null) {
valueCache = new ValueCache();
}
valueCache.inetAddress = addr;
}
@Override
public boolean isPrefixed() {
return getPrefixLength() != null;
}
@Override
public Integer getPrefixLength() {
return cachedPrefixLength;
}
protected static Integer calculatePrefix(IPAddressDivisionSeries series) {
int count = series.getDivisionCount();
if(count > 0) {
if(series.getNetwork().getPrefixConfiguration().allPrefixedAddressesAreSubnets() && !series.getDivision(count - 1).isPrefixed()) {
return null;
}
int result = 0;
for(int i = 0; i < count; i++) {
IPAddressGenericDivision div = series.getDivision(i);
Integer prefix = div.getDivisionPrefixLength();
if(prefix != null) {
result += prefix;
return ParsedAddressGrouping.cache(result);
} else {
result += div.getBitCount();
}
}
}
return null;
}
/**
* Returns the smallest prefix length possible such that this address division grouping includes the block of addresses for that prefix.
*
* @return the prefix length
*/
@Override
public int getMinPrefixLengthForBlock() {
int count = getDivisionCount();
int totalPrefix = getBitCount();
for(int i = count - 1; i >= 0 ; i--) {
AddressDivisionBase div = getDivision(i);
int segBitCount = div.getBitCount();
int segPrefix = div.getMinPrefixLengthForBlock();
if(segPrefix == segBitCount) {
break;
} else {
totalPrefix -= segBitCount;
if(segPrefix != 0) {
totalPrefix += segPrefix;
break;
}
}
}
return totalPrefix;
}
/**
* Returns a prefix length for which the range of this segment grouping matches the block of addresses for that prefix.
*
* If no such prefix exists, returns null
*
* If this segment grouping represents a single value, returns the bit length
*
* @return the prefix length or null
*/
@Override
public Integer getPrefixLengthForSingleBlock() {
int totalPrefix;
if(!isMultiple()) {
totalPrefix = getBitCount();
} else {
int count = getDivisionCount();
totalPrefix = 0;
for(int i = 0; i < count; i++) {
AddressDivisionBase div = getDivision(i);
Integer divPrefix = div.getPrefixLengthForSingleBlock();
if(divPrefix == null) {
return null;
}
totalPrefix += divPrefix;
if(divPrefix < div.getBitCount()) {
//remaining segments must be full range or we return null
for(i++; i < count; i++) {
AddressDivisionBase laterDiv = getDivision(i);
if(!laterDiv.isFullRange()) {
return null;
}
}
}
}
}
return cacheBits(totalPrefix);
}
protected static Integer getPrefixLengthForSingleBlock(IPAddressDivisionSeries series) {
int totalPrefix;
if(!series.isMultiple()) {
totalPrefix = series.getBitCount();
} else {
int count = series.getDivisionCount();
totalPrefix = 0;
boolean isAutoSubnets = series.getNetwork().getPrefixConfiguration().allPrefixedAddressesAreSubnets();
for(int i = 0; i < count; i++) {
IPAddressGenericDivision div = series.getDivision(i);
Integer divPrefix = div.getPrefixLengthForSingleBlock();
if(divPrefix == null) {
return null;
}
totalPrefix += divPrefix;
if(isAutoSubnets && div.isPrefixed()) {
return cacheBits(totalPrefix);
}
if(divPrefix < div.getBitCount()) {
//remaining divisions must be full range or we return null
for(i++; i < count; i++) {
IPAddressGenericDivision laterDiv = series.getDivision(i);
if(!laterDiv.isFullRange()) {
return null;
}
if(isAutoSubnets && laterDiv.isPrefixed()) {
return cacheBits(totalPrefix);
}
}
}
}
}
return cacheBits(totalPrefix);
}
protected static Integer cacheBits(int i) {
return ParsedAddressGrouping.cache(i);
}
/**
* gets the count of addresses that this address division grouping may represent
*
* If this address division grouping is not a subnet block of multiple addresses or has no range of values, then there is only one such address.
*
* @return
*/
@Override
public BigInteger getCount() {
BigInteger cached = cachedCount;
if(cached == null) {
cachedCount = cached = getCountImpl();
}
return cached;
}
protected BigInteger getCountImpl() {
return AddressDivisionSeries.super.getCount();
}
@Override
public BigInteger getPrefixCount() {
BigInteger cached = cachedPrefixCount;
if(cached == null) {
Integer prefixLength = getPrefixLength();
if(prefixLength == null || prefixLength >= getBitCount()) {
cachedPrefixCount = cached = getCount();
} else {
cachedPrefixCount = cached = getPrefixCountImpl();
}
}
return cached;
}
protected BigInteger getPrefixCountImpl() {
return AddressDivisionSeries.super.getPrefixCount();
}
/**
* @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)
*/
@Override
public boolean isMultiple() {
Boolean result = isMultiple;
if(result == null) {
for(int i = getDivisionCount() - 1; i >= 0; i--) {//go in reverse order, with prefixes multiple more likely to show up in last segment
AddressDivisionBase seg = getDivision(i);
if(seg.isMultiple()) {
return isMultiple = Boolean.TRUE;
}
}
return isMultiple = Boolean.FALSE;
}
return result;
}
@Override
public boolean isSequential() {
return !isMultiple() || AddressDivisionSeries.super.isSequential();
}
protected static int adjustHashCode(int currentHash, long lowerValue, long upperValue) {
return AddressDivisionBase.adjustHashCode(currentHash, lowerValue, upperValue);
}
@Override
public int hashCode() {//designed so that this hashcode matches the same in AddressDivision if values are long-sized
int res = hashCode;
if(res == 0) {
res = 1;
int count = getDivisionCount();
for(int i = 0; i < count; i++) {
AddressDivisionBase combo = getDivision(i);
BigInteger lower = combo.getValue(), upper = combo.getUpperValue();
int longBits = Long.SIZE;
do {
long low = lower.longValue();
long up = upper.longValue();
lower = lower.shiftRight(longBits);
upper = upper.shiftRight(longBits);
res = adjustHashCode(res, low, up);
} while(upper.signum() != 0);
}
hashCode = res;
}
return res;
}
protected boolean isSameGrouping(AddressDivisionGroupingBase other) {
int count = getDivisionCount();
if(count != other.getDivisionCount()) {
return false;
} else for(int i = 0; i < count; i++) {
AddressDivisionBase one = getDivision(i);
AddressDivisionBase two = other.getDivision(i);
if(!one.equals(two)) {//this checks the division types and also the bit counts
return false;
}
}
return true;
}
/**
* Two groupings are equal if:
* - they match type/version (ipv4, ipv6, mac, or a specific grouping class)
* - they match division counts
* - each division matches bit counts
* - each division matches their specific grouping class
* - each division matches values
*
* Prefix lengths, for those groupings and/or divisionsS that have them, are ignored.
*/
@Override
public boolean equals(Object o) {
if(o == this) {
return true;
}
if(o instanceof AddressDivisionGroupingBase) {
AddressDivisionGroupingBase other = (AddressDivisionGroupingBase) o;
// we call isSameGrouping on the other object to defer to subclasses
// in particlar, if the other is IPv4/6/MAC/AddressSection, then we call the overridden isSameGrouping
// in those classes which check for IPv4/6/MAC
// Equality and containment consider address versions and types.
// However, the other grouping classes, the division grouping classes:
// -do not support containment
// -support equality across types, similar to java.util.Collections equality with lists, sets, and maps
return other.isSameGrouping(this);
}
return false;
}
protected AddressDivisionBase[] getDivisionsInternal() {
return divisions;
}
@Override
public String toString() {
return Arrays.asList(getDivisionsInternal()).toString();
}
@Override
public String[] getDivisionStrings() {
String result[] = new String[getDivisionCount()];
Arrays.setAll(result, i -> getDivision(i).getWildcardString());
return result;
}
@Override
public boolean isZero() {
int divCount = getDivisionCount();
for(int i = 0; i < divCount; i++) {
if(!getDivision(i).isZero()) {
return false;
}
}
return true;
}
@Override
public boolean includesZero() {
int divCount = getDivisionCount();
for(int i = 0; i < divCount; i++) {
if(!getDivision(i).includesZero()) {
return false;
}
}
return true;
}
@Override
public boolean isMax() {
int divCount = getDivisionCount();
for(int i = 0; i < divCount; i++) {
if(!getDivision(i).isMax()) {
return false;
}
}
return true;
}
@Override
public boolean includesMax() {
int divCount = getDivisionCount();
for(int i = 0; i < divCount; i++) {
if(!getDivision(i).includesMax()) {
return false;
}
}
return true;
}
@Override
public boolean isFullRange() {
int divCount = getDivisionCount();
for(int i = 0; i < divCount; i++) {
AddressDivisionBase div = getDivision(i);
if(!div.isFullRange()) {
return false;
}
}
return true;
}
protected static void checkSubnet(AddressDivisionSeries series, int prefixLength) throws PrefixLenException {
if(prefixLength < 0 || prefixLength > series.getBitCount()) {
throw new PrefixLenException(series, prefixLength);
}
}
@Override
public boolean isSinglePrefixBlock() {//Note for any given prefix length you can compare with getPrefixLengthForSingleBlock
return isPrefixed() && containsSinglePrefixBlock(getPrefixLength());
}
@Override
public boolean isPrefixBlock() { //Note for any given prefix length you can compare with getMinPrefixLengthForBlock
return isPrefixed() && containsPrefixBlock(getPrefixLength());
}
protected static boolean containsPrefixBlock(IPAddressDivisionSeries series, int prefixLength) {
checkSubnet(series, prefixLength);
boolean isAllSubnets = series.getNetwork().getPrefixConfiguration().allPrefixedAddressesAreSubnets();
if(isAllSubnets && series.isPrefixed() && series.getNetworkPrefixLength() <= prefixLength) {
return true;
}
int prevBitCount = 0;
int divCount = series.getDivisionCount();
for(int i = 0; i < divCount; i++) {
IPAddressGenericDivision div = series.getDivision(i);
int bitCount = div.getBitCount();
int totalBitCount = bitCount + prevBitCount;
if(prefixLength < totalBitCount) {
int divPrefixLen = Math.max(0, prefixLength - prevBitCount);
if(!div.containsPrefixBlock(divPrefixLen)) {
return false;
}
if(isAllSubnets && div.isPrefixed()) {
return true;
}
for(++i; i < divCount; i++) {
div = series.getDivision(i);
if(!div.isFullRange()) {
return false;
}
if(isAllSubnets && div.isPrefixed()) {
return true;
}
}
return true;
}
prevBitCount = totalBitCount;
}
return true;
}
protected static boolean containsSinglePrefixBlock(IPAddressDivisionSeries series, int prefixLength) {
checkSubnet(series, prefixLength);
boolean isAllSubnets = series.getNetwork().getPrefixConfiguration().allPrefixedAddressesAreSubnets();
if(isAllSubnets && series.isPrefixed() && series.getNetworkPrefixLength() < prefixLength) {
return false;
}
int prevBitCount = 0;
int divCount = series.getDivisionCount();
for(int i = 0; i < divCount; i++) {
IPAddressGenericDivision div = series.getDivision(i);
int bitCount = div.getBitCount();
int totalBitCount = bitCount + prevBitCount;
if(prefixLength >= totalBitCount) {
if(div.isMultiple()) {
return false;
}
} else {
int divPrefixLen = Math.max(0, prefixLength - prevBitCount);
if(!div.containsSinglePrefixBlock(divPrefixLen)) {
return false;
}
if(isAllSubnets && div.isPrefixed()) {
return true;
}
for(++i; i < divCount; i++) {
div = series.getDivision(i);
if(!div.isFullRange()) {
return false;
}
if(isAllSubnets && div.isPrefixed()) {
return true;
}
}
return true;
}
prevBitCount = totalBitCount;
}
return true;
}
@FunctionalInterface
protected static interface IteratorProvider {
Iterator apply(boolean isLowestRange, boolean isHighestRange, S iteratedAddressItem);
}
protected static class AddressItemRangeSpliterator
extends AddressItemSpliteratorBase implements SplitterSink {
private S forIteration;
private Iterator iterator;
private S split1, split2; // To be assigned by splitter when splitting
protected final IteratorProvider iteratorProvider;
private boolean isLowest;
private final boolean isHighest;
private Function sizer; // Can be null: when null, we use longSizer
private Predicate downSizer;
private final ToLongFunction longSizer; // To be used only when sizer is null.
private long longSize;
private BigInteger bigSize;
final Predicate> splitter;
protected AddressItemRangeSpliterator(
S forIteration,
Predicate> splitter,
IteratorProvider iteratorProvider,
Function sizer /* can be null */,
Predicate downSizer,
ToLongFunction longSizer /* not to be used if sizer not null */) {
this(forIteration, splitter, iteratorProvider, true, true, sizer, downSizer, longSizer);
updateSizers();
}
protected AddressItemRangeSpliterator(
S forIteration,
Predicate> splitter,
IteratorProvider iteratorProvider,
boolean isLowest,
boolean isHighest,
Function sizer /* can be null */,
Predicate downSizer,
ToLongFunction longSizer /* not to be used if sizer not null */) {
this.forIteration = forIteration;
this.iteratorProvider = iteratorProvider;
this.isLowest = isLowest;
this.isHighest = isHighest;
this.longSizer = longSizer;
this.sizer = sizer;
this.downSizer = downSizer;
this.splitter = splitter;
updateSizers();
}
void updateSizers() {
if(sizer != null) {
isBig = downSizer == null || !downSizer.test(forIteration);
if(!isBig) {
sizer = null;
downSizer = null;
}
} else {
isBig = false;
}
longSize = -1;
bigSize = null;
}
private long originalLongSize() {
long size = longSize;
if(size < 0) {
longSize = size = longSizer.applyAsLong(forIteration);
}
return size;
}
private long currentLongSize() {
return originalLongSize() - iteratedCountL;
}
@Override
public long estimateSize() {
if(isBig) {
// if we have iterated a lot, bring us below LONG_MAX, we can give a better estimate
if(currentBigSize().compareTo(LONG_MAX) <= 0) {
return currentBigSize().longValue();
}
return Long.MAX_VALUE;
}
return currentLongSize();
}
private BigInteger originalBigSize() {
BigInteger size = bigSize;
if(bigSize == null) {
bigSize = size = sizer.apply(forIteration);
}
return size;
}
private BigInteger currentBigSize() {
return originalBigSize().subtract(iteratedCountB);
}
@Override
public BigInteger getSize() {
if(isBig) {
return currentBigSize().subtract(BigInteger.valueOf(iteratedCountI));
}
return BigInteger.valueOf(currentLongSize());
}
@Override
public S getAddressItem() {
return forIteration;
}
@Override
public int characteristics() {
if(isBig) {
return CONCURRENT | NONNULL | SORTED | ORDERED | DISTINCT;
}
return super.characteristics();
}
private Iterator provideIterator() {
if(iterator == null) {
iterator = iteratorProvider.apply(isLowest, isHighest, forIteration);
}
return iterator;
}
@Override
public boolean tryAdvance(Consumer super T> action) {
if(inForEach) {
return false;
}
if(isBig ? iteratedCountB.signum() <= 0 || iteratedCountB.compareTo(originalBigSize()) < 0 : iteratedCountL < originalLongSize()) {
return tryAdvance(provideIterator(), action);
}
return false;
}
@Override
public void forEachRemaining(Consumer super T> action) {
if(inForEach) {
return;
}
inForEach = true;
try {
if(isBig) {
forEachRemaining(provideIterator(), action, originalBigSize());
} else {
forEachRemaining(provideIterator(), action, originalLongSize());
}
return;
} finally {
inForEach = false;
}
}
protected boolean canSplit() {
// we can split if no forEachRemaining or tryAdvance in progress, and we are big enough to split into two
if(inForEach) {
return false;
}
// we can split if we are big enough to split into two
return isBig ? iteratedCountB.compareTo(originalBigSize().shiftRight(1)) < 0 :
iteratedCountL < (originalLongSize() >> 1);
}
protected boolean split() {
return splitter.test(this);
}
protected AddressItemRangeSpliterator createSpliterator(
S split,
boolean isLowest,
Function sizer,
Predicate downSizer,
ToLongFunction longSizer) {
return new AddressItemRangeSpliterator(split, splitter, iteratorProvider, isLowest, false, sizer, downSizer, longSizer);
}
@Override
public AddressItemRangeSpliterator trySplit() {
if(!canSplit() || !split()) {
return null;
}
boolean hasIterated = isBig ? iteratedCountB.signum() > 0 : iteratedCountL > 0;
BigInteger splitSizeBig = null;
long splitSize = -1;
// check that we haven't iterated too far for the split
if(hasIterated) {
if(isBig) {
splitSizeBig = sizer.apply(split1);
if(iteratedCountB.compareTo(splitSizeBig) >= 0) {
return null;
}
} else {
splitSize = longSizer.applyAsLong(split1);
if(iteratedCountL >= splitSize) {
return null;
}
}
}
AddressItemRangeSpliterator splitOff = createSpliterator(split1, isLowest, sizer, downSizer, longSizer);
if(hasIterated) {
if(isBig) {
if(splitOff.isBig) {
splitOff.iteratedCountB = iteratedCountB;
} else {
splitOff.iteratedCountL = iteratedCountB.longValue();
}
iteratedCountB = BigInteger.ZERO;
} else {
splitOff.iteratedCountL = iteratedCountL;
iteratedCountL = 0;
}
splitOff.iterator = iterator;
iterator = null;
splitOff.bigSize = splitSizeBig;
splitOff.longSize = splitSize;
}
forIteration = split2;
isLowest = false;
updateSizers();
return splitOff;
}
@Override
public void setSplitValues(S left, S right) {
split1 = left;
split2 = right;
}
}
protected static interface SplitterSink {
void setSplitValues(S left, S right);
S getAddressItem();
};
protected static AddressComponentRangeSpliterator createItemSpliterator(
S forIteration,
Predicate> splitter,
IteratorProvider iteratorProvider,
Function sizer /* can be null */,
Predicate downSizer,
ToLongFunction longSizer /* not to be used if sizer not null */) {
return new AddressItemRangeSpliterator(forIteration, splitter, iteratorProvider, sizer, downSizer, longSizer);
}
protected static AddressComponentSpliterator createSeriesSpliterator(
T forIteration,
Predicate> splitter,
IteratorProvider iteratorProvider,
Function sizer /* can be null */,
Predicate downSizer,
ToLongFunction longSizer /* not to be used if sizer not null */) {
return new AddressSeriesSpliterator(forIteration, splitter, iteratorProvider, sizer, downSizer, longSizer);
}
protected static class AddressStringParams implements AddressDivisionWriter, AddressSegmentParams, Cloneable {
public static final Wildcards DEFAULT_WILDCARDS = new Wildcards();
private Wildcards wildcards = DEFAULT_WILDCARDS;
protected boolean expandSegments; //whether to expand 1 to 001 for IPv4 or 0001 for IPv6
private String segmentStrPrefix = ""; //eg for inet_aton style there is 0x for hex, 0 for octal
private int radix;
//the segment separator and in the case of split digits, the digit separator
protected Character separator;
private boolean uppercase; //whether to print A or a
//print the segments in reverse, and in the case of splitDigits, print the digits in reverse as well
private boolean reverse;
//in each segment, split the digits with the separator, so that 123.456.1.1 becomes 1.2.3.4.5.6.1.1
private boolean splitDigits;
private String addressLabel = "";
private char zoneSeparator;
public AddressStringParams(int radix, Character separator, boolean uppercase) {
this(radix, separator, uppercase, (char) 0);
}
public AddressStringParams(int radix, Character separator, boolean uppercase, char zoneSeparator) {
if(radix < AddressDivisionBase.MIN_RADIX || radix > AddressDivisionBase.MAX_RADIX) {
throw new IllegalArgumentException();
}
this.radix = radix;
this.separator = separator;
this.uppercase = uppercase;
this.zoneSeparator = zoneSeparator;
}
public void setZoneSeparator(char zoneSeparator) {
this.zoneSeparator = zoneSeparator;
}
public String getAddressLabel() {
return addressLabel;
}
public void setAddressLabel(String str) {
this.addressLabel = str;
}
public Character getSeparator() {
return separator;
}
public void setSeparator(Character separator) {
this.separator = separator;
}
@Override
public Wildcards getWildcards() {
return wildcards;
}
public void setWildcards(Wildcards wc) {
wildcards = wc;
}
@Override
public boolean preferWildcards() {
return true;
}
//returns -1 to expand
@Override
public int getLeadingZeros(int segmentIndex) {
if(expandSegments) {
return -1;
}
return 0;
}
@Override
public String getSegmentStrPrefix() {
return segmentStrPrefix;
}
public void setSegmentStrPrefix(String segmentStrPrefix) {
if(segmentStrPrefix == null) {
throw new NullPointerException();
}
this.segmentStrPrefix = segmentStrPrefix;
}
@Override
public int getRadix() {
return radix;
}
public void setRadix(int radix) {
this.radix = radix;
}
public void setUppercase(boolean uppercase) {
this.uppercase = uppercase;
}
@Override
public boolean isUppercase() {
return uppercase;
}
public void setSplitDigits(boolean split) {
this.splitDigits = split;
}
@Override
public boolean isSplitDigits() {
return splitDigits;
}
@Override
public Character getSplitDigitSeparator() {
return separator;
}
@Override
public boolean isReverseSplitDigits() {
return reverse;
}
public void setReverse(boolean rev) {
this.reverse = rev;
}
public boolean isReverse() {
return reverse;
}
public void expandSegments(boolean expand) {
expandSegments = expand;
}
public StringBuilder appendLabel(StringBuilder builder) {
String str = getAddressLabel();
if(str != null && str.length() > 0) {
builder.append(str);
}
return builder;
}
public int getAddressLabelLength() {
String str = getAddressLabel();
if(str != null) {
return str.length();
}
return 0;
}
public int getSegmentsStringLength(T part) {
int count = 0;
if(part.getDivisionCount() != 0) {
int divCount = part.getDivisionCount();
for(int i = 0; i < divCount; i++) {
count += appendSegment(i, null, part);
}
Character separator = getSeparator();
if(separator != null) {
count += divCount - 1;
}
}
return count;
}
public StringBuilder appendSegments(StringBuilder builder, T part) {
int count = part.getDivisionCount();
if(count != 0) {
boolean reverse = isReverse();
int i = 0;
Character separator = getSeparator();
while(true) {
int segIndex = reverse ? (count - i - 1) : i;
appendSegment(segIndex, builder, part);
if(++i == count) {
break;
}
if(separator != null) {
builder.append(separator);
}
}
}
return builder;
}
public int appendSingleDivision(AddressStringDivision seg, StringBuilder builder) {
if(builder == null) {
return getAddressLabelLength() + seg.getStandardString(0, this, null);
}
appendLabel(builder);
seg.getStandardString(0, this, builder);
return 0;
}
protected int appendSegment(int segmentIndex, StringBuilder builder, T part) {
AddressStringDivision seg = part.getDivision(segmentIndex);
return seg.getStandardString(segmentIndex, this, builder);
}
public int getZoneLength(CharSequence zone) {
if(zone != null && zone.length() > 0) {
return zone.length() + 1; /* zone separator is one char */
}
return 0;
}
public int getStringLength(T addr, CharSequence zone) {
int result = getStringLength(addr);
if(zone != null) {
result += getZoneLength(zone);
}
return result;
}
public int getStringLength(T addr) {
return getAddressLabelLength() + getSegmentsStringLength(addr);
}
public StringBuilder appendZone(StringBuilder builder, CharSequence zone) {
if(zone != null && zone.length() > 0) {
builder.append(zoneSeparator).append(zone);
}
return builder;
}
public StringBuilder append(StringBuilder builder, T addr, CharSequence zone) {
return appendZone(appendSegments(appendLabel(builder), addr), zone);
}
public StringBuilder append(StringBuilder builder, T addr) {
return append(builder, addr, null);
}
@Override
public int getDivisionStringLength(AddressStringDivision seg) {
return appendSingleDivision(seg, null);
}
@Override
public StringBuilder appendDivision(StringBuilder builder, AddressStringDivision seg) {
appendSingleDivision(seg, builder);
return builder;
}
public String toString(T addr, CharSequence zone) {
int length = getStringLength(addr, zone);
StringBuilder builder = new StringBuilder(length);
append(builder, addr, zone);
checkLengths(length, builder);
return builder.toString();
}
public String toString(T addr) {
return toString(addr, null);
}
public static void checkLengths(int length, StringBuilder builder) {
//Note: re-enable this when doing development
// boolean calcMatch = length == builder.length();
// boolean capMatch = length == builder.capacity();
// if(!calcMatch || !capMatch) {
// throw new IllegalStateException("length is " + builder.length() + ", capacity is " + builder.capacity() + ", expected length is " + length);
// }
}
public static AddressStringParams toParams(StringOptions opts) {
//since the params here are not dependent on the section, we could cache the params in the options
//this is not true on the IPv6 side where compression settings change based on the section
@SuppressWarnings("unchecked")
AddressStringParams result = (AddressStringParams) getCachedParams(opts);
if(result == null) {
result = new AddressStringParams(opts.base, opts.separator, opts.uppercase);
result.expandSegments(opts.expandSegments);
result.setWildcards(opts.wildcards);
result.setSegmentStrPrefix(opts.segmentStrPrefix);
result.setAddressLabel(opts.addrLabel);
result.setReverse(opts.reverse);
result.setSplitDigits(opts.splitDigits);
setCachedParams(opts, result);
}
return result;
}
@Override
public AddressStringParams clone() {
try {
@SuppressWarnings("unchecked")
AddressStringParams parms = (AddressStringParams) super.clone();
return parms;
} catch(CloneNotSupportedException e) {
return null;
}
}
}
/**
* Each StringParams has settings to write exactly one type of IP address part string.
*
* @author sfoley
*/
protected static class IPAddressStringParams extends AddressStringParams implements IPAddressStringWriter {
public static final WildcardOption DEFAULT_WILDCARD_OPTION = WildcardOption.NETWORK_ONLY;
protected static final int EXTRA_SPACE = 16;
private WildcardOption wildcardOption = DEFAULT_WILDCARD_OPTION;
private int expandSegment[]; //the same as expandSegments but for each segment
private String addressSuffix = "";
public IPAddressStringParams(int radix, Character separator, boolean uppercase) {
this(radix, separator, uppercase, (char) 0);
}
public IPAddressStringParams(int radix, Character separator, boolean uppercase, char zoneSeparator) {
super(radix, separator, uppercase, zoneSeparator);
}
public String getAddressSuffix() {
return addressSuffix;
}
public void setAddressSuffix(String suffix) {
this.addressSuffix = suffix;
}
@Override
public boolean preferWildcards() {
return wildcardOption == WildcardOption.ALL;
}
public void setWildcardOption(WildcardOption option) {
wildcardOption = option;
}
public int getExpandedSegmentLength(int segmentIndex) {
if(expandSegment == null || expandSegment.length <= segmentIndex) {
return 0;
}
return expandSegment[segmentIndex];
}
public void expandSegment(int index, int expansionLength, int segmentCount) {
if(expandSegment == null) {
expandSegment = new int[segmentCount];
}
expandSegment[index] = expansionLength;
}
@Override
public char getTrailingSegmentSeparator() {
return separator;
}
public StringBuilder appendSuffix(StringBuilder builder) {
String suffix = getAddressSuffix();
if(suffix != null) {
builder.append(suffix);
}
return builder;
}
public int getAddressSuffixLength() {
String suffix = getAddressSuffix();
if(suffix != null) {
return suffix.length();
}
return 0;
}
//returns -1 for MAX, or 0, 1, 2, 3 to indicate the string prefix length
@Override
public int getLeadingZeros(int segmentIndex) {
if(expandSegments) {
return -1;
} else if(expandSegment != null && expandSegment.length > segmentIndex) {
return expandSegment[segmentIndex];
}
return 0;
}
@Override
public IPAddressStringParams clone() {
IPAddressStringParams parms = (IPAddressStringParams) super.clone();
if(expandSegment != null) {
parms.expandSegment = expandSegment.clone();
}
return parms;
}
@Override
public int getTrailingSeparatorCount(T addr) {
int count = addr.getDivisionCount();
if(count > 0) {
return count - 1;
}
return 0;
}
public static int getPrefixIndicatorStringLength(IPAddressStringDivisionSeries addr) {
if(addr.isPrefixed()) {
int value = addr.getPrefixLength();
if(value < 10) {
return 2;
} else if(value < 100) {
return 3;
}
return 4;
}
return 0;
}
@Override
public int getStringLength(T addr) {
int count = getSegmentsStringLength(addr);
if(!isReverse() && !preferWildcards()) {
count += getPrefixIndicatorStringLength(addr);
}
return count + getAddressSuffixLength() + getAddressLabelLength();
}
public void appendPrefixIndicator(StringBuilder builder, IPAddressStringDivisionSeries addr) {
if(addr.isPrefixed()) {
builder.append(IPAddress.PREFIX_LEN_SEPARATOR).append(addr.getPrefixLength());
}
}
@Override
public StringBuilder append(StringBuilder builder, T addr, CharSequence zone) {
/*
* Our order is label, then segments, then zone, then suffix, then prefix length.
* This is documented in more detail in IPv6AddressSection for the IPv6-only case.
*/
appendSuffix(appendZone(appendSegments(appendLabel(builder), addr), zone));
if(!isReverse() && !preferWildcards()) {
appendPrefixIndicator(builder, addr);
}
return builder;
}
@Override
protected int appendSegment(int segmentIndex, StringBuilder builder, T part) {
IPAddressStringDivision seg = part.getDivision(segmentIndex);
PrefixConfiguration config = part.getNetwork().getPrefixConfiguration();
//consider all the cases in which we need not account for prefix length
Integer prefix;
if(config.prefixedSubnetsAreExplicit() || preferWildcards()
|| (prefix = seg.getDivisionPrefixLength()) == null || prefix >= seg.getBitCount()
|| (config.zeroHostsAreSubnets() && !part.isPrefixBlock())
|| isSplitDigits()) {
return seg.getStandardString(segmentIndex, this, builder);
}
//prefix length will have an impact on the string - either we need not print the range at all
//because it is equivalent to the prefix length, or we need to adjust the upper value of the
//range so that the host is zero when printing the string
if(seg.isSinglePrefixBlock()) {
return seg.getLowerStandardString(segmentIndex, this, builder);
}
return seg.getPrefixAdjustedRangeString(segmentIndex, this, builder);
}
}
protected static class StringOptionsBase {
//use this field if the options to params conversion is not dependent on the address part so it can be reused
AddressDivisionWriter cachedParams;
}
protected static AddressDivisionWriter getCachedParams(StringOptionsBase opts) {
return opts.cachedParams;
}
protected static void setCachedParams(StringOptionsBase opts, AddressDivisionWriter cachedParams) {
opts.cachedParams = cachedParams;
}
protected static AddressStringParams toIPParams(IPStringOptions opts) {
return AddressDivisionBase.toParams(opts);
}
}