View Javadoc

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 }