1 // Copyright 2009 Google Inc. All Rights Reserved.
2 package com
.google
.appengine
.api
.datastore
;
4 import java
.io
.Serializable
;
5 import java
.util
.Iterator
;
6 import java
.util
.NoSuchElementException
;
9 * Represents a range of unique datastore identifiers from
10 * {@code getStart().getId()} to {@code getEnd().getId()} inclusive.
11 * If an instance of this class is the result of a call to
12 * {@code DatastoreService.allocateIds()}, the {@link Key Keys} returned by
13 * this instance have been consumed in the datastore's id-space and are
14 * guaranteed never to be reused.
16 * This class can be used to construct {@link Entity Entities} with {@link
17 * Key Keys} that have specific id values without fear of the datastore
18 * creating new records with those same ids at a later date. This can be
19 * helpful as part of a data migration or large bulk upload where you may need
20 * to preserve existing ids and relationships between entities.
22 * This class is threadsafe but the {@link Iterator Iterators} returned by
23 * {@link #iterator()} are not.
26 public final class KeyRange
implements Iterable
<Key
>, Serializable
{
27 static final long serialVersionUID
= 962890261927141064L;
29 private final Key parent
;
30 private final String kind
;
31 private final Key start
;
32 private final Key end
;
33 private final AppIdNamespace appIdNamespace
;
35 public KeyRange(Key parent
, String kind
, long start
, long end
) {
36 this(parent
, kind
, start
, end
, DatastoreApiHelper
.getCurrentAppIdNamespace());
39 KeyRange(Key parent
, String kind
, long start
, long end
, AppIdNamespace appIdNamespace
) {
40 if (parent
!= null && !parent
.isComplete()) {
41 throw new IllegalArgumentException("Invalid parent: not a complete key");
44 if (kind
== null || kind
.isEmpty()) {
45 throw new IllegalArgumentException("Invalid kind: cannot be null or empty");
49 throw new IllegalArgumentException("Illegal start " + start
+ ": less than 1");
53 throw new IllegalArgumentException("Illegal end " + end
+ ": less than start " + start
);
58 this.appIdNamespace
= appIdNamespace
;
59 this.start
= KeyFactory
.createKey(parent
, kind
, start
, appIdNamespace
);
60 this.end
= KeyFactory
.createKey(parent
, kind
, end
, appIdNamespace
);
64 * This constructor exists for frameworks (e.g. Google Web Toolkit)
65 * that require it for serialization purposes. It should not be
68 @SuppressWarnings("unused")
74 appIdNamespace
= null;
77 AppIdNamespace
getAppIdNamespace() {
78 return appIdNamespace
;
82 * @return The parent {@link Key} of the range.
89 * @return The kind of the range.
96 * @return The first {@link Key} in the range.
98 public Key
getStart() {
103 * @return The last {@link Key} in the range.
105 public Key
getEnd() {
110 * @return The size of the range.
112 public long getSize() {
113 return end
.getId() - start
.getId() + 1;
117 public Iterator
<Key
> iterator() {
118 return new IdRangeIterator();
122 public boolean equals(Object obj
) {
123 if (!(obj
instanceof KeyRange
)) {
126 KeyRange that
= (KeyRange
) obj
;
128 return (this.start
.equals(that
.start
) && this.end
.equals(that
.end
));
132 public int hashCode() {
133 return 31 * start
.hashCode() + end
.hashCode();
137 * {@link Iterator} implementation that returns {@link Key Keys}
138 * in the range defined by the enclosing {@link KeyRange}.
140 private final class IdRangeIterator
implements Iterator
<Key
> {
141 private long next
= start
.getId();
144 public boolean hasNext() {
145 return next
<= end
.getId();
151 throw new NoSuchElementException();
153 return KeyFactory
.createKey(parent
, kind
, next
++, appIdNamespace
);
157 public void remove() {
158 throw new UnsupportedOperationException();