All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.nedap.archie.base.Interval Maven / Gradle / Ivy

The newest version!
package com.nedap.archie.base;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.nedap.archie.rminfo.Invariant;
import com.nedap.archie.rminfo.RMPropertyIgnore;

import javax.annotation.Nullable;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;
import java.time.temporal.TemporalAmount;
import java.util.Objects;

/**
 * Interval abstraction, featuring upper and lower limits that may be open or closed, included or not included. Interval of ordered items.
 * 

* Created by pieter.bos on 15/10/15. */ @XmlType(name = "INTERVAL") @XmlAccessorType(XmlAccessType.FIELD) public class Interval extends OpenEHRBase { /** * lower bound. */ @Nullable private T lower; /** * Upper bound. */ @Nullable private T upper; /** * lower boundary open (i.e. = -infinity) */ @XmlAttribute(name = "lower_unbounded") private boolean lowerUnbounded = false; /** * upper boundary open (i.e. = +infinity) */ @XmlAttribute(name = "upper_unbounded") private boolean upperUnbounded = false; /** * lower boundary value included in range if not lower_unbounded. */ @XmlAttribute(name = "lower_included") private boolean lowerIncluded = true; /** * upper boundary value included in range if not upper_unbounded. */ @XmlAttribute(name = "upper_included") private boolean upperIncluded = true; public Interval() { } public Interval(T value) { this(value, value); } public Interval(T lower, T upper) { this(lower, upper, true, true); } public Interval(T lower, T upper, boolean lowerIncluded, boolean upperIncluded) { this.lower = lower; this.upper = upper; this.lowerIncluded = lowerIncluded; this.upperIncluded = upperIncluded; if (upper == null) { this.upperUnbounded = true; this.upperIncluded = false; } if (lower == null) { this.lowerUnbounded = true; this.lowerIncluded = false; } } public static Interval lowerUnbounded(T upper, boolean upperIncluded) { Interval result = new Interval<>(null, upper, false, upperIncluded); result.setLowerUnbounded(true); return result; } public static Interval upperUnbounded(T lower, boolean lowerIncluded) { Interval result = new Interval<>(lower, null, lowerIncluded, false); result.setUpperUnbounded(true); return result; } public static Interval unbounded() { Interval result = new Interval(null, null, false, false); result.setLowerUnbounded(true); result.setUpperUnbounded(true); return result; } public T getLower() { return lower; } public void setLower(T lower) { this.lower = lower; } public T getUpper() { return upper; } public void setUpper(T upper) { this.upper = upper; } public boolean isLowerUnbounded() { return lowerUnbounded; } public void setLowerUnbounded(boolean lowerUnbounded) { this.lowerUnbounded = lowerUnbounded; } public boolean isUpperUnbounded() { return upperUnbounded; } public void setUpperUnbounded(boolean upperUnbounded) { this.upperUnbounded = upperUnbounded; } public boolean isLowerIncluded() { return lowerIncluded; } public void setLowerIncluded(boolean lowerIncluded) { this.lowerIncluded = lowerIncluded; } public boolean isUpperIncluded() { return upperIncluded; } public void setUpperIncluded(boolean upperIncluded) { this.upperIncluded = upperIncluded; } public boolean has(T value) { if (lowerUnbounded && upperUnbounded) { return true; } //since TemporalAmount does not implement Comparable we have to do some magic here Comparable comparableValue; Comparable comparableLower; Comparable comparableUpper; if (value instanceof TemporalAmount && lower instanceof TemporalAmount && upper instanceof TemporalAmount) { //TemporalAmount is not comparable, but can always be converted to a duration that is comparable. comparableValue = toComparable(value); comparableLower = toComparable(lower); comparableUpper = toComparable(upper); } else if (!(isComparable(lower) && isComparable(upper) && isComparable(value))) { throw new UnsupportedOperationException("subclasses of interval not implementing comparable should implement their own has method"); } else { comparableValue = (Comparable) value; comparableLower = (Comparable) lower; comparableUpper = (Comparable) upper; } if (value == null) { //interval values are not concerned with cardinality, so return true if not set return true; } if (!lowerUnbounded) { int comparedWithLower = comparableValue.compareTo(comparableLower); if (comparedWithLower < 0 || (!lowerIncluded && comparedWithLower == 0)) { return false; } } if (!upperUnbounded) { int comparedWithUpper = comparableValue.compareTo(comparableUpper); if (comparedWithUpper > 0 || (!upperIncluded && comparedWithUpper == 0)) { return false; } } return true; } /** * Get the lower value as a comparable amount. Required because some temporal amounts are not always directly comparable * @return */ @JsonIgnore @XmlTransient @RMPropertyIgnore public Comparable getComparableLower() { return toComparable(lower); } /** * Get the lower value as a comparable amount. Required because some temporal amounts are not always directly comparable * @return */ @JsonIgnore @XmlTransient @RMPropertyIgnore public Comparable getComparableUpper() { return toComparable(upper); } private Comparable toComparable(T value) { if(value == null) { return null; } if (value instanceof TemporalAmount && !(value instanceof Comparable) && isNonComparableTemporalAmount(value)) { //TemporalAmount is not comparable, but can always be converted to a duration that is comparable. return IntervalDurationConverter.from((TemporalAmount) value); } else if (!isComparable(value)) { throw new UnsupportedOperationException("subclasses of interval not implementing comparable should implement their own has method"); } else { return (Comparable) value; } } private int compareTo(T intervalValue, T value) { Comparable comparableIntervalValue; Comparable comparableValue; if (value instanceof TemporalAmount && !(value instanceof Comparable) && isNonComparableTemporalAmount(intervalValue)) { //TemporalAmount is not comparable, but can always be converted to a duration that is comparable. comparableValue = value == null ? null : IntervalDurationConverter.from((TemporalAmount) value); comparableIntervalValue = intervalValue == null ? null : IntervalDurationConverter.from((TemporalAmount) intervalValue); } else if (!(isComparable(intervalValue) && isComparable(value))) { throw new UnsupportedOperationException("subclasses of interval not implementing comparable should implement their own has method"); } else { comparableValue = (Comparable) value; comparableIntervalValue = (Comparable) intervalValue; } return comparableValue.compareTo(comparableIntervalValue); } private boolean isNonComparableTemporalAmount(T value) { return value == null || (!(value instanceof Comparable) && value instanceof TemporalAmount); } private boolean isComparable(T value) { return value == null || value instanceof Comparable; } /** * True if there is any overlap between intervals represented by Current and * `other'. True if at least one limit of other is strictly inside the limits * of this interval. * * @param other * @return */ public Boolean intersects(Interval other) { return (lowerUnbounded && other.lowerUnbounded) || (upperUnbounded && other.upperUnbounded) || (compareTo(lower, other.lower) < 0 && compareTo(upper, other.upper) < 0 && compareTo(other.lower, upper) < 0) || (compareTo(other.lower, lower) < 0 && compareTo(other.upper, upper) < 0 && compareTo(lower, other.upper) < 0) || other.contains(this) || this.contains(other); } /** * True if current interval properly contains `other'? True if all points of * `other' are inside the current interval. * * @param other * @return */ public Boolean contains(Interval other) { boolean otherHasLower = false; boolean otherHasUpper = false; if (other.lowerUnbounded) { otherHasLower = this.lowerUnbounded; } else { otherHasLower = has(other.lower); } if (other.upperUnbounded) { otherHasUpper = this.upperUnbounded; } else { otherHasUpper = has(other.upper); } return otherHasLower && otherHasUpper; } /** * Returns true if both sets subsume each other. * * @param other * @return */ public Boolean setsAreEqual(Interval other) { return this.contains(other) && other.contains(this); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Interval interval = (Interval) o; return (lowerUnbounded == interval.lowerUnbounded) && (upperUnbounded == interval.upperUnbounded) && (lowerUnbounded || lowerIncluded == interval.lowerIncluded) && (upperUnbounded || upperIncluded == interval.upperIncluded) && (lowerUnbounded || Objects.equals(lower, interval.lower)) && (upperUnbounded || Objects.equals(upper, interval.upper)); } @Override public int hashCode() { return Objects.hash( lowerUnbounded, upperUnbounded, lowerUnbounded? false: lowerIncluded, upperUnbounded? false: upperIncluded, lowerUnbounded? null : lower, upperUnbounded? null : upper); } @Override public String toString() { if (lowerUnbounded) { return "|" + (upperIncluded ? "<=" : "<") + upper + "|"; } if (upperUnbounded) { return "|" + (lowerIncluded ? ">=" : ">") + lower + "|"; } if (lower != null && upper != null && lower == upper) { return lower.toString(); } StringBuilder result = new StringBuilder(); result.append("|"); if (!lowerIncluded) { result.append(">"); } result.append(lower); result.append(".."); if (!upperIncluded) { result.append("<"); } result.append(upper); result.append("|"); return result.toString(); } public void fixUnboundedIncluded() { if (upperUnbounded) { this.upperIncluded = false; } if (lowerUnbounded) { this.lowerIncluded = false; } } @Invariant("Lower_included_valid") public boolean lowerIncludedValid() { if(lowerUnbounded) { return !lowerIncluded; } return true; } @Invariant("Upper_included_valid") public boolean upperIncludedValid() { if(upperUnbounded) { return !upperIncluded; } return true; } @Invariant("Limits_consistent") public boolean limitsConsistent() { if(!lowerUnbounded && !upperUnbounded) { return compareTo(upper, lower) <= 0; } return true; } @Invariant(value = "Limits_comparable", ignored = true) public boolean limitsComparable() { return true; //great test, but hard to implement, you'll find out when using this } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy