--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2010 Association for Decentralized Information Management in\r
+ * Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.databoard.util;
+
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+
+
+
+/**
+ * Number range.
+ *
+ * Examples:
+ * Inclusive "[0..100]"
+ * Exclusive "[0..100)"
+ * Unlimited "[..]"
+ * No upper limit "[0..)"
+ * No lower limit "(..0]"
+ * Exact value "0"
+ * Exclude all "()"
+ *
+ * Inclusive "[0.5..100.5]"
+ * Exclusive "[0.5..100.5)"
+ * Unlimited "[..]"
+ * No upper limit "[0.5..)"
+ * No lower limit "(..0.5]"
+ * Exact value "[0.5]"
+ *
+ * Inclusive "[0e..100]"
+ * Exclusive "[0..100)"
+ * Unlimited ""
+ * No upper limit "[0..)"
+ * No lower limit "(..0]"
+ * Exact value "0"
+ *
+ * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
+ */
+public class Range {
+
+ Limit lower;
+ Limit upper;
+
+ public static Range create(Byte lower, Byte upper, boolean lowerInclusive, boolean upperInclusive)
+ {
+ Limit ll = lowerInclusive ? Limit.inclusive(lower) : Limit.exclusive(lower);
+ Limit ul = upperInclusive ? Limit.inclusive(upper) : Limit.exclusive(upper);
+ return new Range(ll, ul);
+ }
+
+ public static Range create(Integer lower, Integer upper, boolean lowerInclusive, boolean upperInclusive)
+ {
+ Limit ll = lowerInclusive ? Limit.inclusive(lower) : Limit.exclusive(lower);
+ Limit ul = upperInclusive ? Limit.inclusive(upper) : Limit.exclusive(upper);
+ return new Range(ll, ul);
+ }
+
+ public static Range create(Long lower, Long upper, boolean lowerInclusive, boolean upperInclusive)
+ {
+ Limit ll = lowerInclusive ? Limit.inclusive(lower) : Limit.exclusive(lower);
+ Limit ul = upperInclusive ? Limit.inclusive(upper) : Limit.exclusive(upper);
+ return new Range(ll, ul);
+ }
+
+ public static Range create(Float lower, Float upper, boolean lowerInclusive, boolean upperInclusive)
+ {
+ Limit ll = lowerInclusive ? Limit.inclusive(lower) : Limit.exclusive(lower);
+ Limit ul = upperInclusive ? Limit.inclusive(upper) : Limit.exclusive(upper);
+ return new Range(ll, ul);
+ }
+
+ public static Range create(Double lower, Double upper, boolean lowerInclusive, boolean upperInclusive)
+ {
+ Limit ll = lowerInclusive ? Limit.inclusive(lower) : Limit.exclusive(lower);
+ Limit ul = upperInclusive ? Limit.inclusive(upper) : Limit.exclusive(upper);
+ return new Range(ll, ul);
+ }
+
+ public static Range create(Byte exact)
+ {
+ Limit l = Limit.inclusive(exact);
+ return new Range(l, l);
+ }
+
+ public static Range create(Integer exact)
+ {
+ Limit l = Limit.inclusive(exact);
+ return new Range(l, l);
+ }
+
+ public static Range create(Long exact)
+ {
+ Limit l = Limit.inclusive(exact);
+ return new Range(l, l);
+ }
+
+ public static Range create(Float exact)
+ {
+ Limit l = Limit.inclusive(exact);
+ return new Range(l, l);
+ }
+
+ public static Range create(Double exact)
+ {
+ Limit l = Limit.inclusive(exact);
+ return new Range(l, l);
+ }
+
+ public static Range includeAll()
+ {
+ Limit l = Limit.nolimit();
+ return new Range(l, l);
+ }
+
+ public static Range excludeAll()
+ {
+ Limit l = Limit.exclusive(0);
+ return new Range(l, l);
+ }
+
+ public static Range between(Limit limit1, Limit limit2) {
+ Number l = limit1.getValue();
+ Number u = limit2.getValue();
+ if (l!=null && u!=null) {
+ if (NumberComparator.INSTANCE.compare(l, u)<0)
+ return new Range(limit2, limit1);
+ }
+ return new Range(limit1, limit2);
+ }
+
+ public Range(Limit lower, Limit upper)
+ {
+ this.lower = lower;
+ this.upper = upper;
+
+ // Ensure lower < upper
+ Number l = lower.getValue();
+ Number u = upper.getValue();
+ if (l!=null && u!=null) {
+ if (NumberComparator.INSTANCE.compare(l, u)<0)
+ throw new IllegalArgumentException("Lower limit must be less-or-equal to upper limit");
+ }
+
+ // Exact value
+ if (l!=null && u!=null && l.equals(u)) {
+ if (lower.isExclusive() || upper.isExclusive())
+ this.lower = this.upper = Limit.exclusive(0);
+ }
+
+ }
+
+ public Limit getLower() {
+ return lower;
+ }
+
+ public Limit getUpper() {
+ return upper;
+ }
+
+ @Override
+ public String toString() {
+ if (lower instanceof Limit.Nolimit && upper instanceof Limit.Nolimit) return "[..]";
+
+ // Range
+ StringBuilder sb = new StringBuilder();
+
+
+ Number l = lower.getValue();
+ Number u = upper.getValue();
+
+ if (l!=null && u!=null && l.equals(u))
+ {
+ if (lower.isExclusive() || upper.isExclusive())
+ return "()";
+
+ // Exact value
+ return l.toString();
+ }
+
+ sb.append( !lower.isInclusive() ? '(' : "[" );
+ // Range
+ if (lower instanceof Limit.Nolimit == false) sb.append( l );
+ sb.append( ".." );
+ if (upper instanceof Limit.Nolimit == false) sb.append( u );
+ sb.append( !upper.isInclusive() ? ')' : "]" );
+
+ return sb.toString();
+ }
+
+ /**
+ * Double pattern
+ *
+ * This pattern is not perfect as it accepts strings such as
+ * "e0", "", and ".", but its good enough as Double class does the final
+ * parsing.
+ */
+// static Pattern DOUBLE_PATTERN = Pattern.compile("-?+\\d*(?:\\.\\d*)?(?:[eE]\\d++)?");
+ static Pattern RANGE_PATTERN = Pattern.compile(
+ "(?:([\\(\\[])(-?\\d*(?:\\.\\d*)?(?:[eE]-?\\d+)?)??\\.\\.(-?\\d*(?:\\.\\d*)?(?:[eE]-?\\d+)?)??([\\)\\]]))|"+ // [x..y] [x..y) (x..y)
+ "(-?+\\d*(?:\\.\\d*)?(?:[eE]-?\\d+)?)|"+ // x
+ "\\(\\)"); // ()
+
+ public static Range valueOfUnchecked(String txt) {
+ try {
+ return valueOf(txt);
+ } catch (RangeException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ public static Range valueOf(String txt)
+ throws RangeException
+ {\r
+ Matcher m = RANGE_PATTERN.matcher(txt);
+ if (!m.matches()) throw new RangeException("Illegal range '" + txt + "'");
+
+ if ( (m.group(1)!=null) || (m.group(2)!=null) || (m.group(3)!=null) || (m.group(4)!=null) ) {
+
+ Limit l1, l2;
+ if (m.group(2)==null && m.group(3)==null) {
+ return new Range(Limit.nolimit(), Limit.nolimit());
+ } else if (m.group(2)==null) {
+ l1 = Limit.nolimit();
+ } else {
+ try {
+ Long l = Long.parseLong( m.group(2) );
+ l1 = m.group(1).equals("[") ? Limit.inclusive(l) : Limit.exclusive(l);
+ } catch (NumberFormatException nfe) {
+ try {
+ Double d = Double.parseDouble( m.group(2) );
+ l1 = m.group(1).equals("[") ? Limit.inclusive(d) : Limit.exclusive(d);
+ } catch (NumberFormatException e) {
+ throw new RangeException(e);
+ }
+ }
+ }
+
+ if (m.group(3)==null) {
+ l2 = Limit.nolimit();
+ } else {
+ try {
+ Long l = Long.parseLong( m.group(3) );
+ l2 = m.group(4).equals("]") ? Limit.inclusive(l) : Limit.exclusive(l);
+ } catch (NumberFormatException nfe) {
+ try {\r
+ Double d = Double.parseDouble( m.group(3) );
+ l2 = m.group(4).equals("]") ? Limit.inclusive(d) : Limit.exclusive(d);
+ } catch (NumberFormatException e) {
+ throw new RangeException(e);
+ }
+ }
+ }
+
+ return new Range(l1, l2);
+ }
+
+ if ( (m.group(5)!=null) ) {
+ try {
+ Long l = Long.parseLong( m.group(5) );
+ Limit l1 = Limit.inclusive(l);
+ return new Range(l1, l1);
+ } catch (NumberFormatException nfe) {
+ try {
+ Double d = Double.parseDouble( m.group(5) );
+ Limit l1 = Limit.inclusive(d);
+ return new Range(l1, l1);
+ } catch (NumberFormatException e) {
+ throw new RangeException(e);
+ }
+ }
+ }
+
+ Range result = new Range(Limit.exclusive(0), Limit.exclusive(0));
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof Range == false) return false;
+ Range other = (Range) obj;
+ return
+ ( other.upper == null ? upper == null : other.upper.equals( upper ) ) &&
+ ( other.lower == null ? lower == null : other.lower.equals( lower ) );
+ }
+
+ @Override
+ public int hashCode() {
+ return
+ ObjectUtils.hashCode(upper) * 13 +
+ ObjectUtils.hashCode(lower);
+ }
+
+ public boolean contains(Number value)
+ {
+ if (lower instanceof Limit.Nolimit==false)
+ {
+ int compare = NumberComparator.INSTANCE.compare(lower.getValue(), value);
+ if (compare==0 && lower.isExclusive()) return false;
+ if (compare<0) return false;
+ }
+
+ if (upper instanceof Limit.Nolimit==false)
+ {
+ int compare = NumberComparator.INSTANCE.compare(upper.getValue(), value);
+ if (compare==0 && upper.isExclusive()) return false;
+ if (compare>0) return false;
+ }
+
+ return true;
+ }
+
+
+}
+