package org.ektorp;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.node.*;
import org.ektorp.util.Base64;
import org.ektorp.util.Exceptions;
/**
*
* @author henrik lundgren
*
*/
public class PageRequest {
private static final String NEXT_KEY_FIELD_NAME = "key";
private static final String NEXT_DOCID_FIELD_NAME = "id";
private static final String PAGE_SIZE_FIELD_NAME = "s";
private static final String BACK_FIELD_NAME = "b";
private static final String PAGE_FIELD_NAME = "p";
private static final ObjectMapper MAPPER = new ObjectMapper();
private final int pageSize;
private final KeyIdPair nextKey;
private final boolean back;
private final int page;
public static ViewQuery applyPagingParameters(ViewQuery q, PageRequest pr) {
ViewQuery pagedQuery = q.clone();
if (pr.page > 0) {
if (pr.getStartKey() != null) {
pagedQuery.startKey(pr.getStartKey());
}
if (pr.getStartKeyDocId() != null) {
pagedQuery.startDocId(pr.getStartKeyDocId());
}
if (pr.back) {
pagedQuery.descending(!pagedQuery.isDescending());
}
}
int additionalRowsToQuery = 1;
pagedQuery.limit(pr.getPageSize() + additionalRowsToQuery);
return pagedQuery;
}
public int getPageNo() {
return page;
}
public static PageRequest firstPage(int pageSize) {
return new Builder().pageSize(pageSize).build();
}
public PageRequest.Builder nextRequest(Object nextStartKey,
String nextStartDocId) {
try {
JsonNode keyNode = MAPPER.readTree(MAPPER
.writeValueAsString(nextStartKey));
return new Builder(this)
.nextKey(new KeyIdPair(keyNode, nextStartDocId));
} catch (IOException e) {
throw Exceptions.propagate(e);
}
}
private PageRequest(Builder b) {
this.back = b.back;
this.page = b.page;
this.nextKey = b.nextKey;
this.pageSize = b.pageSize;
}
public static PageRequest fromLink(String link) {
try {
JsonNode n = MAPPER.readTree(new ByteArrayInputStream(Base64
.decode(link, Base64.URL_SAFE)));
JsonNode keyNode = n.get(NEXT_KEY_FIELD_NAME);
JsonNode docIdNode = n.get(NEXT_DOCID_FIELD_NAME);
String docId = null;
if (docIdNode != null) {
docId = docIdNode.asText();
}
KeyIdPair keyIdPair;
if (keyNode != null || docId != null) {
keyIdPair = new KeyIdPair(keyNode, docId);
} else {
keyIdPair = null;
}
int pageSize = n.get(PAGE_SIZE_FIELD_NAME).intValue();
boolean back = n.get(BACK_FIELD_NAME).asInt() == 1;
int page = n.get(PAGE_FIELD_NAME).asInt();
return new Builder()
.nextKey(keyIdPair)
.pageSize(pageSize)
.back(back)
.page(page)
.build();
} catch (Exception e) {
throw Exceptions.propagate(e);
}
}
public String asLink() {
try {
return Base64.encodeBytes(MAPPER.writeValueAsBytes(asJson()),
Base64.URL_SAFE);
} catch (Exception e) {
throw Exceptions.propagate(e);
}
}
public JsonNode asJson() {
ObjectNode n = MAPPER.createObjectNode();
if (nextKey != null) {
if (nextKey.key != null) {
n.put(NEXT_KEY_FIELD_NAME, nextKey.key);
}
if (nextKey.docId != null) {
n.put(NEXT_DOCID_FIELD_NAME, nextKey.docId);
}
}
n.put(PAGE_SIZE_FIELD_NAME, pageSize);
n.put(BACK_FIELD_NAME, back ? 1 : 0);
n.put(PAGE_FIELD_NAME, page);
return n;
}
public int getPageSize() {
return pageSize;
}
public Object getStartKey() {
return nextKey != null ? nextKey.key : null;
}
public String getStartKeyDocId() {
return nextKey != null ? nextKey.docId : null;
}
/**
*
* @param startKey
* @param startDocId
* @return
*/
public PageRequest getPreviousPageRequest() {
return new Builder(this)
.page(this.page-1)
.build();
}
public boolean isBack() {
return back;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(this.getClass().getName()).append("(");
builder.append("pageSize=").append(pageSize);
builder.append(",page=").append(page);
builder.append(",back=").append(back);
builder.append(",nextKey=").append(nextKey);
builder.append(")");
return builder.toString();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (back ? 1231 : 1237);
result = prime * result + ((nextKey == null) ? 0 : nextKey.hashCode());
result = prime * result + page;
result = prime * result + pageSize;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
PageRequest other = (PageRequest) obj;
if (back != other.back)
return false;
if (nextKey == null) {
if (other.nextKey != null)
return false;
} else if (!nextKey.equals(other.nextKey))
return false;
if (page != other.page)
return false;
return pageSize == other.pageSize;
}
public static class Builder {
private int pageSize;
private KeyIdPair nextKey;
private boolean back;
private int page;
public Builder() {
}
public Builder(PageRequest prototype) {
this.back = prototype.back;
this.nextKey = prototype.nextKey;
this.page = prototype.page;
this.pageSize = prototype.pageSize;
}
public Builder pageSize(int i) {
this.pageSize = i;
return this;
}
public Builder page(int i) {
this.page = i;
return this;
}
public Builder back(boolean b) {
this.back = b;
return this;
}
public Builder nextKey(KeyIdPair k) {
this.nextKey = k;
return this;
}
public PageRequest build() {
return new PageRequest(this);
}
public int getPageNo() {
return page;
}
public int getNextPage() {
return page + 1;
}
public int getPrevPage() {
return page - 1;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(this.getClass().getName()).append("(");
builder.append("pageSize=").append(pageSize);
builder.append(",page=").append(page);
builder.append(",back=").append(back);
builder.append(",nextKey=").append(nextKey);
builder.append(")");
return builder.toString();
}
}
private static final class KeyIdPair {
final JsonNode key;
final String docId;
KeyIdPair(JsonNode key, String docId) {
this.key = key;
this.docId = docId;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((docId == null) ? 0 : docId.hashCode());
result = prime * result + ((key == null) ? 0 : key.toString().hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
KeyIdPair other = (KeyIdPair) obj;
if (docId == null) {
if (other.docId != null)
return false;
} else if (!docId.equals(other.docId))
return false;
if (key == null) {
if (other.key != null)
return false;
} else if (!key.toString().equals(other.key.toString()))
return false;
return true;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(this.getClass().getName()).append("(");
builder.append("key=").append(key);
builder.append(",docId=").append(docId);
builder.append(")");
return builder.toString();
}
}
}