Package com.google.appengine.api.datastore

Source Code of com.google.appengine.api.datastore.Cursor

// Copyright 2008 Google Inc. All Rights Reserved.
package com.google.appengine.api.datastore;

import static com.google.appengine.api.datastore.FetchOptions.Builder.withStartCursor;
import static com.google.common.io.BaseEncoding.base64Url;

import com.google.apphosting.datastore.DatastoreV3Pb.CompiledCursor;
import com.google.apphosting.datastore.DatastoreV3Pb.Query;
import com.google.common.io.ByteStreams;
import com.google.common.util.Base64;
import com.google.io.protocol.ProtocolSource;
import com.google.storage.onestore.v3.OnestoreEntity.IndexPosition;
import com.google.storage.onestore.v3.OnestoreEntity.IndexPostfix;

import java.io.IOException;
import java.io.Serializable;

/**
* A cursor that represents a position in a query.
*
* To resume a {@link Query} at the position defined by a {@link Cursor}, the
* {@link Cursor} must be present in the {@link FetchOptions} passed to a {@link
* PreparedQuery} identical to the one it was created from.
* <p>
* Cursors can be retrieved from {@code PreparedQuery.asQueryResult*} functions.
* A typical use case would be:
*
* <blockquote>
* <pre>
* Cursor originalCursor = preparedQuery.asQueryResultList(withLimit(20)).getCursor();
* String encodedCursor = original.toWebSafeString();
* </pre>
* </blockquote>
*
* The encoded cursor can then be passed safely in a get or post arg of a web
* request and on another request the next batch of results can be retrieved with:
*
* <blockquote>
* <pre>
* Cursor decodedCursor = Cursor.fromWebSafeString(encodedCursor);
* List<Entity> nextBatch = preparedQuery.asQueryResultList(withLimit(20).cursor(decoded));
* </pre>
* </blockquote>
*
*/
public final class Cursor implements Serializable {
  static final long serialVersionUID = 3515556366838971499L;
  private CompiledCursor compiledCursor;

  Cursor() {
    compiledCursor = new CompiledCursor();
  }

  Cursor(Cursor previousCursor) {
    this(previousCursor.compiledCursor);
  }

  Cursor(CompiledCursor compiledCursor) {
    this.compiledCursor = compiledCursor.clone();
  }

  private void writeObject(java.io.ObjectOutputStream out) throws IOException {
    out.write(compiledCursor.toByteArray());
  }

  private void readObject(java.io.ObjectInputStream in) throws IOException {
    compiledCursor = fromByteArray(ByteStreams.toByteArray(in)).compiledCursor;
  }

  void advance(final int n, PreparedQuery query) {
    if (n > 0) {
      compiledCursor = query.asQueryResultIterator(withStartCursor(this).offset(n).limit(0))
          .getCursor().compiledCursor;
    } else if (n == -1 &&
               compiledCursor.hasPosition() &&
               compiledCursor.getPosition().hasStartKey() &&
               !compiledCursor.getPosition().isStartInclusive()) {
      compiledCursor.getPosition().setStartInclusive(true);
    } else if (n != 0) {
      throw new IllegalArgumentException("Unable to offset cursor by " + n + " results.");
    }
  }

  /**
   * @return a new cursor to the location in a query sorted in the reverse direction
   * @see com.google.appengine.api.datastore.Query#reverse()
   */
  public Cursor reverse() {
    CompiledCursor clone = compiledCursor.clone();
    if (clone.hasPosition()) {
      clone.getPosition().setStartInclusive(!clone.getPosition().isStartInclusive());
    } else if (clone.hasPostfixPosition()) {
      IndexPostfix postfixPosition = clone.getPostfixPosition();
      postfixPosition.setBefore(!postfixPosition.isBefore());
    } else if (clone.hasAbsolutePosition()) {
      IndexPosition absolutePosition = clone.getAbsolutePosition();
      absolutePosition.setBefore(!absolutePosition.isBefore());
    }
    return new Cursor(clone);
  }

  /**
   * Encodes the current cursor as a web safe string that can later be decoded
   * by {@link #fromWebSafeString(String)}
   */
  public String toWebSafeString() {
    return base64Url().omitPadding().encode(compiledCursor.toByteArray());
  }

  /**
   * Decodes the given encoded cursor
   *
   * @param encodedCursor
   * @return the decoded cursor
   * @throws IllegalArgumentException if the provided string is not a valid encoded cursor
   */
  public static Cursor fromWebSafeString(String encodedCursor) {
    if (encodedCursor == null) {
      throw new NullPointerException("encodedCursor must not be null");
    }

    try {
      return fromByteArray(Base64.decodeWebSafe(encodedCursor));
    } catch (IllegalArgumentException e) {
      throw new IllegalArgumentException("Unable to decode provided cursor.", e);
    }
  }

  private static Cursor fromByteArray(byte[] bytes) {
    Cursor result = new Cursor();
    if (!result.compiledCursor.merge(new ProtocolSource(bytes))) {
      throw new IllegalArgumentException("Unable to decode provided cursor.");
    }
    return result;
  }

  CompiledCursor convertToPb() {
    return compiledCursor;
  }

  @Override
  public boolean equals(Object obj) {
    if (obj == null) {
      return false;
    }

    if (obj.getClass() != this.getClass()) {
      return false;
    }

    return compiledCursor.equals(((Cursor) obj).compiledCursor);
  }

  @Override
  public int hashCode() {
    return compiledCursor.hashCode();
  }

  @Override
  public String toString() {
    return compiledCursor.toString();
  }
}
TOP

Related Classes of com.google.appengine.api.datastore.Cursor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.