Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/util/Range.java')
-rw-r--r--plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/util/Range.java515
1 files changed, 515 insertions, 0 deletions
diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/util/Range.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/util/Range.java
new file mode 100644
index 0000000000..41482a424a
--- /dev/null
+++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/util/Range.java
@@ -0,0 +1,515 @@
+/*******************************************************************************
+ * Copyright (c) 2010 THALES GLOBAL SERVICES.
+ * 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:
+ * Obeo - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.sirius.diagram.sequence.util;
+
+import org.eclipse.draw2d.geometry.Rectangle;
+
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Ordering;
+
+/**
+ * Represents a closed interval between two numbers. The starting and ending
+ * points of the range are considered inside the interval.
+ *
+ * @author pcdavid
+ */
+public class Range {
+ /**
+ * A sentinel object to represent the empty range. It is technically not
+ * empty but this instance is treated specially.
+ */
+ private static final Range EMPTY_RANGE = new Range(0, 0);
+
+ private final int lower;
+
+ private final int upper;
+
+ /**
+ * Constructor.
+ *
+ * @param lower
+ * the lower bound of the range.
+ * @param upper
+ * the upper bound of the range.
+ */
+ public Range(int lower, int upper) {
+ Preconditions.checkArgument(lower <= upper);
+ this.lower = lower;
+ this.upper = upper;
+ }
+
+ /**
+ * A function to obtain the lower bound of a range.
+ *
+ * @return a function to obtain the lower bound of a range.
+ */
+ public static Function<Range, Integer> lowerBoundFunction() {
+ return LowerBoundFunction.INSTANCE;
+ }
+
+ /**
+ * A function to obtain the upper bound of a range.
+ *
+ * @return a function to obtain the upper bound of a range.
+ */
+ public static Function<Range, Integer> upperBoundFunction() {
+ return UpperBoundFunction.INSTANCE;
+ }
+
+ /**
+ * A function to obtain the width of a range.
+ *
+ * @return a function to obtain the width of a range.
+ */
+ public static Function<Range, Integer> widthFunction() {
+ return WidthFunction.INSTANCE;
+ }
+
+ /**
+ * Returns an ordering suitable to sort ranges by their lower bound.
+ *
+ * @return an ordering suitable to sort ranges by their lower bound.
+ */
+ public static Ordering<Range> lowerBoundOrdering() {
+ return Ordering.natural().onResultOf(Range.lowerBoundFunction());
+ }
+
+ /**
+ * Returns an ordering suitable to sort ranges by their upper bound.
+ *
+ * @return an ordering suitable to sort ranges by their upper bound.
+ */
+ public static Ordering<Range> upperBoundOrdering() {
+ return Ordering.natural().onResultOf(Range.upperBoundFunction());
+ }
+
+ /**
+ * Returns a range representing the vertical range occupied by a rectangle.
+ *
+ * @param rect
+ * the rectangle.
+ * @return the vertical range of the rectangle.
+ */
+ public static Range verticalRange(Rectangle rect) {
+ return new Range(rect.y, rect.y + rect.height);
+ }
+
+ /**
+ * Returns a range representing the horizontal range occupied by a
+ * rectangle.
+ *
+ * @param rect
+ * the rectangle.
+ * @return the horizontal range of the rectangle.
+ */
+ public static Range horizontalRange(Rectangle rect) {
+ return new Range(rect.x, rect.x + rect.width);
+ }
+
+ /**
+ * The "lowerBound" function.
+ */
+ private static enum LowerBoundFunction implements Function<Range, Integer> {
+ INSTANCE;
+
+ public Integer apply(Range from) {
+ return from.getLowerBound();
+ }
+
+ @Override
+ public String toString() {
+ return "lowerBound";
+ }
+ }
+
+ /**
+ * The "upperBound" function.
+ */
+ private static enum UpperBoundFunction implements Function<Range, Integer> {
+ INSTANCE;
+
+ public Integer apply(Range from) {
+ return from.getUpperBound();
+ }
+
+ @Override
+ public String toString() {
+ return "upperBound";
+ }
+ }
+
+ /**
+ * The "widthBound" function.
+ */
+ private static enum WidthFunction implements Function<Range, Integer> {
+ INSTANCE;
+
+ public Integer apply(Range from) {
+ return from.width();
+ }
+
+ @Override
+ public String toString() {
+ return "width";
+ }
+ }
+
+ /**
+ * Returns the single Range instance which represents an empty range (
+ * <code>null</code> object pattern).
+ *
+ * @return an empty range, which contains no element.
+ */
+ public static Range emptyRange() {
+ return EMPTY_RANGE;
+ }
+
+ /**
+ * Tests whether the range is empty or not.
+ *
+ * @return <code>true</code> if this range is empty.
+ */
+ public boolean isEmpty() {
+ return this == EMPTY_RANGE;
+ }
+
+ /**
+ * Two ranges are equal if they are both empty or have the same bounds.
+ * <p>
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object obj) {
+ boolean result;
+ if (this == obj) {
+ // The empty range is a singleton, so this case covers empty ==
+ // empty.
+ result = true;
+ } else if (obj instanceof Range) {
+ Range that = (Range) obj;
+ if (this.isEmpty() || that.isEmpty()) {
+ result = false;
+ } else {
+ result = this.lower == that.lower && this.upper == that.upper;
+ }
+ } else {
+ result = false;
+ }
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ long temp;
+ temp = lower;
+ result = prime * result + (int) (temp ^ (temp >>> 32));
+ temp = upper;
+ result = prime * result + (int) (temp ^ (temp >>> 32));
+ return result;
+ }
+
+ /**
+ * Returns the lower bound of this range, or <code>NaN</code> if the range
+ * is empty.
+ *
+ * @return the lower bound of this range, or <code>NaN</code> if the range
+ * is empty.
+ */
+ public int getLowerBound() {
+ return isEmpty() ? Integer.MIN_VALUE : lower;
+ }
+
+ /**
+ * Returns the upper bound of this range, or <code>NaN</code> if the range
+ * is empty.
+ *
+ * @return the upper bound of this range, or <code>NaN</code> if the range
+ * is empty.
+ */
+ public int getUpperBound() {
+ return isEmpty() ? Integer.MIN_VALUE : upper;
+ }
+
+ /**
+ * Constrain a value inside this range. If the specified value if less than
+ * this range's lower bound (resp. greater than this range's upper bound),
+ * returns the lower bound (resp. the upper bound). Otherwise return the
+ * value itself.
+ *
+ * @param value
+ * the value to constrain.
+ * @return the value closest to the specified input value which is inside
+ * the range.
+ */
+ public int clamp(int value) {
+ final int result;
+ if (isEmpty()) {
+ result = Integer.MIN_VALUE;
+ } else if (value < lower) {
+ result = lower;
+ } else if (upper < value) {
+ result = upper;
+ } else {
+ result = value;
+ }
+ return result;
+ }
+
+ /**
+ * Tests whether on element is included or not in the range (inclusively).
+ *
+ * @param value
+ * the element to test.
+ * @return <code>true</code> if the element is included in the range.
+ */
+ public boolean includes(int value) {
+ if (!isEmpty()) {
+ return lower <= value && value <= upper;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Returns the width of this range.
+ *
+ * @return the width of this range.
+ */
+ public int width() {
+ if (isEmpty()) {
+ return 0;
+ } else {
+ return upper - lower;
+ }
+ }
+
+ /**
+ * Returns the value in the middle of this range.
+ *
+ * @return the value in the middle of this range.
+ */
+ public int middleValue() {
+ if (isEmpty()) {
+ return Integer.MIN_VALUE;
+ } else {
+ return lower + width() / 2;
+ }
+ }
+
+ /**
+ * Returns a new range which is the union of this range and the other. The
+ * union range is the smallest range which includes all the elements of both
+ * initial ranges.
+ *
+ * @param other
+ * the other range.
+ * @return the union of this range and <code>other</code>.
+ */
+ public Range union(Range other) {
+ final Range result;
+ if (this.isEmpty()) {
+ result = other;
+ } else if (other.isEmpty()) {
+ result = this;
+ } else {
+ result = new Range(Math.min(this.lower, other.lower), Math.max(this.upper, other.upper));
+ }
+ return result;
+ }
+
+ /**
+ * Returns the intersection of this range and another one.
+ *
+ * @param other
+ * the other range.
+ * @return the intersection of this range and <code>other</code>.
+ */
+ public Range intersection(Range other) {
+ Range result;
+ if (this.isEmpty() || other.isEmpty()) {
+ result = Range.emptyRange();
+ } else {
+ int l = Math.max(this.lower, other.lower);
+ int u = Math.min(this.upper, other.upper);
+ if (l <= u) {
+ result = new Range(l, u);
+ } else {
+ result = Range.emptyRange();
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Tests whether or not this range intersects with another one.
+ *
+ * @param other
+ * the other range.
+ * @return <code>true</code> if this range intersects with
+ * <code>other</code>.
+ */
+ public boolean intersects(Range other) {
+ return !intersection(other).isEmpty();
+ }
+
+ /**
+ * Tests whether the specified range is included inside this one.
+ *
+ * @param other
+ * the other range to test for inclusion.
+ * @return <code>true</code> if <code>other</code> is included in this
+ * range.
+ */
+ public boolean includes(Range other) {
+ return this.intersection(other).equals(other);
+ }
+
+ /**
+ * Tests whether only one bound of the specified range is included inside
+ * this one.
+ *
+ * @param other
+ * the other range to test for inclusion.
+ * @return <code>true</code> if <code>other</code> is included in this
+ * range.
+ */
+ public boolean includesOneBoundOnly(Range other) {
+ return (this.includes(other.getLowerBound()) && !this.includes(other.getUpperBound())) || (!this.includes(other.getLowerBound()) && this.includes(other.getUpperBound()));
+ }
+
+ /**
+ * Tests whether only one bound of the specified range is included inside
+ * this one.
+ *
+ * @param other
+ * the other range to test for inclusion.
+ * @return <code>true</code> if <code>other</code> is included in this
+ * range.
+ */
+ public boolean includesAtLeastOneBound(Range other) {
+ return includes(other) || includesOneBoundOnly(other);
+ }
+
+ /**
+ * Returns a new range corresponding to this range shifted by the specified
+ * distance (which can be positive or negative). Shifting an empty range
+ * produces an empty range. Otherwise the resulting range has the same width
+ * as the original.
+ *
+ * @param distance
+ * the distance to shift this range.
+ * @return the new shifted range.
+ */
+ public Range shifted(int distance) {
+ if (isEmpty()) {
+ return Range.EMPTY_RANGE;
+ } else {
+ return new Range(lower + distance, upper + distance);
+ }
+ }
+
+ /**
+ * Returns a new range corresponding to this range grown by the specified
+ * distance (which must be positive). Growing an empty range produces an
+ * empty range. Otherwise the resulting range has a bigger width than
+ * original (+ 2 * distance). The middle value of this range is kept
+ * constant.
+ *
+ * @param distance
+ * the positive distance to grow this range.
+ * @return the new grown range.
+ */
+ public Range grown(int distance) {
+ Preconditions.checkArgument(distance >= 0);
+ if (isEmpty()) {
+ return Range.EMPTY_RANGE;
+ } else {
+ return new Range(lower - distance, upper + distance);
+ }
+ }
+
+ /**
+ * Returns a new range corresponding to this range shrinked by the specified
+ * distance (which must be positive). Shrinking an empty range produces an
+ * empty range. Otherwise the resulting range has a smaller width than
+ * original (- 2 * distance). The middle value of this range is kept
+ * constant.
+ *
+ * @param distance
+ * the positive distance to shrink this range.
+ * @return the new grown range.
+ */
+ public Range shrinked(int distance) {
+ Preconditions.checkArgument(distance >= 0);
+ if (isEmpty()) {
+ return Range.EMPTY_RANGE;
+ } else {
+ Preconditions.checkArgument(width() - 2 * distance >= 0);
+ return new Range(lower + distance, upper - distance);
+ }
+ }
+
+ /**
+ * Returns a percentage of how much the given absolute number is inside this
+ * range.
+ *
+ * @param n
+ * an integer.
+ * @return a number between 0.0 and 1.0 representing how much <code>n</code>
+ * is inside this range (0.0 meaning n is the range's lower bound or
+ * below, 1.0 meaning n is the range's upper bound or above), or
+ * Double.NaN if this range is empty.
+ */
+ public double getProportionalLocation(int n) {
+ double result = Double.NaN;
+ if (!this.isEmpty()) {
+ double offset = this.clamp(n) - this.getLowerBound();
+ double width = this.width();
+ if (width != 0.0) {
+ result = offset / width;
+ } else {
+ result = 0.0;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Validates that this range bounds does not match any bounds of the other
+ * range.
+ *
+ * @param other
+ * the other range.
+ * @return if this range bounds does not match any bounds of the other
+ * range.
+ */
+ public boolean validatesBoundsAreDifferent(Range other) {
+ return getLowerBound() != other.getLowerBound() && getLowerBound() != other.getUpperBound() && getUpperBound() != other.getLowerBound() && getUpperBound() != other.getUpperBound();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ if (!isEmpty()) {
+ return "[" + lower + ", " + upper + "]";
+ } else {
+ return "[<empty range>]";
+ }
+ }
+}

Back to the top