package org.jscsi.target.settings; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * An abstract parent class to {@link SingleNumericalValue} and {@link NumericalValueRange}. Objects of these * two classes represent single * integers and integer intervals, respectively, and can be parsed from any * properly formatted <i>key=value</i> pair <i>value</i>, be they formatted in * decimal, hexadecimal, or Base64 notation. * <p> * Instances of {@link NumericalValue} can be intersected, i.e. that by using either the * {@link #intersect(NumericalValue)} or the {@link #intersect(NumericalValue, NumericalValue)}, a * {@link NumericalValue} can be created which encompasses all integers that are part of the range of values * represented by the intersected {@link NumericalValue} objects. * <p> * For example, if the interval [1,10] is intersected with the interval [5,15], the result would be a * {@link NumericalValueRange} representing the interval [5,10]. Intersecting an interval with a single number * would return the number, but only if the number is part of the interval. Generally, both methods return * <code>null</code> if there is no overlap. * * @author Andreas Ergenzinger */ public abstract class NumericalValue { // *** single number regular expressions *** /** * Regular expression for decimal integers. */ private static final String DECIMAL_REGEX = "0|[-]?[1-9][0-9]*"; /** * Regular expression for hexadecimal integers. */ private static final String HEX_REGEX = "0[x|X][0-9a-fA-F]+"; /** * Regular expression for Base64 integers. */ private static final String BASE_64_REGEX = "0[b|B][0-9a-zA-Z+/]+"; private static final String SINGLE_CONSTANT_REGEX = DECIMAL_REGEX + "|" + HEX_REGEX + "|" + BASE_64_REGEX; // *** single number patterns *** /** * A precompiled pattern for matching decimal integer {@link String}. */ public static final Pattern DECIMAL_CONSTANT_PATTERN = Pattern.compile(DECIMAL_REGEX); /** * A precompiled pattern for matching hexadecimal integer {@link String}. */ public static final Pattern HEX_CONSTANT_PATTERN = Pattern.compile(HEX_REGEX); /** * A precompiled pattern for matching Base64 integer {@link String}. */ public static final Pattern BASE_64_CONSTANT_PATTERN = Pattern.compile(BASE_64_REGEX); /** * A precompiled pattern for matching decimal, hexadecimal, and Base64 * integer {@link String}. */ public static final Pattern SINGLE_CONSTANT_PATTERN = Pattern.compile(SINGLE_CONSTANT_REGEX); /** * A precompiled pattern for matching decimal, hexadecimal, and Base64 * integer interval {@link String} (two integers separated by '~'). */ public static final Pattern NUMERICAL_RANGE_PATTERN = Pattern.compile("(" + SINGLE_CONSTANT_REGEX + ")~(" + SINGLE_CONSTANT_REGEX + ")"); // *** methods *** /** * Parses a {@link NumericalValue} from a {@link String}. * * @param value * the {@link String} to parse. * @return a {@link NumericalValue} or <code>null</code> if the parameter * does not match any of the supported patterns (specified by the * iSCSI standard). */ public static final NumericalValue parseNumericalValue(final String value) { // return SingleNumericalValue ... final Matcher singleValueMatcher = SINGLE_CONSTANT_PATTERN.matcher(value); if (singleValueMatcher.matches()) return SingleNumericalValue.parseSingleNumericValue(value); // ... NumericalValueRange ... final Matcher rangeMatcher = NUMERICAL_RANGE_PATTERN.matcher(value); if (rangeMatcher.matches()) return NumericalValueRange.parseNumericalValueRange(value); // ... or null return null; } /** * Returns a {@link NumericalValue} spanning the overlap of this {@link NumericalValue} and the parameter. * * @param value * the {@link NumericalValue} to be intersected with this object * @return a {@link NumericalValue} representing the intersection of this {@link NumericalValue} with the * parameter */ public NumericalValue intersect(final NumericalValue value) { return intersect(this, value); } /** * Returns a {@link NumericalValue} representing the intersection of the two * parameters * * @param a * the first {@link NumericalValue} * @param b * the second {@link NumericalValue} * @return a {@link NumericalValue} representing the intersection of the two * parameters */ public static NumericalValue intersect(final NumericalValue a, final NumericalValue b) { // early exit if (a == null || b == null) return null; // get ranges of a and b int aMin, aMax, bMin, bMax; if (a instanceof SingleNumericalValue) { final SingleNumericalValue v = (SingleNumericalValue)a; aMin = v.getValue(); aMax = aMin; } else { final NumericalValueRange r = (NumericalValueRange)a; aMin = r.getMin(); aMax = r.getMax(); } if (b instanceof SingleNumericalValue) { final SingleNumericalValue v = (SingleNumericalValue)b; bMin = v.getValue(); bMax = bMin; } else { final NumericalValueRange r = (NumericalValueRange)b; bMin = r.getMin(); bMax = r.getMax(); } // intersect ranges final int min = Math.max(aMin, bMin); final int max = Math.min(aMax, bMax); if (min == max) return SingleNumericalValue.create(min); else return NumericalValueRange.create(min, max); } /** * Returns true if the passed {@link Integer} or {@link NumericalValue} lies * completely inside the interval represented by this {@link NumericalValue} . If the parameter is not an * {@link Integer} or a {@link NumericalValue}, * the method will return <code>false</code>. * * @param value * the {@link Integer} or {@link NumericalValue} to check * @return <code>true</code> if the value is complete contained, <code>false</code> if it is not */ public abstract boolean contains(Object value); /** * Returns true if the passed integer lies completely inside the interval * represented by this {@link NumericalValue}. * * @param value * the integer to check * @return <code>true</code> if the value is complete contained, <code>false</code> if it is not */ public abstract boolean contains(int value); }