/******************************************************************************* * Copyright (c) 2010 Association for Decentralized Information Management in * Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.databoard.util; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 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 */ 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 { 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 { 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; } }