From 6d0204cafe8c95acd72728b3d1be616d87a31db1 Mon Sep 17 00:00:00 2001 From: Teemu Suo-Anttila Date: Wed, 31 Aug 2016 16:03:20 +0300 Subject: Add simple data change handling for Selects Change-Id: I16f9577ea4091fb4febe167d76e141b5945f53ab --- shared/src/main/java/com/vaadin/shared/Range.java | 439 +++++++++++++++++++++ .../main/java/com/vaadin/shared/ui/grid/Range.java | 439 --------------------- .../java/com/vaadin/shared/ui/grid/RangeTest.java | 2 + 3 files changed, 441 insertions(+), 439 deletions(-) create mode 100644 shared/src/main/java/com/vaadin/shared/Range.java delete mode 100644 shared/src/main/java/com/vaadin/shared/ui/grid/Range.java (limited to 'shared') diff --git a/shared/src/main/java/com/vaadin/shared/Range.java b/shared/src/main/java/com/vaadin/shared/Range.java new file mode 100644 index 0000000000..98d9ba54e2 --- /dev/null +++ b/shared/src/main/java/com/vaadin/shared/Range.java @@ -0,0 +1,439 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.shared; + +import java.io.Serializable; + +/** + * An immutable representation of a range, marked by start and end points. + *

+ * The range is treated as inclusive at the start, and exclusive at the end. + * I.e. the range [0..1[ has the length 1, and represents one integer: 0. + *

+ * The range is considered {@link #isEmpty() empty} if the start is the same as + * the end. + * + * @since 7.4 + * @author Vaadin Ltd + */ +public final class Range implements Serializable { + private final int start; + private final int end; + + /** + * Creates a range object representing a single integer. + * + * @param integer + * the number to represent as a range + * @return the range represented by integer + */ + public static Range withOnly(final int integer) { + return new Range(integer, integer + 1); + } + + /** + * Creates a range between two integers. + *

+ * The range start is inclusive and the end is exclusive. + * So, a range "between" 0 and 5 represents the numbers 0, 1, 2, 3 and 4, + * but not 5. + * + * @param start + * the start of the the range, inclusive + * @param end + * the end of the range, exclusive + * @return a range representing [start..end[ + * @throws IllegalArgumentException + * if start > end + */ + public static Range between(final int start, final int end) + throws IllegalArgumentException { + return new Range(start, end); + } + + /** + * Creates a range from a start point, with a given length. + * + * @param start + * the first integer to include in the range + * @param length + * the length of the resulting range + * @return a range starting from start, with + * length number of integers following + * @throws IllegalArgumentException + * if length < 0 + */ + public static Range withLength(final int start, final int length) + throws IllegalArgumentException { + if (length < 0) { + /* + * The constructor of Range will throw an exception if start > + * start+length (i.e. if length is negative). We're throwing the + * same exception type, just with a more descriptive message. + */ + throw new IllegalArgumentException("length must not be negative"); + } + return new Range(start, start + length); + } + + /** + * Creates a new range between two numbers: [start..end[. + * + * @param start + * the start integer, inclusive + * @param end + * the end integer, exclusive + * @throws IllegalArgumentException + * if start > end + */ + private Range(final int start, final int end) + throws IllegalArgumentException { + if (start > end) { + throw new IllegalArgumentException( + "start must not be greater than end"); + } + + this.start = start; + this.end = end; + } + + /** + * Returns the inclusive start point of this range. + * + * @return the start point of this range + */ + public int getStart() { + return start; + } + + /** + * Returns the exclusive end point of this range. + * + * @return the end point of this range + */ + public int getEnd() { + return end; + } + + /** + * The number of integers contained in the range. + * + * @return the number of integers contained in the range + */ + public int length() { + return getEnd() - getStart(); + } + + /** + * Checks whether the range has no elements between the start and end. + * + * @return true iff the range contains no elements. + */ + public boolean isEmpty() { + return getStart() >= getEnd(); + } + + /** + * Checks whether this range and another range are at least partially + * covering the same values. + * + * @param other + * the other range to check against + * @return true if this and other intersect + */ + public boolean intersects(final Range other) { + return getStart() < other.getEnd() && other.getStart() < getEnd(); + } + + /** + * Checks whether an integer is found within this range. + * + * @param integer + * an integer to test for presence in this range + * @return true iff integer is in this range + */ + public boolean contains(final int integer) { + return getStart() <= integer && integer < getEnd(); + } + + /** + * Checks whether this range is a subset of another range. + * + * @return true iff other completely wraps this + * range + */ + public boolean isSubsetOf(final Range other) { + if (isEmpty() && other.isEmpty()) { + return true; + } + + return other.getStart() <= getStart() && getEnd() <= other.getEnd(); + } + + /** + * Overlay this range with another one, and partition the ranges according + * to how they position relative to each other. + *

+ * The three partitions are returned as a three-element Range array: + *

+ * + * @param other + * the other range to act as delimiters. + * @return a three-element Range array of partitions depicting the elements + * before (index 0), shared/inside (index 1) and after (index 2). + */ + public Range[] partitionWith(final Range other) { + final Range[] splitBefore = splitAt(other.getStart()); + final Range rangeBefore = splitBefore[0]; + final Range[] splitAfter = splitBefore[1].splitAt(other.getEnd()); + final Range rangeInside = splitAfter[0]; + final Range rangeAfter = splitAfter[1]; + return new Range[] { rangeBefore, rangeInside, rangeAfter }; + } + + /** + * Get a range that is based on this one, but offset by a number. + * + * @param offset + * the number to offset by + * @return a copy of this range, offset by offset + */ + public Range offsetBy(final int offset) { + if (offset == 0) { + return this; + } else { + return new Range(start + offset, end + offset); + } + } + + @Override + public String toString() { + return getClass().getSimpleName() + " [" + getStart() + ".." + getEnd() + + "[" + (isEmpty() ? " (empty)" : ""); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + end; + result = prime * result + start; + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Range other = (Range) obj; + if (end != other.end) { + return false; + } + if (start != other.start) { + return false; + } + return true; + } + + /** + * Checks whether this range starts before the start of another range. + * + * @param other + * the other range to compare against + * @return true iff this range starts before the + * other + */ + public boolean startsBefore(final Range other) { + return getStart() < other.getStart(); + } + + /** + * Checks whether this range ends before the start of another range. + * + * @param other + * the other range to compare against + * @return true iff this range ends before the + * other + */ + public boolean endsBefore(final Range other) { + return getEnd() <= other.getStart(); + } + + /** + * Checks whether this range ends after the end of another range. + * + * @param other + * the other range to compare against + * @return true iff this range ends after the + * other + */ + public boolean endsAfter(final Range other) { + return getEnd() > other.getEnd(); + } + + /** + * Checks whether this range starts after the end of another range. + * + * @param other + * the other range to compare against + * @return true iff this range starts after the + * other + */ + public boolean startsAfter(final Range other) { + return getStart() >= other.getEnd(); + } + + /** + * Split the range into two at a certain integer. + *

+ * Example: [5..10[.splitAt(7) == [5..7[, [7..10[ + * + * @param integer + * the integer at which to split the range into two + * @return an array of two ranges, with [start..integer[ in the + * first element, and [integer..end[ in the second + * element. + *

+ * If {@code integer} is less than {@code start}, [empty, + * {@code this} ] is returned. if integer is equal to + * or greater than {@code end}, [{@code this}, empty] is returned + * instead. + */ + public Range[] splitAt(final int integer) { + if (integer < start) { + return new Range[] { Range.withLength(start, 0), this }; + } else if (integer >= end) { + return new Range[] { this, Range.withLength(end, 0) }; + } else { + return new Range[] { new Range(start, integer), + new Range(integer, end) }; + } + } + + /** + * Split the range into two after a certain number of integers into the + * range. + *

+ * Calling this method is equivalent to calling + * {@link #splitAt(int) splitAt}({@link #getStart()}+length); + *

+ * Example: + * [5..10[.splitAtFromStart(2) == [5..7[, [7..10[ + * + * @param length + * the length at which to split this range into two + * @return an array of two ranges, having the length-first + * elements of this range, and the second range having the rest. If + * length ≤ 0, the first element will be empty, and + * the second element will be this range. If length + * ≥ {@link #length()}, the first element will be this range, + * and the second element will be empty. + */ + public Range[] splitAtFromStart(final int length) { + return splitAt(getStart() + length); + } + + /** + * Combines two ranges to create a range containing all values in both + * ranges, provided there are no gaps between the ranges. + * + * @param other + * the range to combine with this range + * + * @return the combined range + * + * @throws IllegalArgumentException + * if the two ranges aren't connected + */ + public Range combineWith(Range other) throws IllegalArgumentException { + if (getStart() > other.getEnd() || other.getStart() > getEnd()) { + throw new IllegalArgumentException( + "There is a gap between " + this + " and " + other); + } + + return Range.between(Math.min(getStart(), other.getStart()), + Math.max(getEnd(), other.getEnd())); + } + + /** + * Creates a range that is expanded the given amounts in both ends. + * + * @param startDelta + * the amount to expand by in the beginning of the range + * @param endDelta + * the amount to expand by in the end of the range + * + * @return an expanded range + * + * @throws IllegalArgumentException + * if the new range would have start > end + */ + public Range expand(int startDelta, int endDelta) + throws IllegalArgumentException { + return Range.between(getStart() - startDelta, getEnd() + endDelta); + } + + /** + * Limits this range to be within the bounds of the provided range. + *

+ * This is basically an optimized way of calculating + * {@link #partitionWith(Range)}[1] without the overhead of + * defining the parts that do not overlap. + *

+ * If the two ranges do not intersect, an empty range is returned. There are + * no guarantees about the position of that range. + * + * @param bounds + * the bounds that the returned range should be limited to + * @return a bounded range + */ + public Range restrictTo(Range bounds) { + boolean startWithin = bounds.contains(getStart()); + boolean endWithin = bounds.contains(getEnd()); + boolean boundsWithin = getStart() < bounds.getStart() + && getEnd() >= bounds.getEnd(); + + if (startWithin) { + if (endWithin) { + return this; + } else { + return Range.between(getStart(), bounds.getEnd()); + } + } else { + if (endWithin) { + return Range.between(bounds.getStart(), getEnd()); + } else if (boundsWithin) { + return bounds; + } else { + return Range.withLength(getStart(), 0); + } + } + } +} diff --git a/shared/src/main/java/com/vaadin/shared/ui/grid/Range.java b/shared/src/main/java/com/vaadin/shared/ui/grid/Range.java deleted file mode 100644 index 93805e4c0b..0000000000 --- a/shared/src/main/java/com/vaadin/shared/ui/grid/Range.java +++ /dev/null @@ -1,439 +0,0 @@ -/* - * Copyright 2000-2016 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.vaadin.shared.ui.grid; - -import java.io.Serializable; - -/** - * An immutable representation of a range, marked by start and end points. - *

- * The range is treated as inclusive at the start, and exclusive at the end. - * I.e. the range [0..1[ has the length 1, and represents one integer: 0. - *

- * The range is considered {@link #isEmpty() empty} if the start is the same as - * the end. - * - * @since 7.4 - * @author Vaadin Ltd - */ -public final class Range implements Serializable { - private final int start; - private final int end; - - /** - * Creates a range object representing a single integer. - * - * @param integer - * the number to represent as a range - * @return the range represented by integer - */ - public static Range withOnly(final int integer) { - return new Range(integer, integer + 1); - } - - /** - * Creates a range between two integers. - *

- * The range start is inclusive and the end is exclusive. - * So, a range "between" 0 and 5 represents the numbers 0, 1, 2, 3 and 4, - * but not 5. - * - * @param start - * the start of the the range, inclusive - * @param end - * the end of the range, exclusive - * @return a range representing [start..end[ - * @throws IllegalArgumentException - * if start > end - */ - public static Range between(final int start, final int end) - throws IllegalArgumentException { - return new Range(start, end); - } - - /** - * Creates a range from a start point, with a given length. - * - * @param start - * the first integer to include in the range - * @param length - * the length of the resulting range - * @return a range starting from start, with - * length number of integers following - * @throws IllegalArgumentException - * if length < 0 - */ - public static Range withLength(final int start, final int length) - throws IllegalArgumentException { - if (length < 0) { - /* - * The constructor of Range will throw an exception if start > - * start+length (i.e. if length is negative). We're throwing the - * same exception type, just with a more descriptive message. - */ - throw new IllegalArgumentException("length must not be negative"); - } - return new Range(start, start + length); - } - - /** - * Creates a new range between two numbers: [start..end[. - * - * @param start - * the start integer, inclusive - * @param end - * the end integer, exclusive - * @throws IllegalArgumentException - * if start > end - */ - private Range(final int start, final int end) - throws IllegalArgumentException { - if (start > end) { - throw new IllegalArgumentException( - "start must not be greater than end"); - } - - this.start = start; - this.end = end; - } - - /** - * Returns the inclusive start point of this range. - * - * @return the start point of this range - */ - public int getStart() { - return start; - } - - /** - * Returns the exclusive end point of this range. - * - * @return the end point of this range - */ - public int getEnd() { - return end; - } - - /** - * The number of integers contained in the range. - * - * @return the number of integers contained in the range - */ - public int length() { - return getEnd() - getStart(); - } - - /** - * Checks whether the range has no elements between the start and end. - * - * @return true iff the range contains no elements. - */ - public boolean isEmpty() { - return getStart() >= getEnd(); - } - - /** - * Checks whether this range and another range are at least partially - * covering the same values. - * - * @param other - * the other range to check against - * @return true if this and other intersect - */ - public boolean intersects(final Range other) { - return getStart() < other.getEnd() && other.getStart() < getEnd(); - } - - /** - * Checks whether an integer is found within this range. - * - * @param integer - * an integer to test for presence in this range - * @return true iff integer is in this range - */ - public boolean contains(final int integer) { - return getStart() <= integer && integer < getEnd(); - } - - /** - * Checks whether this range is a subset of another range. - * - * @return true iff other completely wraps this - * range - */ - public boolean isSubsetOf(final Range other) { - if (isEmpty() && other.isEmpty()) { - return true; - } - - return other.getStart() <= getStart() && getEnd() <= other.getEnd(); - } - - /** - * Overlay this range with another one, and partition the ranges according - * to how they position relative to each other. - *

- * The three partitions are returned as a three-element Range array: - *

- * - * @param other - * the other range to act as delimiters. - * @return a three-element Range array of partitions depicting the elements - * before (index 0), shared/inside (index 1) and after (index 2). - */ - public Range[] partitionWith(final Range other) { - final Range[] splitBefore = splitAt(other.getStart()); - final Range rangeBefore = splitBefore[0]; - final Range[] splitAfter = splitBefore[1].splitAt(other.getEnd()); - final Range rangeInside = splitAfter[0]; - final Range rangeAfter = splitAfter[1]; - return new Range[] { rangeBefore, rangeInside, rangeAfter }; - } - - /** - * Get a range that is based on this one, but offset by a number. - * - * @param offset - * the number to offset by - * @return a copy of this range, offset by offset - */ - public Range offsetBy(final int offset) { - if (offset == 0) { - return this; - } else { - return new Range(start + offset, end + offset); - } - } - - @Override - public String toString() { - return getClass().getSimpleName() + " [" + getStart() + ".." + getEnd() - + "[" + (isEmpty() ? " (empty)" : ""); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + end; - result = prime * result + start; - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final Range other = (Range) obj; - if (end != other.end) { - return false; - } - if (start != other.start) { - return false; - } - return true; - } - - /** - * Checks whether this range starts before the start of another range. - * - * @param other - * the other range to compare against - * @return true iff this range starts before the - * other - */ - public boolean startsBefore(final Range other) { - return getStart() < other.getStart(); - } - - /** - * Checks whether this range ends before the start of another range. - * - * @param other - * the other range to compare against - * @return true iff this range ends before the - * other - */ - public boolean endsBefore(final Range other) { - return getEnd() <= other.getStart(); - } - - /** - * Checks whether this range ends after the end of another range. - * - * @param other - * the other range to compare against - * @return true iff this range ends after the - * other - */ - public boolean endsAfter(final Range other) { - return getEnd() > other.getEnd(); - } - - /** - * Checks whether this range starts after the end of another range. - * - * @param other - * the other range to compare against - * @return true iff this range starts after the - * other - */ - public boolean startsAfter(final Range other) { - return getStart() >= other.getEnd(); - } - - /** - * Split the range into two at a certain integer. - *

- * Example: [5..10[.splitAt(7) == [5..7[, [7..10[ - * - * @param integer - * the integer at which to split the range into two - * @return an array of two ranges, with [start..integer[ in the - * first element, and [integer..end[ in the second - * element. - *

- * If {@code integer} is less than {@code start}, [empty, - * {@code this} ] is returned. if integer is equal to - * or greater than {@code end}, [{@code this}, empty] is returned - * instead. - */ - public Range[] splitAt(final int integer) { - if (integer < start) { - return new Range[] { Range.withLength(start, 0), this }; - } else if (integer >= end) { - return new Range[] { this, Range.withLength(end, 0) }; - } else { - return new Range[] { new Range(start, integer), - new Range(integer, end) }; - } - } - - /** - * Split the range into two after a certain number of integers into the - * range. - *

- * Calling this method is equivalent to calling - * {@link #splitAt(int) splitAt}({@link #getStart()}+length); - *

- * Example: - * [5..10[.splitAtFromStart(2) == [5..7[, [7..10[ - * - * @param length - * the length at which to split this range into two - * @return an array of two ranges, having the length-first - * elements of this range, and the second range having the rest. If - * length ≤ 0, the first element will be empty, and - * the second element will be this range. If length - * ≥ {@link #length()}, the first element will be this range, - * and the second element will be empty. - */ - public Range[] splitAtFromStart(final int length) { - return splitAt(getStart() + length); - } - - /** - * Combines two ranges to create a range containing all values in both - * ranges, provided there are no gaps between the ranges. - * - * @param other - * the range to combine with this range - * - * @return the combined range - * - * @throws IllegalArgumentException - * if the two ranges aren't connected - */ - public Range combineWith(Range other) throws IllegalArgumentException { - if (getStart() > other.getEnd() || other.getStart() > getEnd()) { - throw new IllegalArgumentException( - "There is a gap between " + this + " and " + other); - } - - return Range.between(Math.min(getStart(), other.getStart()), - Math.max(getEnd(), other.getEnd())); - } - - /** - * Creates a range that is expanded the given amounts in both ends. - * - * @param startDelta - * the amount to expand by in the beginning of the range - * @param endDelta - * the amount to expand by in the end of the range - * - * @return an expanded range - * - * @throws IllegalArgumentException - * if the new range would have start > end - */ - public Range expand(int startDelta, int endDelta) - throws IllegalArgumentException { - return Range.between(getStart() - startDelta, getEnd() + endDelta); - } - - /** - * Limits this range to be within the bounds of the provided range. - *

- * This is basically an optimized way of calculating - * {@link #partitionWith(Range)}[1] without the overhead of - * defining the parts that do not overlap. - *

- * If the two ranges do not intersect, an empty range is returned. There are - * no guarantees about the position of that range. - * - * @param bounds - * the bounds that the returned range should be limited to - * @return a bounded range - */ - public Range restrictTo(Range bounds) { - boolean startWithin = bounds.contains(getStart()); - boolean endWithin = bounds.contains(getEnd()); - boolean boundsWithin = getStart() < bounds.getStart() - && getEnd() >= bounds.getEnd(); - - if (startWithin) { - if (endWithin) { - return this; - } else { - return Range.between(getStart(), bounds.getEnd()); - } - } else { - if (endWithin) { - return Range.between(bounds.getStart(), getEnd()); - } else if (boundsWithin) { - return bounds; - } else { - return Range.withLength(getStart(), 0); - } - } - } -} diff --git a/shared/src/test/java/com/vaadin/shared/ui/grid/RangeTest.java b/shared/src/test/java/com/vaadin/shared/ui/grid/RangeTest.java index bde581022b..921edf145e 100644 --- a/shared/src/test/java/com/vaadin/shared/ui/grid/RangeTest.java +++ b/shared/src/test/java/com/vaadin/shared/ui/grid/RangeTest.java @@ -20,6 +20,8 @@ import static org.junit.Assert.assertTrue; import org.junit.Test; +import com.vaadin.shared.Range; + @SuppressWarnings("static-method") public class RangeTest { -- cgit v1.2.3