Revision created by MOE tool push_codebase.
[gae.git] / java / src / main / com / google / appengine / api / datastore / Cursor.java
bloba12bb47831e2c7d7e5ca287d9c795a1a60995818
1 // Copyright 2008 Google Inc. All Rights Reserved.
2 package com.google.appengine.api.datastore;
4 import static com.google.appengine.api.datastore.FetchOptions.Builder.withStartCursor;
5 import static com.google.common.base.Preconditions.checkNotNull;
6 import static com.google.common.io.BaseEncoding.base64Url;
8 import com.google.apphosting.datastore.DatastoreV3Pb.Query;
9 import com.google.common.io.ByteStreams;
10 import com.google.common.util.Base64;
11 import com.google.protobuf.ByteString;
13 import java.io.IOException;
14 import java.io.Serializable;
16 /**
17 * A cursor that represents a position in a query.
19 * To resume a {@link Query} at the position defined by a {@link Cursor}, the
20 * {@link Cursor} must be present in the {@link FetchOptions} passed to a {@link
21 * PreparedQuery} identical to the one it was created from.
22 * <p>
23 * Cursors can be retrieved from {@code PreparedQuery.asQueryResult*} functions.
24 * A typical use case would be:
26 * <blockquote>
27 * <pre>
28 * Cursor originalCursor = preparedQuery.asQueryResultList(withLimit(20)).getCursor();
29 * String encodedCursor = original.toWebSafeString();
30 * </pre>
31 * </blockquote>
33 * The encoded cursor can then be passed safely in a get or post arg of a web
34 * request and on another request the next batch of results can be retrieved with:
36 * <blockquote>
37 * <pre>
38 * Cursor decodedCursor = Cursor.fromWebSafeString(encodedCursor);
39 * List<Entity> nextBatch = preparedQuery.asQueryResultList(withLimit(20).cursor(decoded));
40 * </pre>
41 * </blockquote>
44 public final class Cursor implements Serializable {
45 static final long serialVersionUID = 3515556366838971499L;
46 private ByteString cursorBytes;
48 Cursor() {
49 cursorBytes = ByteString.EMPTY;
52 Cursor(Cursor previousCursor) {
53 this(previousCursor.cursorBytes);
56 Cursor(ByteString cursorBytes) {
57 checkNotNull(cursorBytes);
58 this.cursorBytes = cursorBytes;
61 private void writeObject(java.io.ObjectOutputStream out) throws IOException {
62 out.write(cursorBytes.toByteArray());
65 private void readObject(java.io.ObjectInputStream in) throws IOException {
66 cursorBytes = ByteString.copyFrom(ByteStreams.toByteArray(in));
69 Cursor advance(final int n, PreparedQuery query) {
70 if (n == 0) {
71 return this;
72 } else if (n > 0) {
73 return query.asQueryResultIterator(withStartCursor(this).offset(n).limit(0)).getCursor();
75 throw new IllegalArgumentException("Unable to offset cursor by " + n + " results.");
78 /**
79 * @Deprecated It is no longer necessary to call {@link #reverse()} on
80 * cursors.
82 * A cursor returned by a query may also be used in the query returned by
83 * {@link com.google.appengine.api.datastore.Query#reverse()}.
85 * @return a cursor identical to {@code this}
87 @Deprecated
88 public Cursor reverse() {
89 return this;
92 /**
93 * Encodes the current cursor as a web safe string that can later be decoded
94 * by {@link #fromWebSafeString(String)}
96 public String toWebSafeString() {
97 return base64Url().omitPadding().encode(cursorBytes.toByteArray());
101 * Decodes the given encoded cursor
103 * @param encodedCursor
104 * @return the decoded cursor
105 * @throws IllegalArgumentException if the provided string is not a valid encoded cursor
107 public static Cursor fromWebSafeString(String encodedCursor) {
108 checkNotNull(encodedCursor, "encodedCursor must not be null");
110 try {
111 return new Cursor(
112 ByteString.copyFrom(Base64.decodeWebSafe(encodedCursor)));
113 } catch (IllegalArgumentException e) {
114 throw new IllegalArgumentException("Unable to decode provided cursor.", e);
118 ByteString toByteString() {
119 return ByteString.copyFrom(cursorBytes.toByteArray());
122 @Override
123 public boolean equals(Object obj) {
124 if (obj == null) {
125 return false;
128 if (obj.getClass() != this.getClass()) {
129 return false;
132 return cursorBytes.equals(((Cursor) obj).cursorBytes);
135 @Override
136 public int hashCode() {
137 return cursorBytes.hashCode();
140 @Override
141 public String toString() {
142 return cursorBytes.toString();