1 package org.jscsi.target.settings; 2 3 4 import java.util.regex.Matcher; 5 import java.util.regex.Pattern; 6 7 8 /** 9 * An abstract parent class to {@link SingleNumericalValue} and {@link NumericalValueRange}. Objects of these two 10 * classes represent single integers and integer intervals, respectively, and can be parsed from any properly formatted 11 * <i>key=value</i> pair <i>value</i>, be they formatted in decimal, hexadecimal, or Base64 notation. 12 * <p> 13 * Instances of {@link NumericalValue} can be intersected, i.e. that by using either the 14 * {@link #intersect(NumericalValue)} or the {@link #intersect(NumericalValue, NumericalValue)}, a 15 * {@link NumericalValue} can be created which encompasses all integers that are part of the range of values represented 16 * by the intersected {@link NumericalValue} objects. 17 * <p> 18 * For example, if the interval [1,10] is intersected with the interval [5,15], the result would be a 19 * {@link NumericalValueRange} representing the interval [5,10]. Intersecting an interval with a single number would 20 * return the number, but only if the number is part of the interval. Generally, both methods return <code>null</code> 21 * if there is no overlap. 22 * 23 * @author Andreas Ergenzinger 24 */ 25 public abstract class NumericalValue { 26 27 // *** single number regular expressions *** 28 /** 29 * Regular expression for decimal integers. 30 */ 31 private static final String DECIMAL_REGEX = "0|[-]?[1-9][0-9]*"; 32 33 /** 34 * Regular expression for hexadecimal integers. 35 */ 36 private static final String HEX_REGEX = "0[x|X][0-9a-fA-F]+"; 37 38 /** 39 * Regular expression for Base64 integers. 40 */ 41 private static final String BASE_64_REGEX = "0[b|B][0-9a-zA-Z+/]+"; 42 private static final String SINGLE_CONSTANT_REGEX = DECIMAL_REGEX + "|" + HEX_REGEX + "|" + BASE_64_REGEX; 43 44 // *** single number patterns *** 45 /** 46 * A precompiled pattern for matching decimal integer {@link String}. 47 */ 48 public static final Pattern DECIMAL_CONSTANT_PATTERN = Pattern.compile(DECIMAL_REGEX); 49 50 /** 51 * A precompiled pattern for matching hexadecimal integer {@link String}. 52 */ 53 public static final Pattern HEX_CONSTANT_PATTERN = Pattern.compile(HEX_REGEX); 54 55 /** 56 * A precompiled pattern for matching Base64 integer {@link String}. 57 */ 58 public static final Pattern BASE_64_CONSTANT_PATTERN = Pattern.compile(BASE_64_REGEX); 59 60 /** 61 * A precompiled pattern for matching decimal, hexadecimal, and Base64 integer {@link String}. 62 */ 63 public static final Pattern SINGLE_CONSTANT_PATTERN = Pattern.compile(SINGLE_CONSTANT_REGEX); 64 65 /** 66 * A precompiled pattern for matching decimal, hexadecimal, and Base64 integer interval {@link String} (two integers 67 * separated by '~'). 68 */ 69 public static final Pattern NUMERICAL_RANGE_PATTERN = Pattern.compile("(" + SINGLE_CONSTANT_REGEX + ")~(" + SINGLE_CONSTANT_REGEX + ")"); 70 71 // *** methods *** 72 73 /** 74 * Parses a {@link NumericalValue} from a {@link String}. 75 * 76 * @param value the {@link String} to parse. 77 * @return a {@link NumericalValue} or <code>null</code> if the parameter does not match any of the supported 78 * patterns (specified by the iSCSI standard). 79 */ 80 public static final NumericalValue parseNumericalValue (final String value) { 81 // return SingleNumericalValue ... 82 final Matcher singleValueMatcher = SINGLE_CONSTANT_PATTERN.matcher(value); 83 if (singleValueMatcher.matches()) return SingleNumericalValue.parseSingleNumericValue(value); 84 // ... NumericalValueRange ... 85 final Matcher rangeMatcher = NUMERICAL_RANGE_PATTERN.matcher(value); 86 if (rangeMatcher.matches()) return NumericalValueRange.parseNumericalValueRange(value); 87 // ... or null 88 return null; 89 } 90 91 /** 92 * Returns a {@link NumericalValue} spanning the overlap of this {@link NumericalValue} and the parameter. 93 * 94 * @param value the {@link NumericalValue} to be intersected with this object 95 * @return a {@link NumericalValue} representing the intersection of this {@link NumericalValue} with the parameter 96 */ 97 public NumericalValue intersect (final NumericalValue value) { 98 return intersect(this, value); 99 } 100 101 /** 102 * Returns a {@link NumericalValue} representing the intersection of the two parameters 103 * 104 * @param a the first {@link NumericalValue} 105 * @param b the second {@link NumericalValue} 106 * @return a {@link NumericalValue} representing the intersection of the two parameters 107 */ 108 public static NumericalValue intersect (final NumericalValue a, final NumericalValue b) { 109 // early exit 110 if (a == null || b == null) return null; 111 // get ranges of a and b 112 int aMin, aMax, bMin, bMax; 113 if (a instanceof SingleNumericalValue) { 114 final SingleNumericalValue v = (SingleNumericalValue) a; 115 aMin = v.getValue(); 116 aMax = aMin; 117 } else { 118 final NumericalValueRange r = (NumericalValueRange) a; 119 aMin = r.getMin(); 120 aMax = r.getMax(); 121 } 122 if (b instanceof SingleNumericalValue) { 123 final SingleNumericalValue v = (SingleNumericalValue) b; 124 bMin = v.getValue(); 125 bMax = bMin; 126 } else { 127 final NumericalValueRange r = (NumericalValueRange) b; 128 bMin = r.getMin(); 129 bMax = r.getMax(); 130 } 131 // intersect ranges 132 final int min = Math.max(aMin, bMin); 133 final int max = Math.min(aMax, bMax); 134 if (min == max) 135 return SingleNumericalValue.create(min); 136 else 137 return NumericalValueRange.create(min, max); 138 } 139 140 /** 141 * Returns true if the passed {@link Integer} or {@link NumericalValue} lies completely inside the interval 142 * represented by this {@link NumericalValue} . If the parameter is not an {@link Integer} or a 143 * {@link NumericalValue}, the method will return <code>false</code>. 144 * 145 * @param value the {@link Integer} or {@link NumericalValue} to check 146 * @return <code>true</code> if the value is complete contained, <code>false</code> if it is not 147 */ 148 public abstract boolean contains (Object value); 149 150 /** 151 * Returns true if the passed integer lies completely inside the interval represented by this {@link NumericalValue} 152 * . 153 * 154 * @param value the integer to check 155 * @return <code>true</code> if the value is complete contained, <code>false</code> if it is not 156 */ 157 public abstract boolean contains (int value); 158 }