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

inet.ipaddr.format.IPAddressDivisionGrouping Maven / Gradle / Ivy

There is a newer version: 5.5.1
Show newest version
/*
 * Copyright 2017 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.util.Arrays;

import inet.ipaddr.AddressNetwork;
import inet.ipaddr.AddressNetwork.PrefixConfiguration;
import inet.ipaddr.AddressValueException;
import inet.ipaddr.IPAddress;
import inet.ipaddr.IPAddressNetwork;
import inet.ipaddr.IPAddressSection.WildcardOptions.WildcardOption;
import inet.ipaddr.IPAddressSegment;
import inet.ipaddr.InconsistentPrefixException;
import inet.ipaddr.format.util.IPAddressStringWriter;
import inet.ipaddr.ipv6.IPv6Address;

/**
 * IPAddressDivisionGrouping objects consist of a series of IPAddressDivision objects, each division containing one or more segments.
 * 

* With the IPAddressSection subclass, each division is one segment (eg either groupings of 4 like 1.2.3.4 or groupings of 8 like 1:2:3:4:5:6:7:8). *

* For IPv6, a compressed segment still counts as one of the groupings, it is simply not printed as part of the text representation. *

* Alternative groupings include ipv4 groupings define by inet_aton (eg groupings of 1, 2, or 3 divisions like 1, 1.2, and 1.2.3) and the mixed ipv6/ipv4 representation of ipv6 addresses (eg a grouping of 10 divisions like a:b:c:d:e:f:1.2.3.4) *

* IPAddressDivisionGrouping objects are immutable. Some of the derived state is created upon demand and cached. This also makes them thread-safe. *

* IPAddressDivisionGrouping objects may be associated with a prefix length, in which case that number of bits in the upper-most * portion of the object represent a prefix, while the remaining bits assume all possible values. * * @author sfoley */ public class IPAddressDivisionGrouping extends AddressDivisionGrouping implements IPAddressStringDivisionSeries { private static final long serialVersionUID = 4L; private final IPAddressNetwork network; protected static final RangeCache ZEROS_CACHE = new RangeCache(); static { if(RangeCache.PRELOAD_CACHE) { ZEROS_CACHE.preloadCache(-1); } } /** * If the grouping is prefixed, then note that we allow both null:null:x:0:0 where is x is the division bit count and null:null:0:0:0 which essentially have the same overall prefix grouping prefix. * For further discussion of this, see {@link AddressDivisionGrouping#normalizePrefixBoundary(int, IPAddressSegment[], int, int, java.util.function.BiFunction)} * * @param divisions * @param network * @throws NullPointerException if network is null or a division is null */ public IPAddressDivisionGrouping(IPAddressDivision divisions[], IPAddressNetwork network) throws AddressValueException { super(divisions); if(network == null) { throw new NullPointerException(getMessage("ipaddress.error.nullNetwork")); } this.network = network; int totalPrefixBits = 0; for(int i = 0; i < divisions.length; i++) { IPAddressDivision division = divisions[i]; /** * Across an address prefixes are: * (null):...:(null):(1 to x):(0):...:(0) */ Integer divPrefix = division.getDivisionPrefixLength(); if(divPrefix != null) { cachedPrefixLength = totalPrefixBits + divPrefix; for(++i; i < divisions.length; i++) { division = divisions[i]; divPrefix = division.getDivisionPrefixLength(); if(divPrefix == null || divPrefix != 0) { throw new InconsistentPrefixException(divisions[i - 1], division, divPrefix); } } return; } totalPrefixBits += division.getBitCount(); } cachedPrefixLength = NO_PREFIX_LENGTH; } /** * @throws NullPointerException if getNetwork() returns null or a division is null * @param divisions * @param checkSegs */ protected IPAddressDivisionGrouping(IPAddressDivision divisions[], boolean checkSegs) { super(divisions, checkSegs); network = getNetwork();//getNetwork() must be overridden in subclasses if(network == null) { throw new NullPointerException(getMessage("ipaddress.error.nullNetwork")); } } @Override public IPAddressNetwork getNetwork() { return network; } @Override public IPAddressDivision getDivision(int index) { return (IPAddressDivision) super.getDivision(index); } @Override public int isMore(AddressDivisionSeries other) { if(!isMultiple()) { return other.isMultiple() ? -1 : 0; } if(!other.isMultiple()) { return 1; } if(isSinglePrefixBlock() && other.isSinglePrefixBlock()) { int bits = getBitCount() - getPrefixLength(); int otherBits = other.getBitCount() - other.getPrefixLength(); return bits - otherBits; } return getCount().compareTo(other.getCount()); } /** * @return whether this address represents a network prefix or the set of all addresses with the same network prefix */ @Override public boolean isPrefixed() { return getNetworkPrefixLength() != null; } @Override public Integer getPrefixLength() { return getNetworkPrefixLength(); } protected Integer calculatePrefix() { int result = 0; int count = getDivisionCount(); for(int i = 0; i < count; i++) { IPAddressDivision div = getDivision(i); Integer prefix = div.getDivisionPrefixLength(); if(prefix != null) { result += prefix; return result; } else { result += div.getBitCount(); } } return null; } public Integer getNetworkPrefixLength() { Integer ret = cachedPrefixLength; if(ret == null) { int count = getDivisionCount(); if(count > 0) { if(!getNetwork().getPrefixConfiguration().allPrefixedAddressesAreSubnets() || getDivision(count - 1).isPrefixed()) { Integer result = calculatePrefix(); if(result != null) { return cachedPrefixLength = result; } } } cachedPrefixLength = NO_PREFIX_LENGTH; return null; } if(ret == NO_PREFIX_LENGTH) { return null; } return ret; } /** * Returns whether this address section represents a subnet block of addresses associated its prefix length. * * Returns false if it has no prefix length, if it is a single address with a prefix length (ie not a subnet), or if it is a range of addresses that does not include * the entire subnet block for its prefix length. * * If {@link AddressNetwork#getPrefixConfiguration} is set to consider all prefixes as subnets, this returns true for any grouping with prefix length. * * @return */ @Override public boolean isPrefixBlock() { Integer networkPrefixLength = getNetworkPrefixLength(); if(networkPrefixLength == null) { return false; } if(getNetwork().getPrefixConfiguration().allPrefixedAddressesAreSubnets()) { return true; } return containsPrefixBlock(networkPrefixLength); } @Override public boolean containsPrefixBlock(int prefixLength) { checkSubnet(prefixLength); boolean isAllSubnets = getNetwork().getPrefixConfiguration().allPrefixedAddressesAreSubnets(); if(isAllSubnets && isPrefixed() && getNetworkPrefixLength() <= prefixLength) { return true; } int prevBitCount = 0; int divCount = getDivisionCount(); for(int i = 0; i < divCount; i++) { IPAddressDivision div = getDivision(i); int bitCount = div.getBitCount(); int totalBitCount = bitCount + prevBitCount; if(prefixLength < totalBitCount) { int divPrefixLen = Math.max(0, prefixLength - prevBitCount); if(!div.isPrefixBlock(divPrefixLen)) { return false; } if(isAllSubnets && div.isPrefixed()) { return true; } for(++i; i < divCount; i++) { div = getDivision(i); if(!div.isFullRange()) { return false; } if(isAllSubnets && div.isPrefixed()) { return true; } } return true; } prevBitCount = totalBitCount; } return true; } @Override public boolean containsSinglePrefixBlock(int prefixLength) { checkSubnet(prefixLength); boolean isAllSubnets = getNetwork().getPrefixConfiguration().allPrefixedAddressesAreSubnets(); if(isAllSubnets && isPrefixed() && getNetworkPrefixLength() < prefixLength) { return false; } int prevBitCount = 0; int divCount = getDivisionCount(); for(int i = 0; i < divCount; i++) { IPAddressDivision div = 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.isSinglePrefixBlock(divPrefixLen)) { return false; } if(isAllSubnets && div.isPrefixed()) { return true; } for(++i; i < divCount; i++) { div = getDivision(i); if(!div.isFullRange()) { return false; } if(isAllSubnets && div.isPrefixed()) { return true; } } return true; } prevBitCount = totalBitCount; } return true; } /** * Returns whether the division grouping range matches the block of values for its prefix length. * In other words, returns true if and only if it has a prefix length and it has just a single prefix. */ @Override public boolean isSinglePrefixBlock() { Integer networkPrefixLength = getNetworkPrefixLength(); if(networkPrefixLength == null) { return false; } return containsSinglePrefixBlock(networkPrefixLength); } @Override public Integer getPrefixLengthForSingleBlock() { if(getNetwork().getPrefixConfiguration().allPrefixedAddressesAreSubnets()) { int totalPrefix = 0; int divCount = getDivisionCount(); for(int i = 0; i < divCount; i++) { IPAddressDivision div = getDivision(i); int segPrefix = div.getMinPrefixLengthForBlock(); if(!div.isSinglePrefixBlock(segPrefix)) { return null; } if(div.isPrefixed()) { return totalPrefix + segPrefix; } if(segPrefix < div.getBitCount()) { //remaining segments must be full range or we return null for(i++; i < divCount; i++) { IPAddressDivision laterDiv = getDivision(i); if(!laterDiv.isFullRange()) { return null; } if(laterDiv.isPrefixed()) { break; } } return totalPrefix + segPrefix; } totalPrefix += segPrefix; } return totalPrefix; } return super.getPrefixLengthForSingleBlock(); } public boolean includesZeroHost() { Integer networkPrefixLength = getNetworkPrefixLength(); if(networkPrefixLength == null || networkPrefixLength >= getBitCount()) { return false; } if(getNetwork().getPrefixConfiguration().allPrefixedAddressesAreSubnets()) { return true; } int divCount = getDivisionCount(); for(int i = 0; i < divCount; i++) { IPAddressDivision div = getDivision(i); Integer segmentPrefixLength = div.getDivisionPrefixLength(); if(segmentPrefixLength != null) { long mask = ~(~0 << (div.getBitCount() - segmentPrefixLength)); if((mask & div.getLowerValue()) != 0) { return false; } for(++i; i < divCount; i++) { div = getDivision(i); if(!div.includesZero()) { return false; } } } } return true; } @Override public boolean equals(Object o) { if(o == this) { return true; } if(o instanceof IPAddressDivisionGrouping) { IPAddressDivisionGrouping other = (IPAddressDivisionGrouping) o; return other.isSameGrouping(this); //we call isSameGrouping on the other object to defer to subclasses } return false; } /** * @return the segments which are zero */ public RangeList getZeroSegments() { return getZeroSegments(false); } /** * @return the segments which are zero or whose prefix-based range includes 0 */ public RangeList getZeroRangeSegments() { if(isPrefixed()) { return getZeroSegments(true); } return getZeroSegments(); } protected static RangeList getNoZerosRange() { return RangeCache.NO_ZEROS; } protected static RangeList getSingleRange(int index, int len) { RangeCache cache = ZEROS_CACHE.addRange(index, -1, len); return cache.get(); } protected RangeList getZeroSegments(boolean includeRanges) { RangeCache cache = ZEROS_CACHE; int divisionCount = getDivisionCount(); boolean isFullRangeHost = !getNetwork().getPrefixConfiguration().prefixedSubnetsAreExplicit() && isPrefixBlock(); includeRanges &= isFullRangeHost; int currentIndex = -1, lastIndex = -1, currentCount = 0; for(int i = 0; i < divisionCount; i++) { IPAddressDivision division = getDivision(i); boolean isCompressible = division.isZero() || (includeRanges && division.isPrefixed() && division.isSinglePrefixBlock(0, division.getDivisionPrefixLength())); if(isCompressible) { if(++currentCount == 1) { currentIndex = i; } if(i == divisionCount - 1) { cache = cache.addRange(currentIndex, lastIndex, currentCount); lastIndex = currentIndex + currentCount; } } else if(currentCount > 0) { cache = cache.addRange(currentIndex, lastIndex, currentCount); lastIndex = currentIndex + currentCount; currentCount = 0; } } return cache.get(); } public static class Range { public final int index; public final int length; Range(int index, int length) { this.index = index; this.length = length; } @Override public String toString() { return "[" + index + ',' + (index + length) + ']'; } } public static class RangeList { final Range ranges[]; RangeList(Range ranges[]) { if(ranges == null) { throw new NullPointerException(); } this.ranges = ranges; } public int size() { return ranges.length; } public Range getRange(int index) { return ranges[index]; } @Override public String toString() { return Arrays.asList(ranges).toString(); } } /** * A cache of ZeroRange objects in a tree structure. * * Starting from the root of the tree, as you traverse an address grouping from left to right, * if you have another range located at offset x from the last one, and it has length y, * then you follow nextRange[x][y] in the tree. * * When you have no more ranges (and this no more tree nodes to follow), then you can use the field for the cached ZeroRanges object * which is associated with the path you've followed (which corresponds to the zero-ranges in the address). * * @author sfoley * */ private static class RangeCache { static boolean PRELOAD_CACHE; static final int MAX_DIVISION_COUNT = IPv6Address.SEGMENT_COUNT; static final RangeList NO_ZEROS = new RangeList(new Range[0]); RangeCache nextRange[][];//nextRange[x - 1][y - 1] indicates tree entry for cases where the next range is at offset x from the current one and has length y RangeCache parent;//the parent of this entry in the tree RangeList zeroRanges; Range range; RangeCache() { this(null, MAX_DIVISION_COUNT, null); zeroRanges = NO_ZEROS; } private RangeCache(RangeCache parent, int potentialZeroOffsets, Range range) { if(potentialZeroOffsets > 0) { nextRange = new RangeCache[potentialZeroOffsets][]; for(int i = 0; i < potentialZeroOffsets; i++) { nextRange[i] = new RangeCache[potentialZeroOffsets - i]; } } this.parent = parent; this.range = range; } private void get(Range ranges[], int rangesIndex) { ranges[--rangesIndex] = range; if(rangesIndex > 0) { parent.get(ranges, rangesIndex); } } public RangeList get() { RangeList result = zeroRanges; if(result == null) { int depth = 0; RangeCache up = parent; while(up != null) { depth++; up = up.parent; } Range ranges[] = new Range[depth]; if(depth > 0) { ranges[--depth] = range; if(depth > 0) { parent.get(ranges, depth); } } zeroRanges = result = new RangeList(ranges); } return result; } void preloadCache(int lastIndex) { if(nextRange != null) { for(int i = 0; i < nextRange.length; i++) { RangeCache next[] = nextRange[i]; for(int j = 0; j < next.length; j++) { Range newRange; if(lastIndex == -1) {//we are the root ZEROS_CACHE newRange = new Range(i + lastIndex + 1, j + 1); } else { newRange = ZEROS_CACHE.nextRange[i + lastIndex + 1][j].range; } int nextPotentialZeroIndex = i + lastIndex + j + 3; int remainingPotentialZeroOffsets = RangeCache.MAX_DIVISION_COUNT - nextPotentialZeroIndex; RangeCache newRangeCache = new RangeCache(this, remainingPotentialZeroOffsets, newRange); newRangeCache.get(); next[j] = newRangeCache; } } for(int i = 0; i < nextRange.length; i++) { RangeCache next[] = nextRange[i]; for(int j = 0; j < next.length; j++) { RangeCache nextCache = next[j]; Range nextRange = nextCache.range; nextCache.preloadCache(nextRange.index + nextRange.length); } } } } public RangeCache addRange(int currentIndex, int lastIndex, int currentCount) { int offset = currentIndex - lastIndex;//the offset from the end of the last zero-range, which must be at least 1 int cacheOffset = offset - 1;//since offset must be at least 1 we adjust by 1 int cacheCount = currentCount - 1;//since currentCount must be at least 1, we adjust by 1 RangeCache next = nextRange[cacheOffset][cacheCount]; if(next == null) { //we will never reach here when the cache is preloaded. synchronized(this) { next = nextRange[cacheOffset][cacheCount]; if(next == null) { int nextPotentialZeroIndex = lastIndex + 1;//we adjust by 1 the next potential index since at offset 0 we do not have a 0 int remainingPotentialZeroOffsets = RangeCache.MAX_DIVISION_COUNT - nextPotentialZeroIndex; Range newRange; if(this == ZEROS_CACHE) { newRange = new Range(currentIndex, currentCount); } else { RangeCache rootNext = ZEROS_CACHE.nextRange[currentIndex][currentCount - 1]; if(rootNext == null) { ZEROS_CACHE.nextRange[currentIndex][currentCount - 1] = new RangeCache(ZEROS_CACHE, RangeCache.MAX_DIVISION_COUNT, newRange = new Range(currentIndex, currentCount)); } else { newRange = rootNext.range; } } nextRange[cacheOffset][cacheCount] = next = new RangeCache(this, remainingPotentialZeroOffsets, newRange); } } } return next; } } /** * 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()) { return AddressDivision.toUnsignedStringLengthFast(addr.getPrefixLength(), 10) + 1; } 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); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy