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

inet.ipaddr.format.validate.AddressParseData 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.validate;

import java.io.Serializable;
import java.math.BigInteger;

import inet.ipaddr.AddressNetwork.AddressSegmentCreator;
import inet.ipaddr.IncompatibleAddressException;
import inet.ipaddr.HostIdentifierString;
import inet.ipaddr.IPAddress;
import inet.ipaddr.IPAddress.IPVersion;
import inet.ipaddr.IPAddressSection;
import inet.ipaddr.IPAddressSegment;
import inet.ipaddr.IPAddressStringParameters;
import inet.ipaddr.MACAddressString;
import inet.ipaddr.format.validate.ParsedMACAddress.MACAddressParseData.MACFormat;
import inet.ipaddr.ipv4.IPv4Address;
import inet.ipaddr.ipv4.IPv4AddressNetwork.IPv4AddressCreator;
import inet.ipaddr.ipv4.IPv4AddressSection;
import inet.ipaddr.ipv4.IPv4AddressSegment;
import inet.ipaddr.ipv6.IPv6Address;
import inet.ipaddr.ipv6.IPv6AddressNetwork.IPv6AddressCreator;
import inet.ipaddr.ipv6.IPv6AddressSection;
import inet.ipaddr.ipv6.IPv6AddressSegment;
import inet.ipaddr.mac.MACAddress;
import inet.ipaddr.mac.MACAddressNetwork.MACAddressCreator;
import inet.ipaddr.mac.MACAddressSection;
import inet.ipaddr.mac.MACAddressSegment;

class AddressParseData implements Serializable {
	
	private static final long serialVersionUID = 4L;

	//these are for the segment values
	public static final int LOWER_INDEX = 0, UPPER_INDEX = 1, EXTENDED_LOWER_INDEX = 2, EXTENDED_UPPER_INDEX = 3;
	
	//these are for the indices
	public static final int LOWER_RADIX_INDEX = 0, UPPER_RADIX_INDEX = 1,
			LOWER_STR_DIGITS_INDEX = 2, LOWER_STR_START_INDEX = 3, LOWER_STR_END_INDEX = 4,
			UPPER_STR_DIGITS_INDEX = 5, UPPER_STR_START_INDEX = 6, UPPER_STR_END_INDEX = 7;
	
	//these are for the flags
	public static final int WILDCARD_INDEX = 0, SINGLE_WILDCARD_INDEX = 1, STANDARD_STR_INDEX = 2, STANDARD_RANGE_STR_INDEX = 3;
	
	boolean flags[][];
	int indices[][];
	long values[][];
	
	int segmentCount;
	
	boolean anyWildcard;
	boolean isEmpty, isAll;
	boolean isSingleSegment;
	
	//these are indices into the original string used while parsing
	int consecutiveSepIndex = -1;
	int addressEndIndex;
	
	final CharSequence str;
	
	AddressParseData(CharSequence str) {
		this.str = str;
	}
	
	void initSegmentData(int segmentCapacity) {
		flags = new boolean[segmentCapacity][STANDARD_RANGE_STR_INDEX + 1];
		indices = new int[segmentCapacity][UPPER_STR_END_INDEX + 1];
		values = new long[segmentCapacity][EXTENDED_UPPER_INDEX + 1];
	}
	
	boolean isWildcard(int index) {
		return flags[index][WILDCARD_INDEX];
	}
	
	void reverseSegments() {
		int mid = segmentCount >>> 1;
		for(int i = 0, reverseIndex = segmentCount - 1; i < mid; i++, reverseIndex--) {
			boolean tmpb[] = flags[i];
			int tmpi[] = indices[i];
			long tmpl[] = values[i];
			flags[i] = flags[reverseIndex];
			indices[i] = indices[reverseIndex];
			values[i] = values[reverseIndex];
			flags[reverseIndex] = tmpb;
			indices[reverseIndex] = tmpi;
			values[reverseIndex] = tmpl;
		}
	}
	
	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		builder.append("address string: ").append(str).append('\n');
		if(addressEndIndex > 0 && addressEndIndex < str.length()) {
			builder.append("address end: ").append(str.subSequence(addressEndIndex, str.length())).append('\n');
		}
		builder.append("segment count: ").append(segmentCount).append('\n');
		if(segmentCount > 0) {
			for(int i = 0; i < segmentCount; i++) {
				builder.append("segment ").append(i).append(":\n");
				boolean flags[] = this.flags[i];
				boolean isWildcard = flags[WILDCARD_INDEX];
				if(isWildcard) {
					builder.append("\tis wildcard").append('\n');
				} else {
					long values[] = this.values[i];
					long lower = values[LOWER_INDEX];
					long upper = values[UPPER_INDEX];
					long extendedUpper = values[EXTENDED_UPPER_INDEX];
					long extendedLower = values[EXTENDED_LOWER_INDEX];
					BigInteger lowerResult;
					if(extendedLower != 0) {
						BigInteger extended = BigInteger.valueOf(extendedLower);
						BigInteger shiftMore = extended.shiftLeft(Long.SIZE);
						BigInteger notExtended = BigInteger.valueOf(lower);
						lowerResult = shiftMore.or(notExtended);
						builder.append("\tvalue: ").append(lowerResult).append('\n');
						builder.append("\tvalue in hex: ").append(lowerResult.toString(16)).append('\n');
					} else {
						builder.append("\tvalue: ").append(lower).append('\n');
						builder.append("\tvalue in hex: ").append(Long.toHexString(lower)).append('\n');
						lowerResult = null;
					}
					int indices[] = this.indices[i];
					builder.append("\tstring: ").append(str.subSequence(indices[LOWER_STR_START_INDEX], indices[LOWER_STR_END_INDEX])).append('\n');
					builder.append("\tradix: ").append(indices[LOWER_RADIX_INDEX]).append('\n');
					builder.append("\tis standard: ").append(flags[STANDARD_STR_INDEX]).append('\n');
					if(extendedUpper != 0) {
						BigInteger extended = BigInteger.valueOf(extendedUpper);
						BigInteger shiftMore = extended.shiftLeft(Long.SIZE);
						BigInteger notExtended = BigInteger.valueOf(upper);
						BigInteger result = shiftMore.or(notExtended);
						if(!result.equals(lowerResult)) {
							builder.append("\tupper value: ").append(result).append('\n');
							builder.append("\tupper value in hex: ").append(result.toString(16)).append('\n');
							builder.append("\tupper string: ").append(str.subSequence(indices[UPPER_STR_START_INDEX], indices[UPPER_STR_END_INDEX])).append('\n');
							builder.append("\tupper radix: ").append(indices[UPPER_RADIX_INDEX]).append('\n');
							builder.append("\tis standard range: ").append(flags[STANDARD_RANGE_STR_INDEX]).append('\n');
						}
					} else {
						if(upper != lower) {
							builder.append("\tupper value: ").append(upper).append('\n');
							builder.append("\tupper value in hex: ").append(Long.toHexString(upper)).append('\n');
							builder.append("\tupper string: ").append(str.subSequence(indices[UPPER_STR_START_INDEX], indices[UPPER_STR_END_INDEX])).append('\n');
							builder.append("\tupper radix: ").append(indices[UPPER_RADIX_INDEX]).append('\n');
							builder.append("\tis standard range: ").append(flags[STANDARD_RANGE_STR_INDEX]).append('\n');
						}
					}
					if(flags[SINGLE_WILDCARD_INDEX]) {
						builder.append("\thas single wildcard: ").append('\n');
					}
				}
			}
			builder.append("has a wildcard segment: ").append(anyWildcard).append('\n');
			if(consecutiveSepIndex >= 0) {
				builder.append("has compressed segment(s) at character ").append(consecutiveSepIndex + 1).append('\n');
			}
			if(isSingleSegment) {
				builder.append("is single segment").append('\n');
			}
		} else if (isEmpty) {
			builder.append("is empty").append('\n');
		} else if (isAll) {
			builder.append("is all addresses").append('\n');
		}
		return builder.toString();
	}
}

class ParsedMACAddress implements Serializable {

	private static final long serialVersionUID = 4L;
	
	static class MACAddressParseData implements Serializable {
		
		private static final long serialVersionUID = 4L;
		
		final AddressParseData addressParseData;
		
		static enum MACFormat {
			DASHED(MACAddress.DASH_SEGMENT_SEPARATOR),
			COLON_DELIMITED(MACAddress.COLON_SEGMENT_SEPARATOR),
			DOTTED(MACAddress.DOTTED_SEGMENT_SEPARATOR),
			SPACE_DELIMITED(MACAddress.SPACE_SEGMENT_SEPARATOR);
			
			private char separator;
			
			MACFormat(char separator) {
				this.separator = separator;
			}
			
			char getSeparator() {
				return separator;
			}
			
			@Override
			public String toString() {
				StringBuilder builder = new StringBuilder();
				builder.append("mac format:").append(super.toString()).append('\n');
				builder.append("segment separator:").append(separator).append('\n');
				return builder.toString();
			}
		};
		
		boolean isDoubleSegment;
		boolean isExtended;
		
		MACFormat format;
		
		MACAddressParseData(CharSequence str) {
			addressParseData = new AddressParseData(str);
		}
		
		void initSegmentData(int segmentCapacity) {
			addressParseData.initSegmentData(segmentCapacity);
		}
		
		boolean isWildcard(int index) {
			return addressParseData.isWildcard(index);
		}
		
		@Override
		public String toString() {
			StringBuilder builder = new StringBuilder();
			builder.append(addressParseData);
			if(isDoubleSegment) {
				builder.append("is double segment").append('\n');
			}
			builder.append("bit length:").append(isExtended ? 64 : 48).append('\n');
			if(format != null) {
				builder.append(format);
			}
			return builder.toString();
		}
	};
	
	private final String addressString;
	private final MACAddressString originator;
	private final MACAddressParseData parseData;
	
	ParsedMACAddress(
			MACAddressString from, 
			String addressString,
			MACAddressParseData parseData) {
		this.parseData = parseData;
		this.addressString = addressString;
		this.originator = from;
	}

	private MACAddressCreator getMACAddressCreator() {
		return originator.getValidationOptions().getNetwork().getAddressCreator();
	}

	MACAddress createAddress()  {
		ParsedAddressCreator creator = getMACAddressCreator();
		return creator.createAddressInternal(createSection(), originator);
	}

	private MACAddressSection createSection()  {
		AddressParseData addressParseData = parseData.addressParseData;
		int actualInitialSegmentCount = addressParseData.segmentCount;
		MACAddressCreator creator = getMACAddressCreator();
		MACFormat format = parseData.format;
		
		int finalSegmentCount, initialSegmentCount;
		if(format == null) {
			initialSegmentCount = finalSegmentCount = 
					parseData.isExtended ? MACAddress.EXTENDED_UNIQUE_IDENTIFIER_64_SEGMENT_COUNT : MACAddress.MEDIA_ACCESS_CONTROL_SEGMENT_COUNT;
		} else if(format == MACFormat.DOTTED) {
			initialSegmentCount = parseData.isExtended ? MACAddress.MEDIA_ACCESS_CONTROL_DOTTED_64_SEGMENT_COUNT : MACAddress.MEDIA_ACCESS_CONTROL_DOTTED_SEGMENT_COUNT;
			if(actualInitialSegmentCount <= MACAddress.MEDIA_ACCESS_CONTROL_DOTTED_SEGMENT_COUNT && !parseData.isExtended) {
				finalSegmentCount = MACAddress.MEDIA_ACCESS_CONTROL_SEGMENT_COUNT;
			} else {
				finalSegmentCount = MACAddress.EXTENDED_UNIQUE_IDENTIFIER_64_SEGMENT_COUNT;
			}
		} else {
			if(addressParseData.isSingleSegment || parseData.isDoubleSegment) {
				finalSegmentCount = parseData.isExtended ? MACAddress.EXTENDED_UNIQUE_IDENTIFIER_64_SEGMENT_COUNT : MACAddress.MEDIA_ACCESS_CONTROL_SEGMENT_COUNT;
			} else if(actualInitialSegmentCount <= MACAddress.MEDIA_ACCESS_CONTROL_SEGMENT_COUNT && !parseData.isExtended) {
				finalSegmentCount = MACAddress.MEDIA_ACCESS_CONTROL_SEGMENT_COUNT;
			} else {
				finalSegmentCount = MACAddress.EXTENDED_UNIQUE_IDENTIFIER_64_SEGMENT_COUNT;
			}
			initialSegmentCount = finalSegmentCount;
		}
		int missingCount = initialSegmentCount - actualInitialSegmentCount;
		boolean expandedSegments = (missingCount <= 0);
		MACAddressSegment segments[] = creator.createSegmentArray(finalSegmentCount);
		for(int i = 0, normalizedSegmentIndex = 0; i < actualInitialSegmentCount; i++) {
			long vals[] = addressParseData.values[i];
			boolean flags[] = addressParseData.flags[i];
			int indices[] = addressParseData.indices[i];
			long lower = vals[AddressParseData.LOWER_INDEX];
			long upper = vals[AddressParseData.UPPER_INDEX];
			if(format == MACFormat.DOTTED) {//aaa.bbb.ccc.ddd
				//aabb is becoming aa.bb
				int lowerHalfLower = (((int) lower) >>> 8);
				int lowerHalfUpper = (((int) upper) >>> 8);
				int adjustedLower2 = ((int) lower) & 0xff;
				int adjustedUpper2 = ((int) upper) & 0xff;
				if(lowerHalfLower != lowerHalfUpper && adjustedUpper2 - adjustedLower2 != 0xff) {
					throw new IncompatibleAddressException(addressString, "ipaddress.error.invalid.joined.ranges");
				}
				segments[normalizedSegmentIndex++] = createSegment(
						addressString,
						lowerHalfLower,
						lowerHalfUpper,
						null,
						null,
						creator);
				segments[normalizedSegmentIndex] = createSegment(
						addressString,
						adjustedLower2,
						adjustedUpper2,
						null,
						null,
						creator);
			} else {
				if(addressParseData.isSingleSegment || parseData.isDoubleSegment) {
					int count = (i == actualInitialSegmentCount - 1) ? missingCount : (MACAddress.ORGANIZATIONAL_UNIQUE_IDENTIFIER_SEGMENT_COUNT - 1);
					missingCount -= count;
					boolean isRange = (lower != upper);
					boolean previousAdjustedWasRange = false;
					while(count >= 0) { //add the missing segments
						int newLower, newUpper;
						boolean segFlags[] = flags;
						if(isRange) {
							int segmentMask = MACAddress.MAX_VALUE_PER_SEGMENT;
							int shift = count << 3;
							newLower = (int) (lower >>> shift) & segmentMask;
							newUpper = (int) (upper >>> shift) & segmentMask;
							if(previousAdjustedWasRange && newUpper - newLower != MACAddress.MAX_VALUE_PER_SEGMENT) {
								//any range extending into upper segments must have full range in lower segments
								//otherwise there is no way for us to represent the address
								//so we need to check whether the lower parts cover the full range
								//eg cannot represent 0.0.0x100-0x10f or 0.0.1-1ff, but can do 0.0.0x100-0x1ff or 0.0.0-1ff
								throw new IncompatibleAddressException(addressString, "ipaddress.error.invalid.joined.ranges");
							}
							previousAdjustedWasRange = newLower != newUpper;
							
							//we may be able to reuse our strings on the final segment
							//for previous segments, strings can be reused only when the value is 0, which we do not need to cache.  Any other value changes when shifted.  
							if(count == 0 && newLower == lower) {
								if(newUpper != upper) {
									segFlags[AddressParseData.STANDARD_RANGE_STR_INDEX] = false;
								}
							} else {
								segFlags = null;
							}
						} else {
							newLower = newUpper = (int) (lower >> (count << 3)) & MACAddress.MAX_VALUE_PER_SEGMENT;
							if(count != 0 || newLower != lower) {
								segFlags = null;
							}
						}
						segments[normalizedSegmentIndex] = createSegment(
							addressString,
							newLower,
							newUpper,
							segFlags,
							indices,
							creator);
						++normalizedSegmentIndex;
						count--;
					}
					//break;
					continue;
				} //end joined segments
				segments[normalizedSegmentIndex] = createSegment(
						addressString,
						(int) lower,
						(int) upper,
						flags,
						indices,
						creator);
			}
			if(!expandedSegments) {
				//check for any missing segments that we should account for here
				if(parseData.isWildcard(i)) {
					boolean expandSegments = true;
					for(int j = i + 1; j < actualInitialSegmentCount; j++) {
						if(parseData.isWildcard(j)) {//another wildcard further down
							expandSegments = false;
							break;
						}
					}
					if(expandSegments) {
						expandedSegments = true;
						int count = missingCount;
						while(count-- > 0) { //add the missing segments
							if(format == MACFormat.DOTTED) {
								MACAddressSegment seg = createSegment(
										addressString,
										0,
										MACAddress.MAX_VALUE_PER_SEGMENT,
										null,
										null,
										creator);
								segments[++normalizedSegmentIndex] = seg;
								segments[++normalizedSegmentIndex] = seg;
							} else {
								segments[++normalizedSegmentIndex] = createSegment(
									addressString,
									0,
									MACAddress.MAX_VALUE_PER_SEGMENT,
									null,
									null,
									creator);
							}
						}
					}
				}
			}
			normalizedSegmentIndex++;
		}
		ParsedAddressCreator addressCreator = creator;
		MACAddressSection result = addressCreator.createSectionInternal(segments);
		return result;
	}
		
	private static  S createSegment(
			String addressString,
			int val,
			int upperVal,
			boolean flags[],
			int indices[],
			ParsedAddressCreator creator) {
		if(val != upperVal) {
			return createRangeSegment(addressString, val, upperVal, flags, indices, creator);
		}
		S result;
		if(flags == null) {
			result = creator.createSegment(val, val, null);
		} else {
			result = creator.createSegmentInternal(
				val,
				null,//prefix length
				addressString,
				val,
				flags[AddressParseData.STANDARD_STR_INDEX],
				indices[AddressParseData.LOWER_STR_START_INDEX],
				indices[AddressParseData.LOWER_STR_END_INDEX]);
		}
		return result;
	}
	
	private static  S createRangeSegment(
			String addressString,
			int lower,
			int upper,
			boolean flags[],
			int indices[],
			ParsedAddressCreator creator) {
		S result;
		if(flags == null) {
			result = creator.createSegment(lower, upper, null);
		} else {
			result = creator.createSegmentInternal(
				lower,
				upper,
				null,
				addressString,
				lower,
				upper,
				flags[AddressParseData.STANDARD_STR_INDEX],
				flags[AddressParseData.STANDARD_RANGE_STR_INDEX],
				indices[AddressParseData.LOWER_STR_START_INDEX],
				indices[AddressParseData.LOWER_STR_END_INDEX],
				indices[AddressParseData.UPPER_STR_END_INDEX]);
		}
		return result;
	}
}

/**
 * The result from parsing a valid address string.  This can be converted into an {@link IPv4Address} or {@link IPv6Address} instance.
 * 
 * @author sfoley
 *
 */
class ParsedIPAddress implements Serializable {

	private static final long serialVersionUID = 4L;
	
	/**
	 * Stores the data from a parsed address.  This data can later be translated into {@link IPv4Address} or {@link IPv6Address} objects.
	 * @author sfoley
	 *
	 */
	static class IPAddressParseData implements Serializable {
	
		private static final long serialVersionUID = 4L;
		
		AddressParseData addressParseData;
		
		int qualifierIndex = -1;
		
		boolean isPrefixed, isZoned;
		
		IPVersion ipVersion;
		
		boolean is_inet_aton_joined;
		ParsedIPAddress mixedParsedAddress;
	
		boolean isBase85, isBase85Zoned;
		
		IPAddressParseData(CharSequence str) {
			addressParseData = new AddressParseData(str);
		}
		
		void initSegmentData(int segmentCapacity) {
			addressParseData.initSegmentData(segmentCapacity);
		}
		
		void clearQualifier() {
			qualifierIndex = -1;
			isBase85Zoned = isPrefixed = isZoned = false;
		}

		void reverseSegments() {
			if(mixedParsedAddress != null) {
				mixedParsedAddress.reverseSegments();
			}
			addressParseData.reverseSegments();
		}
		
		boolean isCompressed() {
			return addressParseData.consecutiveSepIndex >= 0;
		}
		
		boolean isCompressed(int index) {
			int inds[] = addressParseData.indices[index];
			int end = inds[AddressParseData.UPPER_STR_END_INDEX];
			int start = inds[AddressParseData.LOWER_STR_START_INDEX];
			return start == end;
		}
		
		boolean isWildcard(int index) {
			return addressParseData.isWildcard(index);
		}
		
		boolean isMixedIPv6() {
			return mixedParsedAddress != null;
		}
		
		@Override
		public String toString() {
			StringBuilder builder = new StringBuilder();
			builder.append(addressParseData);
			builder.append("ip version: ").append(ipVersion);
			if(ipVersion.isIPv6()) {
				if(isMixedIPv6()) {
					if(isZoned) {
						builder.append(", with zone ");
						printQualifier(builder);
					}
					if(isPrefixed) {
						builder.append(", with prefix length ");
						printQualifier(builder);
					}
					builder.append(", with IPv4 embedded address: ").append('\n').append(mixedParsedAddress.parseData);
				} else {
					if(isBase85) {
						builder.append(" base 85");
						if(isBase85Zoned) {
							builder.append(", with zone ");
							printQualifier(builder);
						}
					} else {
						if(isZoned) {
							builder.append(", with zone ");
							printQualifier(builder);
						}
					}
					if(isPrefixed) {
						builder.append(", with prefix length ");
						printQualifier(builder);
					}
					builder.append('\n');
				}
			} else if(ipVersion.isIPv4()) {
				if(isPrefixed) {
					builder.append(", with prefix length  ");
					printQualifier(builder);
				}
				if(is_inet_aton_joined) {
					builder.append(", with joined segments");
				}
				builder.append('\n');
			}
			return builder.toString();
		}

		private void printQualifier(StringBuilder builder) {
			if(qualifierIndex >= 0) {//zone, prefix, or port
				CharSequence str = addressParseData.str;
				builder.append(str.subSequence(qualifierIndex, str.length()));
			} else {
				builder.append("unknown");
			}
		}
	};

	static class CachedIPAddresses implements Serializable {
		
		private static final long serialVersionUID = 4L;
		
		//address is 1.2.0.0/16 and hostAddress is 1.2.3.4 for the string 1.2.3.4/16
		protected T address, hostAddress;
		
		CachedIPAddresses() {}

		public CachedIPAddresses(T address) {
			this(address, address);
		}
		
		public CachedIPAddresses(T address, T hostAddress) {
			this.address = address;
			this.hostAddress = hostAddress;
		}
		
		public T getAddress() {
			return address;
		}
		
		public T getHostAddress() {
			return hostAddress;
		}
	}
	
	abstract class IPAddresses extends CachedIPAddresses  {

		private static final long serialVersionUID = 4L;
		
		private final R section, hostSection;

		IPAddresses(R section, R hostSection) {
			this.section = section;
			this.hostSection = hostSection;
		}
		
		abstract ParsedAddressCreator getCreator();
		
		@Override
		public T getAddress() {
			if(address == null) {
				address = getCreator().createAddressInternal(section, qualifier.getZone(), originator);
			}
			return address;
		}
		
		@Override
		public T getHostAddress() {
			if(hostSection == null) {
				return getAddress();
			}
			if(hostAddress == null) {
				hostAddress = getCreator().createAddressInternal(hostSection, qualifier.getZone(), null);
			}
			return hostAddress;
		}
		
		R getSection() {
			return section;
		}
	}
	
	private final IPVersion ipVersion; //the version, either IPv4 or IPv6.
	private final ParsedHostIdentifierStringQualifier qualifier;
	private final CharSequence addressString;
	final IPAddressStringParameters options;
	private final HostIdentifierString originator;
	private final IPAddressParseData parseData;
	
	ParsedIPAddress(
			HostIdentifierString from, 
			CharSequence addressString,
			IPAddressStringParameters options,
			IPAddressParseData parseData,
			IPVersion ipVersion,
			ParsedHostIdentifierStringQualifier qualifier) {
		this.ipVersion = ipVersion;
		this.parseData = parseData;
		this.qualifier = qualifier;
		this.addressString = addressString;
		this.options = options;
		this.originator = from;
	}
	
	private IPv6AddressCreator getIPv6AddressCreator() {
		return options.getIPv6Parameters().getNetwork().getAddressCreator();
	}
	
	private IPv4AddressCreator getIPv4AddressCreator() {
		return options.getIPv4Parameters().getNetwork().getAddressCreator();
	}
	
	
	void reverseSegments() {
		parseData.reverseSegments();
	}

	IPVersion getIPVersion() {
		return ipVersion;
	}
	
	boolean isMixedIPv6() {
		return parseData.isMixedIPv6();
	}
	
	boolean isBase85IPv6() {
		return parseData.isBase85;
	}
	
	boolean isIPv6() {
		return parseData.ipVersion.isIPv6();
	}
	
	boolean isIPv4() {
		return parseData.ipVersion.isIPv4();
	}
	
	Integer getNetworkPrefixLength() {
		return qualifier.getNetworkPrefixLength();
	}
	
	boolean isPrefixed() {
		return getNetworkPrefixLength() != null;
	}
	
	IPAddresses createAddresses()  {
		IPVersion version = ipVersion;
		if(version == IPVersion.IPV4) {
			return createIPv4Addresses();
		} else if(version == IPVersion.IPV6) {
			return createIPv6Addresses();
		}
		return null;
	}
	
	private static  S[] allocateHostSegments(
			S segments[],
			S originalSegments[],
			AddressSegmentCreator creator,
			int segmentCount,
			int originalCount) {
		if(segments == null) {
			segments = creator.createSegmentArray(segmentCount);
			System.arraycopy(originalSegments,  0,  segments, 0, originalCount);
		}
		return segments;
	}
	
	@SuppressWarnings("serial")
	private IPAddresses createIPv4Addresses() {
		IPAddress mask = qualifier.getMask();
		if(mask != null && mask.getBlockMaskPrefixLength(true) != null) {
			mask = null;//we don't do any masking if the mask is a subnet mask, instead we just map it to the corresponding prefix length
		}
		boolean hasMask = mask != null;
		int segmentCount = parseData.addressParseData.segmentCount;
		IPv4AddressCreator creator = getIPv4AddressCreator();
		int ipv4SegmentCount = IPv4Address.SEGMENT_COUNT;
		int missingCount = ipv4SegmentCount - segmentCount;
		IPv4AddressSegment hostSegments[] = null;
		IPv4AddressSegment segments[] = creator.createSegmentArray(ipv4SegmentCount);
		boolean expandedSegments = (missingCount <= 0);
		int expandedStart, expandedEnd;
		expandedStart = expandedEnd = -1;
		for(int i = 0, normalizedSegmentIndex = 0; i < segmentCount; i++, normalizedSegmentIndex++) {
			long vals[] = parseData.addressParseData.values[i];
			boolean flags[] = parseData.addressParseData.flags[i];
			int indices[] = parseData.addressParseData.indices[i];
			long lower = vals[AddressParseData.LOWER_INDEX];
			long upper = vals[AddressParseData.UPPER_INDEX];
			
			//handle inet_aton style joined segments
			boolean isLastSegment = i == segmentCount - 1;
			if(!expandedSegments && isLastSegment && !parseData.isWildcard(i)) {
				expandedSegments = true;
				int count = missingCount;
				expandedStart = i;
				expandedEnd = i + count;
				while(count >= 0) { //add the missing segments
					Integer currentPrefix = getSegmentPrefixLength(normalizedSegmentIndex, IPv4Address.BITS_PER_SEGMENT, qualifier);
					int newLower, newUpper;
					boolean segFlags[] = flags;
					if(lower != upper) {
						int shift = IPv4Address.BITS_PER_SEGMENT * count;
						int segmentMask = IPv4Address.MAX_VALUE_PER_SEGMENT;
						newLower = (int) (lower >>> shift) & segmentMask;
						newUpper = (int) (upper >>> shift) & segmentMask;
						//we may be able to reuse our strings on the final segment
						//for previous segments, strings can be reused only when the value is 0, which we do not need to cache.  Any other value changes when shifted.  
						if(count == 0 && newLower == lower) {
							if(newUpper != upper) {
								segFlags[AddressParseData.STANDARD_RANGE_STR_INDEX] = false;
							}
						} else {
							segFlags = null;
						}
					} else {
						newLower = newUpper = (int) (lower >> (IPv4Address.BITS_PER_SEGMENT * count)) & IPv4Address.MAX_VALUE_PER_SEGMENT;
						if(count != 0 || newLower != lower) {
							segFlags = null;
						}
					}
					Integer segmentMask = hasMask ? mask.getSegment(normalizedSegmentIndex).getLowerSegmentValue() : null;
					if(segmentMask != null || currentPrefix != null) {
						hostSegments = allocateHostSegments(hostSegments, segments, creator, ipv4SegmentCount, normalizedSegmentIndex);
						hostSegments[normalizedSegmentIndex] = createSegment(addressString, IPVersion.IPV4, newLower, newUpper, flags, indices, null, null, creator);
					}
					segments[normalizedSegmentIndex] = createSegment(
						addressString,
						IPVersion.IPV4,
						newLower,
						newUpper,
						segFlags,
						indices,
						currentPrefix,
						segmentMask,
						creator);
					++normalizedSegmentIndex;
					count--;
				}
				break;
			} //end handle inet_aton joined segments
			Integer segmentMask = hasMask ? mask.getSegment(normalizedSegmentIndex).getLowerSegmentValue() : null;
			Integer segmentPrefixLength = getSegmentPrefixLength(normalizedSegmentIndex, IPv4Address.BITS_PER_SEGMENT, qualifier);
			if(segmentMask != null || segmentPrefixLength != null) {
				hostSegments = allocateHostSegments(hostSegments, segments, creator, ipv4SegmentCount, normalizedSegmentIndex);
				hostSegments[normalizedSegmentIndex] = createSegment(addressString, IPVersion.IPV4, (int) lower, (int) upper, flags, indices, null, null, creator);
			}
			segments[normalizedSegmentIndex] = createSegment(
					addressString,
					IPVersion.IPV4,
					(int) lower,
					(int) upper,
					flags,
					indices,
					segmentPrefixLength,
					segmentMask,
					creator);
			if(!expandedSegments &&
					//check for any missing segments that we should account for here
					parseData.isWildcard(i) && (!parseData.is_inet_aton_joined || isLastSegment)) {
				boolean expandSegments = true;
				for(int j = i + 1; j < segmentCount; j++) {
					if(parseData.isWildcard(j)) {//another wildcard further down
						expandSegments = false;
						break;
					}
				}
				if(expandSegments) {
					expandedSegments = true;
					int count = missingCount;
					while(count-- > 0) { //add the missing segments
						++normalizedSegmentIndex;
						segmentMask = hasMask ?  mask.getSegment(normalizedSegmentIndex).getLowerSegmentValue() : null;
						segmentPrefixLength = getSegmentPrefixLength(normalizedSegmentIndex, IPv4Address.BITS_PER_SEGMENT, qualifier);
						if(segmentMask != null || segmentPrefixLength != null) {
							hostSegments = allocateHostSegments(hostSegments, segments, creator, ipv4SegmentCount, normalizedSegmentIndex);
							hostSegments[normalizedSegmentIndex] = createSegment(addressString, IPVersion.IPV4, (int) lower, (int) upper, flags, indices, null, null, creator);
						}
						segments[normalizedSegmentIndex] = createSegment(
							addressString,
							IPVersion.IPV4,
							0,
							IPv4Address.MAX_VALUE_PER_SEGMENT,
							null,
							null,
							segmentPrefixLength,
							segmentMask,
							creator);
					}
				}
			}
		}
		ParsedAddressCreator addressCreator = creator;
		Integer prefLength = getPrefixLength(qualifier);
		IPv4AddressSection result = addressCreator.createPrefixedSectionInternal(segments, prefLength);
		IPv4AddressSection hostResult;
		if(hostSegments != null) {
			hostResult = addressCreator.createSectionInternal(hostSegments);
		} else {
			hostResult = null;
		}
		if(checkExpandedValues(result, expandedStart, expandedEnd)) {
			throw new IncompatibleAddressException(addressString, "ipaddress.error.invalid.joined.ranges");
		}
		if(checkExpandedValues(hostResult, expandedStart, expandedEnd)) {
			hostResult = null;
		}
		return new IPAddresses(result, hostResult) {
			@Override
			ParsedAddressCreator getCreator() {
				return getIPv4AddressCreator();
			}
		};
	}
	
	/*
	 * When expanding a set of segments into multiple, it is possible that the new segments do not accurately
	 * cover the same ranges of values.  This occurs when there is a range in the upper segments and the lower
	 * segments do not cover the full range (as is the case in the original unexpanded segment).
	 * 
	 * This does not include compressed 0 segments or compressed '*' segments, as neither can have the issue.
	 * 
	 * Returns true if the expansion was invalid.
	 * 
	 */
	private boolean checkExpandedValues(IPAddressSection section, int start, int end) {
		if(section != null && start < end) {
			IPAddressSegment seg = section.getSegment(start);
			boolean lastWasRange = seg.isMultiple();
			do {
				seg = section.getSegment(++start);
				if(lastWasRange) {
					if(!seg.isFullRange()) {
						return true;
					}
				} else {
					lastWasRange = seg.isMultiple();
				}
			} while(start < end);
		}
		return false;
	}
	
	@SuppressWarnings("serial")
	IPAddresses createIPv6Addresses()  {
		IPAddress mask = qualifier.getMask();
		if(mask != null && mask.getBlockMaskPrefixLength(true) != null) {
			mask = null;//we don't do any masking if the mask is a subnet mask, instead we just map it to the corresponding prefix length
		}
		boolean hasMask = mask != null;
		int segmentCount = parseData.addressParseData.segmentCount;
		IPv6AddressCreator creator = getIPv6AddressCreator();
		int ipv6SegmentCount = IPv6Address.SEGMENT_COUNT;
		IPv6AddressSegment hostSegments[] = null;
		IPv6AddressSegment segments[] = creator.createSegmentArray(ipv6SegmentCount);
		boolean mixed = isMixedIPv6();
		int normalizedSegmentIndex = 0;
		int missingSegmentCount = (mixed ? IPv6Address.MIXED_ORIGINAL_SEGMENT_COUNT : IPv6Address.SEGMENT_COUNT) - segmentCount;
		boolean expandedSegments = (missingSegmentCount <= 0);
		int expandedStart, expandedEnd;
		expandedStart = expandedEnd = -1;
		
		//get the segments for IPv6
		for(int i = 0; i < segmentCount; i++) {
			long vals[] = parseData.addressParseData.values[i];
			boolean flags[] = parseData.addressParseData.flags[i];
			int indices[] = parseData.addressParseData.indices[i];
			long lower = vals[AddressParseData.LOWER_INDEX];
			long upper = vals[AddressParseData.UPPER_INDEX];
			
			//handle joined segments
			if(!expandedSegments && i == segmentCount - 1 && !parseData.isWildcard(i)) {
				expandedSegments = true;
				int count = missingSegmentCount;
				long lowerHighBytes, upperHighBytes;
				boolean isRange;
				if(count >= 4) {
					lowerHighBytes = vals[AddressParseData.EXTENDED_LOWER_INDEX];//the high half of the lower value
					upperHighBytes = vals[AddressParseData.EXTENDED_UPPER_INDEX];//the high half of the upper value
					isRange = (lower != upper) || (lowerHighBytes != upperHighBytes);
				} else {
					lowerHighBytes = upperHighBytes = 0;
					isRange = (lower != upper);
				}
				expandedStart = i;
				expandedEnd = i + count;
				
				while(count >= 0) { //add the missing segments
					Integer currentPrefix = getSegmentPrefixLength(normalizedSegmentIndex, IPv6Address.BITS_PER_SEGMENT, qualifier);
					int newLower, newUpper;
					boolean segFlags[] = flags;
					if(isRange) {
						int segmentMask = IPv6Address.MAX_VALUE_PER_SEGMENT;
						if(count >= 4) {
							int shift = IPv6Address.BITS_PER_SEGMENT * (count % 4);
							newLower = (int) (lowerHighBytes >>> shift) & segmentMask;
							newUpper = (int) (upperHighBytes >>> shift) & segmentMask;
						} else {
							int shift = IPv6Address.BITS_PER_SEGMENT * count;
							newLower = (int) (lower >>> shift) & segmentMask;
							newUpper = (int) (upper >>> shift) & segmentMask;
						}
						//we may be able to reuse our strings on the final segment
						//for previous segments, strings can be reused only when the value is 0, which we do not need to cache.  Any other value changes when shifted.  
						if(count == 0 && newLower == lower && lowerHighBytes == 0) {
							if(newUpper != upper || upperHighBytes != 0) {
								segFlags[AddressParseData.STANDARD_RANGE_STR_INDEX] = false;
							}
						} else {
							segFlags = null;
						}
					} else {
						if(count >= 4) {
							newLower = newUpper = (int) (lowerHighBytes >>> (IPv6Address.BITS_PER_SEGMENT * (count % 4))) & IPv6Address.MAX_VALUE_PER_SEGMENT;
							segFlags = null;
						} else {
							newLower = newUpper = (int) (lower >>> (IPv6Address.BITS_PER_SEGMENT * count)) & IPv6Address.MAX_VALUE_PER_SEGMENT;
							if(count != 0 || newLower != lower || lowerHighBytes != 0) {
								segFlags = null;
							}
						}
					}
					Integer segmentMask = hasMask ? mask.getSegment(normalizedSegmentIndex).getLowerSegmentValue() : null;
					if(segmentMask != null || currentPrefix != null) {
						hostSegments = allocateHostSegments(hostSegments, segments, creator, ipv6SegmentCount, normalizedSegmentIndex);
						hostSegments[normalizedSegmentIndex] = createSegment(addressString, IPVersion.IPV6, newLower, newUpper, segFlags, indices, null, null, creator);
					}
					segments[normalizedSegmentIndex] = createSegment(
						addressString,
						IPVersion.IPV6,
						newLower,
						newUpper,
						segFlags,
						indices,
						currentPrefix,
						segmentMask,
						creator);
					++normalizedSegmentIndex;
					count--;
				}
				break;
			} //end joined segments
			
			Integer segmentMask = hasMask ? mask.getSegment(normalizedSegmentIndex).getLowerSegmentValue() : null;
			Integer segmentPrefixLength = getSegmentPrefixLength(normalizedSegmentIndex, IPv6Address.BITS_PER_SEGMENT, qualifier);
			if(segmentMask != null || segmentPrefixLength != null) {
				hostSegments = allocateHostSegments(hostSegments, segments, creator, ipv6SegmentCount, normalizedSegmentIndex);
				hostSegments[normalizedSegmentIndex] = createSegment(addressString, IPVersion.IPV6, (int) lower, (int) upper, flags, indices, null, null, creator);
			}
			segments[normalizedSegmentIndex] = createSegment(
				addressString,
				IPVersion.IPV6,
				(int) lower,
				(int) upper,
				flags,
				indices,
				segmentPrefixLength,
				segmentMask,
				creator);
			normalizedSegmentIndex++;
			int expandValueLower = 0, expandValueUpper = 0;
			if(!expandedSegments) {
				//check for any missing segments that we should account for here
				boolean expandSegments = false;
				if(parseData.isWildcard(i)) {
					expandValueLower = 0;
					expandValueUpper = IPv6Address.MAX_VALUE_PER_SEGMENT;
					expandSegments = true;
					for(int j = i + 1; j < segmentCount; j++) {
						if(parseData.isWildcard(j) || parseData.isCompressed(j)) {//another wildcard further down
							expandSegments = false;
							break;
						}
					}
				} else {
					//compressed ipv6?
					if(parseData.isCompressed(i)) {
						expandSegments = true;
						expandValueLower = expandValueUpper = 0;
					}
				}
				//fill in missing segments
				if(expandSegments) {
					expandedSegments = true;
					int count = missingSegmentCount;
					while(count-- > 0) { //add the missing segments
						segmentMask = hasMask ? mask.getSegment(normalizedSegmentIndex).getLowerSegmentValue() : null;
						segmentPrefixLength = getSegmentPrefixLength(normalizedSegmentIndex, IPv6Address.BITS_PER_SEGMENT, qualifier);
						if(segmentMask != null || segmentPrefixLength != null) {
							hostSegments = allocateHostSegments(hostSegments, segments, creator, ipv6SegmentCount, normalizedSegmentIndex);
							hostSegments[normalizedSegmentIndex] = createSegment(addressString, IPVersion.IPV6, expandValueLower, expandValueUpper, null, null, null, null, creator);
						}
						segments[normalizedSegmentIndex] = createSegment(
								addressString,
								IPVersion.IPV6,
								expandValueLower,
								expandValueUpper,
								null,
								null,
								segmentPrefixLength,
								segmentMask,
								creator);
						normalizedSegmentIndex++;
					}
				}
			}
		}
		IPv6AddressSection result, hostResult;
		result = hostResult = null;
		ParsedAddressCreator addressCreator = creator;
		if(mixed) {
			IPv4AddressSection ipv4AddressSection = parseData.mixedParsedAddress.createIPv4Addresses().getSection();
			boolean embeddedSectionIsChanged = false;
			for(int n = 0; n < 2; n++) {
				int m = n << 1;
				IPv4AddressSegment one = ipv4AddressSection.getSegment(m);
				IPv4AddressSegment two = ipv4AddressSection.getSegment(m + 1);
				Integer segmentMask = hasMask ? mask.getSegment(normalizedSegmentIndex).getLowerSegmentValue() : null;
				IPv6AddressSegment newSegment;
				Integer segmentPrefixLength = getSegmentPrefixLength(normalizedSegmentIndex, IPv6Address.BITS_PER_SEGMENT, qualifier);
				boolean doHostSegment = segmentMask != null || segmentPrefixLength != null;
				if(doHostSegment) {
					hostSegments = allocateHostSegments(hostSegments, segments, creator, ipv6SegmentCount, normalizedSegmentIndex);
				}
				int oneLower = one.getLowerSegmentValue();
				int twoLower = two.getLowerSegmentValue();
				if(!one.isMultiple() && !two.isMultiple()) {
					if(doHostSegment) {
						hostSegments[normalizedSegmentIndex] = createSegment(oneLower, twoLower, null, null, creator);
					}
					segments[normalizedSegmentIndex] = newSegment = createSegment(
							oneLower,
							twoLower,
							segmentPrefixLength,
							segmentMask,
							creator);
				} else {
					// this can throw IncompatibleAddressException
					int oneUpper = one.getUpperSegmentValue();
					int twoUpper = two.getUpperSegmentValue();
					if(doHostSegment) {
						hostSegments[normalizedSegmentIndex] = createSegment(one, two, oneLower, oneUpper, twoLower, twoUpper, null, null, creator);
					}
					segments[normalizedSegmentIndex] = newSegment = createSegment(
							one, 
							two,
							oneLower,
							oneUpper,
							twoLower,
							twoUpper,
							segmentPrefixLength,
							segmentMask,
							creator);
				}
				embeddedSectionIsChanged |= newSegment.isPrefixed() || /* note that parseData.mixedParsedAddress is never prefixed */ 
						newSegment.getLowerSegmentValue() != ((one.getLowerSegmentValue() << IPv4Address.BITS_PER_SEGMENT) | two.getLowerSegmentValue()) ||
						newSegment.getUpperSegmentValue() != ((one.getUpperSegmentValue() << IPv4Address.BITS_PER_SEGMENT) | two.getUpperSegmentValue());
				normalizedSegmentIndex++;
			}
			if(!embeddedSectionIsChanged) {
				if(hostSegments != null) {
					hostResult = addressCreator.createSectionInternal(hostSegments, ipv4AddressSection);
				}
				result = addressCreator.createSectionInternal(segments, ipv4AddressSection, getPrefixLength(qualifier));
			}
		} 
		if(result == null) {
			if(hostSegments != null) {
				hostResult = addressCreator.createSectionInternal(hostSegments);
			}
			result = addressCreator.createPrefixedSectionInternal(segments, getPrefixLength(qualifier));
		}
		if(checkExpandedValues(result, expandedStart, expandedEnd)) {
			throw new IncompatibleAddressException(addressString, "ipaddress.error.invalid.joined.ranges");
		}
		if(checkExpandedValues(hostResult, expandedStart, expandedEnd)) {
			hostResult = null;
		}
		return new IPAddresses(result, hostResult) {
			@Override
			ParsedAddressCreator getCreator() {
				return getIPv6AddressCreator();
			}
		};
	}
	
	private static  S createSegment(
			CharSequence addressString,
			IPVersion version,
			int val,
			int upperVal,
			boolean flags[],
			int indices[],
			Integer segmentPrefixLength,
			Integer mask,
			ParsedAddressCreator creator) {
		if(val != upperVal) {
			return createRangeSegment(addressString, version, val, upperVal, flags, indices, segmentPrefixLength, mask, creator);
		}
		int stringVal = val;
		if(mask != null) {
			val &= mask;
		}
		S result;
		if(flags == null) {
			result = creator.createSegment(val, val, segmentPrefixLength);
		} else {
			result = creator.createSegmentInternal(
				val,
				segmentPrefixLength,
				addressString,
				stringVal,
				flags[AddressParseData.STANDARD_STR_INDEX],
				indices[AddressParseData.LOWER_STR_START_INDEX],
				indices[AddressParseData.LOWER_STR_END_INDEX]);
		}
		return result;
	}
	
	/*
	 * create an IPv6 segment by joining two IPv4 segments
	 */
	private static IPv6AddressSegment createSegment(int value1, int value2, Integer segmentPrefixLength, Integer mask,
			IPv6AddressCreator creator) {
		int value = (value1 << IPv4Address.BITS_PER_SEGMENT) | value2;
		if(mask != null) {
			value &= mask;
		}
		IPv6AddressSegment result = creator.createSegment(value, segmentPrefixLength);
		return result;
	}
	
	/*
	 * create an IPv6 segment by joining two IPv4 segments
	 */
	private static IPv6AddressSegment createSegment(
			IPv4AddressSegment one,
			IPv4AddressSegment two,
			int upperRangeLower,
			int upperRangeUpper,
			int lowerRangeLower,
			int lowerRangeUpper,
			Integer segmentPrefixLength,
			Integer mask,
			IPv6AddressCreator creator) throws IncompatibleAddressException {
		boolean hasMask = (mask != null);
		if(hasMask) {
			int maskInt = mask.intValue();
			int shift = IPv4Address.BITS_PER_SEGMENT;
			int shiftedMask = maskInt >> shift;
			upperRangeLower &= shiftedMask;
			upperRangeUpper &= shiftedMask;
			lowerRangeLower &= maskInt;
			lowerRangeUpper &= maskInt;
		}
		IPv6AddressSegment result = join(one, two, upperRangeLower, upperRangeUpper, lowerRangeLower, lowerRangeUpper, segmentPrefixLength, creator);
		if(hasMask && !result.isMaskCompatibleWithRange(mask.intValue(), segmentPrefixLength)) {
			throw new IncompatibleAddressException(result, mask, "ipaddress.error.maskMismatch");
		}
		return result;
	}
	
	private static IPv6AddressSegment join(
			IPv4AddressSegment one,
			IPv4AddressSegment two,
			int upperRangeLower,
			int upperRangeUpper,
			int lowerRangeLower,
			int lowerRangeUpper,
			Integer segmentPrefixLength,
			IPv6AddressCreator creator) throws IncompatibleAddressException {
		int shift = IPv4Address.BITS_PER_SEGMENT;
		if(upperRangeLower != upperRangeUpper) {
			//if the high segment has a range, the low segment must match the full range, 
			//otherwise it is not possible to create an equivalent IPv6 range when joining two IPv4 ranges
			if(segmentPrefixLength != null && creator.getNetwork().getPrefixConfiguration().allPrefixedAddressesAreSubnets()) {
				if(segmentPrefixLength > shift) {
					int lowerPrefixLength = segmentPrefixLength - shift;
					
					int fullMask = ~(~0 << shift); //allBitSize must be 6 digits at most for this shift to work per the java spec (so it must be less than 2^6 = 64)
					int networkMask = fullMask & (fullMask << (shift - lowerPrefixLength));
					int hostMask = ~networkMask & fullMask;
					lowerRangeLower &= networkMask;
					lowerRangeUpper |= hostMask;
					if(lowerRangeLower != 0 || lowerRangeUpper != IPv4Address.MAX_VALUE_PER_SEGMENT) {
						throw new IncompatibleAddressException(one, two, "ipaddress.error.invalidMixedRange");
					}
				} else {
					lowerRangeLower = 0;
					lowerRangeUpper = IPv4Address.MAX_VALUE_PER_SEGMENT;
				}
			} else if(lowerRangeLower != 0 || lowerRangeUpper != IPv4Address.MAX_VALUE_PER_SEGMENT) {
				throw new IncompatibleAddressException(one, two, "ipaddress.error.invalidMixedRange");
			}
		}
		return creator.createSegment(
				(upperRangeLower << shift) | lowerRangeLower,
				(upperRangeUpper << shift) | lowerRangeUpper,
				segmentPrefixLength);
	}
	
	private static  S createRangeSegment(
			CharSequence addressString,
			IPVersion version,
			int stringLower,
			int stringUpper,
			boolean flags[],
			int indices[],
			Integer segmentPrefixLength,
			Integer mask,
			ParsedAddressCreator creator) {
		int lower = stringLower, upper = stringUpper;
		boolean hasMask = (mask != null);
		if(hasMask) {
			int maskInt = mask.intValue();
			lower &= maskInt;
			upper &= maskInt;
		}
		S result;
		if(flags == null) {
			result = creator.createSegment(lower, upper, segmentPrefixLength);
		} else {
			result = creator.createSegmentInternal(
				lower,
				upper,
				segmentPrefixLength,
				addressString,
				stringLower,
				stringUpper,
				flags[AddressParseData.STANDARD_STR_INDEX],
				flags[AddressParseData.STANDARD_RANGE_STR_INDEX],
				indices[AddressParseData.LOWER_STR_START_INDEX],
				indices[AddressParseData.LOWER_STR_END_INDEX],
				indices[AddressParseData.UPPER_STR_END_INDEX]);
		}
		if(hasMask && !result.isMaskCompatibleWithRange(mask.intValue(), segmentPrefixLength)) {
			throw new IncompatibleAddressException(result, mask, "ipaddress.error.maskMismatch");
		}
		return result;
	}
	
	static IPAddress createAllAddress(IPVersion version, ParsedHostIdentifierStringQualifier qualifier, HostIdentifierString originator, 
			IPAddressStringParameters options) {
		int segmentCount = IPAddress.getSegmentCount(version);
		IPAddress mask = qualifier.getMask();
		if(mask != null && mask.getBlockMaskPrefixLength(true) != null) {
			mask = null;//we don't do any masking if the mask is a subnet mask, instead we just map it to the corresponding prefix length
		}
		boolean hasMask = mask != null;
		Integer prefLength = getPrefixLength(qualifier);
		if(version.isIPv4()) {
			ParsedAddressCreator creator = options.getIPv4Parameters().getNetwork().getAddressCreator();
			IPv4AddressSegment segments[] = creator.createSegmentArray(segmentCount);
			for(int i = 0; i < segmentCount; i++) {
				Integer segmentMask = hasMask ? mask.getSegment(i).getLowerSegmentValue() : null;
				segments[i] = createRangeSegment(
						null,
						version,
						0,
						IPv4Address.MAX_VALUE_PER_SEGMENT,
						null,
						null,
						getSegmentPrefixLength(i, version, qualifier),
						segmentMask,
						creator);
			}
			return creator.createAddressInternal(segments, originator, prefLength);
		} else {
			ParsedAddressCreator creator = options.getIPv6Parameters().getNetwork().getAddressCreator();
			IPv6AddressSegment segments[] = creator.createSegmentArray(segmentCount);
			for(int i = 0; i < segmentCount; i++) {
				Integer segmentMask = hasMask ? mask.getSegment(i).getLowerSegmentValue() : null;
				segments[i] = createRangeSegment(
						null,
						version,
						0,
						IPv6Address.MAX_VALUE_PER_SEGMENT,
						null,
						null,
						getSegmentPrefixLength(i, version, qualifier),
						segmentMask,
						creator);
			}
			return creator.createAddressInternal(segments, qualifier.getZone(), originator, prefLength);
		}
	}

	private static Integer getPrefixLength(ParsedHostIdentifierStringQualifier qualifier) {
		IPAddress mask = qualifier.getMask();
		Integer bits = mask != null ? mask.getBlockMaskPrefixLength(true) : qualifier.getNetworkPrefixLength(); //note that the result of mask.getBlockMaskPrefixLength(true) is cached inside IPAddressSection
		return bits;
	}

	/**
	 * Across the address prefixes are:
	 * IPv6: (null):...:(null):(1 to 16):(0):...:(0)
	 * or IPv4: ...(null).(1 to 8).(0)...
	 * 
	 * @param segmentIndex
	 * @param segmentCount
	 * @param version
	 * @return
	 */
	private static Integer getSegmentPrefixLength(int segmentIndex, int bitsPerSegment, ParsedHostIdentifierStringQualifier qualifier) {
		IPAddress mask = qualifier.getMask();
		Integer networkPrefixLength = qualifier.getNetworkPrefixLength();
		//note that either mask or networkPrefixLength is non-null but not both
		Integer bits = mask != null ? mask.getBlockMaskPrefixLength(true) : networkPrefixLength; //note that the result of mask.getBlockMaskPrefixLength(true) is cached inside IPAddressSection
		return IPAddressSection.getSegmentPrefixLength(bitsPerSegment, bits, segmentIndex);
	}
	
	/**
	 * Across the address prefixes are:
	 * IPv6: (null):...:(null):(1 to 16):(0):...:(0)
	 * or IPv4: ...(null).(1 to 8).(0)...
	 * 
	 * @param segmentIndex
	 * @param segmentCount
	 * @param version
	 * @return
	 */
	private static Integer getSegmentPrefixLength(int segmentIndex, IPVersion version, ParsedHostIdentifierStringQualifier qualifier) {
		return getSegmentPrefixLength(segmentIndex, IPAddressSection.bitsPerSegment(version), qualifier);
	}
	
	@Override
	public String toString() {
		return addressString.toString();
	}
}