summaryrefslogtreecommitdiffstats
path: root/server/src/com/vaadin/data
diff options
context:
space:
mode:
authorHenri Sara <hesara@vaadin.com>2012-08-31 13:29:42 +0300
committerHenri Sara <hesara@vaadin.com>2012-08-31 14:09:58 +0300
commit01646b14df5708e50e4eb031ec5b4f0da4ab5d15 (patch)
treeaea2f654ed4f0ddca0f61708d1a3be8edb09deaf /server/src/com/vaadin/data
parent61a1218d388b85b496e0fd98f2a5c5226ab4f83d (diff)
downloadvaadin-framework-01646b14df5708e50e4eb031ec5b4f0da4ab5d15.tar.gz
vaadin-framework-01646b14df5708e50e4eb031ec5b4f0da4ab5d15.zip
Add Container.Indexed.getItemIds(int, int) for a range of items (#8028)
This permits optimization of data fetches from various containers where getting single items by index in a loop might be costly. Also add a helper method in ContainerHelpers to make it easier to implement the new method where performance of fetching an id by index is not an issue. SQLContainer still uses this helper instead of an optimized implementation.
Diffstat (limited to 'server/src/com/vaadin/data')
-rw-r--r--server/src/com/vaadin/data/Container.java54
-rw-r--r--server/src/com/vaadin/data/ContainerHelpers.java91
-rw-r--r--server/src/com/vaadin/data/RangeOutOfContainerBoundsException.java169
-rw-r--r--server/src/com/vaadin/data/util/AbstractInMemoryContainer.java40
-rw-r--r--server/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java14
5 files changed, 362 insertions, 6 deletions
diff --git a/server/src/com/vaadin/data/Container.java b/server/src/com/vaadin/data/Container.java
index 155dde87ef..28d6cad18d 100644
--- a/server/src/com/vaadin/data/Container.java
+++ b/server/src/com/vaadin/data/Container.java
@@ -18,6 +18,7 @@ package com.vaadin.data;
import java.io.Serializable;
import java.util.Collection;
+import java.util.List;
import com.vaadin.data.util.filter.SimpleStringFilter;
import com.vaadin.data.util.filter.UnsupportedFilterException;
@@ -484,16 +485,61 @@ public interface Container extends Serializable {
public int indexOfId(Object itemId);
/**
- * Gets the ID of an Item by an index number.
+ * Get the item id for the item at the position given by
+ * <code>index</code>. <br>
+ * <br>
+ * <b>Throws:</b> {@link IndexOutOfBoundsException} if
+ * <code>index</code> is outside the range of the container. (i.e.
+ * <code>index &lt; 0 || container.size()-1 &lt; index</code>)
*
* @param index
- * Index of the requested id in (the filtered and sorted view
- * of) the Container
- * @return ID of the Item in the given index
+ * the index of the requested item id
+ * @return the item id of the item at the given index
*/
public Object getIdByIndex(int index);
/**
+ * Get <code>numberOfItems</code> consecutive item ids from the
+ * container, starting with the item id at <code>startIndex</code>. <br>
+ * <br>
+ *
+ * Implementations should return the exact number of item ids given by
+ * <code>numberOfItems</code>. The returned list must hence contain all
+ * of the item ids from the range: <br>
+ * <br>
+ * <code>startIndex</code> to
+ * <code>startIndex + (numberOfItems-1)</code>. <br>
+ * <br>
+ *
+ * The returned list must contain all of the requested item ids or throw
+ * a {@link RangeOutOfContainerBoundsException} to indicate that the
+ * container does not contain all the requested item ids.<br>
+ * <br>
+ * For quick migration to new API see:
+ * {@link ContainerHelpers#getItemIdsUsingGetIdByIndex(int, int, Indexed)}
+ * . <br>
+ * <br>
+ * <b>Throws:</b> {@link IllegalArgumentException} if
+ * <code>numberOfItems</code> is < 0 <br>
+ * <b>Throws:</b> {@link RangeOutOfContainerBoundsException} if all of
+ * the requested item ids cannot be fetched <br>
+ * <b>Throws:</b> {@link IndexOutOfBoundsException} if
+ * <code>startIndex</code> is outside the range of the container. (i.e.
+ * <code>startIndex &lt; 0 || container.size()-1 &lt; startIndex</code>)
+ *
+ * @param startIndex
+ * the index for the first item which id to include
+ * @param numberOfItems
+ * the number of consecutive item ids to get from the given
+ * start index, must be >= 0
+ * @return List containing all of the requested item ids or empty list
+ * if <code>numberOfItems</code> == 0; not null
+ *
+ * @since 7.0
+ */
+ public List<?> getItemIds(int startIndex, int numberOfItems);
+
+ /**
* Adds a new item at given index (in the filtered view).
* <p>
* The indices of the item currently in the given position and all the
diff --git a/server/src/com/vaadin/data/ContainerHelpers.java b/server/src/com/vaadin/data/ContainerHelpers.java
new file mode 100644
index 0000000000..9ec2da4362
--- /dev/null
+++ b/server/src/com/vaadin/data/ContainerHelpers.java
@@ -0,0 +1,91 @@
+package com.vaadin.data;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import com.vaadin.data.Container.Indexed;
+
+/**
+ * Contains helper methods for containers that can be used to ease development
+ * of containers in Vaadin.
+ *
+ * @since 7.0
+ */
+public class ContainerHelpers {
+
+ /**
+ * Get a range of item ids from the container using
+ * {@link Indexed#getIdByIndex(int)}. This is just a helper method to aid
+ * developers to quickly add the required functionality to a Container
+ * during development. This should not be used in a "finished product"
+ * unless fetching an id for an index is very inexpensive because a separate
+ * request will be performed for each index in the range.
+ *
+ * @param startIndex
+ * index of the first item id to get
+ * @param numberOfIds
+ * the number of consecutive items whose ids should be returned
+ * @param container
+ * the container from which the items should be fetched
+ * @return A list of item ids in the range specified
+ */
+ public static List<?> getItemIdsUsingGetIdByIndex(int startIndex,
+ int numberOfIds, Container.Indexed container) {
+
+ if (container == null) {
+ throw new IllegalArgumentException(
+ "The given container cannot be null!");
+ }
+
+ if (startIndex < 0) {
+ throw new IndexOutOfBoundsException(
+ "Start index cannot be negative! startIndex=" + startIndex);
+ }
+
+ if (startIndex > container.size()) {
+ throw new IndexOutOfBoundsException(
+ "Start index exceeds container size! startIndex="
+ + startIndex + " containerLastItemIndex="
+ + (container.size() - 1));
+ }
+
+ if (numberOfIds < 1) {
+ if (numberOfIds == 0) {
+ return Collections.emptyList();
+ }
+
+ throw new IllegalArgumentException(
+ "Cannot get negative amount of items! numberOfItems="
+ + numberOfIds);
+ }
+
+ // not included in the range
+ int endIndex = startIndex + numberOfIds;
+
+ if (endIndex > container.size()) {
+ throw new RangeOutOfContainerBoundsException(
+ "Cannot get all requested item ids from container. "
+ + "Container size might have changed, recalculate numberOfIds "
+ + "based on the actual container size!",
+ startIndex, numberOfIds, container.size());
+ }
+
+ ArrayList<Object> rangeOfIds = new ArrayList<Object>();
+ for (int i = startIndex; i < endIndex; i++) {
+ Object idByIndex = container.getIdByIndex(i);
+ if (idByIndex == null) {
+ throw new RuntimeException(
+ "Unable to get item id for index: "
+ + i
+ + " from container using Container.Indexed#getIdByIndex() "
+ + "even though container.size() > endIndex. "
+ + "Returned item id was null. "
+ + "Check your container implementation!");
+ }
+ rangeOfIds.add(idByIndex);
+ }
+
+ return Collections.unmodifiableList(rangeOfIds);
+ }
+}
diff --git a/server/src/com/vaadin/data/RangeOutOfContainerBoundsException.java b/server/src/com/vaadin/data/RangeOutOfContainerBoundsException.java
new file mode 100644
index 0000000000..43058e45d8
--- /dev/null
+++ b/server/src/com/vaadin/data/RangeOutOfContainerBoundsException.java
@@ -0,0 +1,169 @@
+package com.vaadin.data;
+
+/**
+ * A exception that indicates that the container is unable to return all of the
+ * consecutive item ids requested by the caller. This can happen if the
+ * container size has changed since the input parameters for
+ * {@link Container.Indexed#getItemIds(int, int)} were computed or if the
+ * requested range exceeds the containers size due to some other factor.<br>
+ * <br>
+ *
+ * The exception can contain additional parameters for easier debugging. The
+ * additional parameters are the <code>startIndex</code> and
+ * <code>numberOfIds</code> which were given to
+ * {@link Container.Indexed#getItemIds(int, int)} as well as the size of the
+ * container when the fetch was executed. The container size can be retrieved
+ * with {@link #getContainerCurrentSize()}. <br>
+ * <br>
+ *
+ * The additional parameters are optional but the party that received the
+ * exception can check whether or not these were set by calling
+ * {@link #isAdditionalParametersSet()}.
+ *
+ * @since 7.0
+ */
+public class RangeOutOfContainerBoundsException extends RuntimeException {
+
+ private final int startIndex;
+ private final int numberOfIds;
+ private final int containerCurrentSize;
+ private final boolean additionalParametersSet;
+
+ // Discourage users to create exceptions without at least some kind of
+ // message...
+ private RangeOutOfContainerBoundsException() {
+ super();
+ startIndex = -1;
+ numberOfIds = -1;
+ containerCurrentSize = -1;
+ additionalParametersSet = false;
+ }
+
+ public RangeOutOfContainerBoundsException(String message) {
+ super(message);
+ startIndex = -1;
+ numberOfIds = -1;
+ containerCurrentSize = -1;
+ additionalParametersSet = false;
+ }
+
+ public RangeOutOfContainerBoundsException(String message,
+ Throwable throwable) {
+ super(message, throwable);
+ startIndex = -1;
+ numberOfIds = -1;
+ containerCurrentSize = -1;
+ additionalParametersSet = false;
+ }
+
+ public RangeOutOfContainerBoundsException(Throwable throwable) {
+ super(throwable);
+ startIndex = -1;
+ numberOfIds = -1;
+ containerCurrentSize = -1;
+ additionalParametersSet = false;
+ }
+
+ /**
+ * Create a new {@link RangeOutOfContainerBoundsException} with the
+ * additional parameters:
+ * <ul>
+ * <li>startIndex - start index for the query</li>
+ * <li>numberOfIds - the number of consecutive item ids to get</li>
+ * <li>containerCurrentSize - the size of the container during the execution
+ * of the query</li>
+ * </ul>
+ * given.
+ *
+ * @param message
+ * @param startIndex
+ * the given startIndex for the query
+ * @param numberOfIds
+ * the number of item ids requested
+ * @param containerCurrentSize
+ * the current size of the container
+ */
+ public RangeOutOfContainerBoundsException(String message, int startIndex,
+ int numberOfIds, int containerCurrentSize) {
+ super(message);
+
+ this.startIndex = startIndex;
+ this.numberOfIds = numberOfIds;
+ this.containerCurrentSize = containerCurrentSize;
+ additionalParametersSet = true;
+ }
+
+ /**
+ * Create a new {@link RangeOutOfContainerBoundsException} with the given
+ * query parameters set in the exception along with the containers current
+ * size and a @link {@link Throwable}.
+ *
+ * @param message
+ * @param startIndex
+ * the given startIndex for the query
+ * @param numberOfIds
+ * the number of item ids queried for
+ * @param containerCurrentSize
+ * the current size of the container
+ * @param throwable
+ */
+ public RangeOutOfContainerBoundsException(String message, int startIndex,
+ int numberOfIds, int containerCurrentSize, Throwable throwable) {
+ super(message, throwable);
+
+ this.startIndex = startIndex;
+ this.numberOfIds = numberOfIds;
+ this.containerCurrentSize = containerCurrentSize;
+ additionalParametersSet = true;
+ }
+
+ /**
+ * Get the given startIndex for the query. Remember to check if this
+ * parameter is set by calling {@link #isAdditionalParametersSet()}
+ *
+ * @return the startIndex given to the container
+ */
+ public int getStartIndex() {
+ return startIndex;
+ }
+
+ /**
+ * Get the number of item ids requested. Remember to check if this parameter
+ * is set with {@link #isAdditionalParametersSet()}
+ *
+ * @return the number of item ids the container was ordered to fetch
+ */
+ public int getNumberOfIds() {
+ return numberOfIds;
+ }
+
+ /**
+ * Get the container size when the query was actually executed. Remember to
+ * check if this parameter is set with {@link #isAdditionalParametersSet()}
+ */
+ public int getContainerCurrentSize() {
+ return containerCurrentSize;
+ }
+
+ /**
+ * Check whether or not the additional parameters for the exception were set
+ * during creation or not.
+ *
+ * The additional parameters can be retrieved with: <br>
+ * <ul>
+ * <li> {@link #getStartIndex()}</li>
+ * <li> {@link #getNumberOfIds()}</li>
+ * <li> {@link #getContainerCurrentSize()}</li>
+ * </ul>
+ *
+ * @return true if parameters are set, false otherwise.
+ *
+ * @see #RangeOutOfContainerBoundsException(String, int, int, int)
+ * RangeOutOfContainerBoundsException(String, int, int, int) for more
+ * details on the additional parameters
+ */
+ public boolean isAdditionalParametersSet() {
+ return additionalParametersSet;
+ }
+
+}
diff --git a/server/src/com/vaadin/data/util/AbstractInMemoryContainer.java b/server/src/com/vaadin/data/util/AbstractInMemoryContainer.java
index 6eea9cb421..dbfcad3982 100644
--- a/server/src/com/vaadin/data/util/AbstractInMemoryContainer.java
+++ b/server/src/com/vaadin/data/util/AbstractInMemoryContainer.java
@@ -26,6 +26,7 @@ import java.util.Set;
import com.vaadin.data.Container;
import com.vaadin.data.Container.ItemSetChangeNotifier;
import com.vaadin.data.Item;
+import com.vaadin.data.RangeOutOfContainerBoundsException;
import com.vaadin.data.util.filter.SimpleStringFilter;
import com.vaadin.data.util.filter.UnsupportedFilterException;
@@ -251,6 +252,45 @@ public abstract class AbstractInMemoryContainer<ITEMIDTYPE, PROPERTYIDCLASS, ITE
}
@Override
+ public List<ITEMIDTYPE> getItemIds(int startIndex, int numberOfIds) {
+ if (startIndex < 0) {
+ throw new IndexOutOfBoundsException(
+ "Start index cannot be negative! startIndex=" + startIndex);
+ }
+
+ if (startIndex > getVisibleItemIds().size()) {
+ throw new IndexOutOfBoundsException(
+ "Start index exceeds container size! startIndex="
+ + startIndex + " containerLastItemIndex="
+ + (getVisibleItemIds().size() - 1));
+ }
+
+ if (numberOfIds < 1) {
+ if (numberOfIds == 0) {
+ return Collections.emptyList();
+ }
+
+ throw new IllegalArgumentException(
+ "Cannot get negative amount of items! numberOfItems="
+ + numberOfIds);
+ }
+
+ int endIndex = startIndex + numberOfIds;
+
+ if (endIndex > getVisibleItemIds().size()) {
+ throw new RangeOutOfContainerBoundsException(
+ "Cannot get all requested item ids from container. "
+ + "Container size might have changed, recalculate numberOfIds "
+ + "based on the actual container size!",
+ startIndex, numberOfIds, getVisibleItemIds().size());
+ }
+
+ return Collections.unmodifiableList(getVisibleItemIds().subList(
+ startIndex, endIndex));
+
+ }
+
+ @Override
public int indexOfId(Object itemId) {
return getVisibleItemIds().indexOf(itemId);
}
diff --git a/server/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java b/server/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java
index a53f32b96e..7a63e8c6c2 100644
--- a/server/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java
+++ b/server/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java
@@ -33,6 +33,7 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import com.vaadin.data.Container;
+import com.vaadin.data.ContainerHelpers;
import com.vaadin.data.Item;
import com.vaadin.data.Property;
import com.vaadin.data.util.filter.Compare.Equal;
@@ -656,9 +657,11 @@ public class SQLContainer implements Container, Container.Filterable,
@Override
public Object getIdByIndex(int index) {
- if (index < 0 || index > size() - 1) {
- return null;
+ if (index < 0) {
+ throw new IndexOutOfBoundsException("Index is negative! index="
+ + index);
}
+
if (index < size) {
if (itemIndexes.keySet().contains(index)) {
return itemIndexes.get(index);
@@ -672,6 +675,13 @@ public class SQLContainer implements Container, Container.Filterable,
}
}
+ @Override
+ public List<Object> getItemIds(int startIndex, int numberOfIds) {
+ // TODO create a better implementation
+ return (List<Object>) ContainerHelpers.getItemIdsUsingGetIdByIndex(
+ startIndex, numberOfIds, this);
+ }
+
/**********************************************/
/** Methods from interface Container.Ordered **/
/**********************************************/