All Downloads are FREE. Search and download functionalities are using the official Maven repository.

main.java.com.cloudant.client.internal.views.ViewResponseImpl Maven / Gradle / Ivy

There is a newer version: 2.20.1
Show newest version
/*
 * Copyright (c) 2015 IBM Corp. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the
 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language governing permissions
 * and limitations under the License.
 */

package com.cloudant.client.internal.views;

import com.cloudant.client.api.model.Document;
import com.cloudant.client.api.views.ViewResponse;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;

class ViewResponseImpl implements ViewResponse {

    private final ViewQueryParameters initialQueryParameters;
    private final boolean hasPrevious;
    private final boolean hasNext;
    private final long pageNumber;
    private PageMetadata nextPageMetadata;
    private PageMetadata previousPageMetadata;
    private final long totalRows;
    private final long resultFrom;
    private final long resultTo;

    private final List> rows = new ArrayList>();
    private List keys = null;
    private List values = null;
    private List docs = null;

    ViewResponseImpl(ViewQueryParameters viewQueryParameters, JsonObject response) {
        this(viewQueryParameters, response, null);
    }

    ViewResponseImpl(ViewQueryParameters initialQueryParameters, JsonObject response,
                     PageMetadata pageMetadata) {
        this.initialQueryParameters = initialQueryParameters;

        PageMetadata.PagingDirection thisPageDirection;
        if (pageMetadata == null) {
            previousPageMetadata = null;
            pageNumber = 1l;
            //from a first page we can only page FORWARD
            thisPageDirection = PageMetadata.PagingDirection.FORWARD;
        } else {
            this.pageNumber = pageMetadata.pageNumber;
            thisPageDirection = pageMetadata.direction;
        }

        //build the rows from the response
        JsonArray rowsArray = response.getAsJsonArray("rows");
        if (rowsArray != null) {
            for (JsonElement row : rowsArray) {
                rows.add(new RowImpl(initialQueryParameters, row));
            }
        }
        int resultRows = rows.size();

        JsonElement totalRowsElement = response.get("total_rows");
        if (totalRowsElement != null) {
            totalRows = totalRowsElement.getAsLong();
        } else {
            //if there is no total rows element, use the rows size
            totalRows = rows.size();
        }

        long rowsPerPage = (initialQueryParameters.getRowsPerPage() != null) ?
                initialQueryParameters.getRowsPerPage().longValue() : totalRows;

        //we expect limit = rowsPerPage + 1 results, if we have rowsPerPage or less we are on the
        // last page
        hasNext = resultRows > rowsPerPage;

        if (PageMetadata.PagingDirection.BACKWARD == thisPageDirection) {
            //Result needs reversing because to implement backward paging the view reading
            // order is reversed
            Collections.reverse(rows);
        }

        //set previous page links if not the first page
        if (this.pageNumber > 1) {
            hasPrevious = true;
            // Construct the previous page metadata (i.e. paging backward)
            // Decrement the page number by 1
            // The startKey of this page is also the start key of the previous page, but using a
            // descending lookup indicated by the paging direction.
            previousPageMetadata = new PageMetadata(PageMetadata.PagingDirection
                    .BACKWARD, this.pageNumber - 1l, PageMetadata.reversePaginationQueryParameters
                    (initialQueryParameters, rows.get(0).getKey(), rows.get(0).getId()));
        } else {
            hasPrevious = false;
        }

        // If we are not on the last page, we need to use the last
        // result as the start key for the next page and therefore
        // we don't return it to the user.
        // If we are on the last page, the final row should be returned
        // to the user.
        int lastIndex = resultRows - 1;
        if (hasNext) {
            // Construct the next page metadata (i.e. paging forward)
            // Increment the page number by 1
            // The last element is the start of the next page so use the key and ID from that
            // element for creating the next page query parameters.
            nextPageMetadata = new PageMetadata(PageMetadata.PagingDirection
                    .FORWARD, this.pageNumber + 1l, PageMetadata.forwardPaginationQueryParameters
                    (initialQueryParameters, rows.get(lastIndex).getKey(), rows.get(lastIndex)
                            .getId()));

            // The final element is the first element of the next page, so remove from the list that
            // will be returned.
            rows.remove(lastIndex);
        } else {
            nextPageMetadata = null;
        }

        // calculate paging display info
        long offset = (this.pageNumber - 1) * rowsPerPage;
        resultFrom = offset + 1;
        resultTo = offset + (hasNext ? rowsPerPage : resultRows);
    }

    @Override
    public List> getRows() {
        return Collections.unmodifiableList(rows);
    }

    @Override
    public List getKeys() {
        if (keys == null) {
            populateKV();
        }
        return keys;
    }

    @Override
    public List getValues() {
        if (values == null) {
            populateKV();
        }
        return values;
    }

    @Override
    public List getDocs() {
        if (initialQueryParameters.getIncludeDocs()) {
            if (docs == null) {
                docs = new ArrayList();
                for (Row row : getRows()) {
                    docs.add(row.getDocument());
                }
            }
            return docs;
        } else {
            throw new IllegalStateException("Cannot getDocs() when include_docs is false.");
        }
    }

    @Override
    public  List getDocsAs(Class docType) {
        if (initialQueryParameters.getIncludeDocs()) {
            List documents = new ArrayList();
            for (Row row : getRows()) {
                documents.add(row.getDocumentAsType(docType));
            }
            return documents;
        } else {
            throw new IllegalStateException("Cannot getDocs() when include_docs is false.");
        }
    }

    @Override
    public boolean hasNextPage() {
        return hasNext;
    }

    @Override
    public boolean hasPreviousPage() {
        return hasPrevious;
    }

    @Override
    public ViewResponse nextPage() throws IOException {
        if (hasNext) {
            JsonObject response = ViewRequester.getResponseAsJson(nextPageMetadata
                    .pageRequestParameters);
            return new ViewResponseImpl(initialQueryParameters, response, nextPageMetadata);
        } else {
            return null;
        }
    }

    @Override
    public ViewResponse previousPage() throws IOException {
        if (hasPrevious) {
            JsonObject response = ViewRequester.getResponseAsJson(previousPageMetadata
                    .pageRequestParameters);
            return new ViewResponseImpl(initialQueryParameters, response,
                    previousPageMetadata);
        } else {
            return null;
        }
    }

    @Override
    public String getNextPageToken() {
        if (hasNext) {
            return PaginationToken.tokenize(nextPageMetadata);
        }
        return null;
    }

    @Override
    public String getPreviousPageToken() {
        if (hasPrevious) {
            return PaginationToken.tokenize(previousPageMetadata);
        }
        return null;
    }

    @Override
    public Long getPageNumber() {
        return this.pageNumber;
    }

    @Override
    public Long getFirstRowCount() {
        return resultFrom;
    }

    @Override
    public Long getLastRowCount() {
        return resultTo;
    }

    @Override
    public Long getTotalRowCount() {
        return totalRows;
    }

    @Override
    public Iterator> iterator() {
        return new Iterator>() {

            //initially true to allow next() to get the first element
            boolean hasNext = true;
            //hold a ref to the previous page so we don't have to make the GET for the next page
            // until next() is called
            ViewResponse previousPage = null;

            @Override
            public boolean hasNext() {
                return hasNext;
            }

            @Override
            public ViewResponse next() {
                try {
                    if (hasNext) {
                        ViewResponse pageToReturn = (previousPage == null) ?
                                ViewResponseImpl.this : previousPage.nextPage();
                        hasNext = pageToReturn.hasNextPage();
                        previousPage = pageToReturn;
                        return pageToReturn;
                    } else {
                        throw new NoSuchElementException("No more pages");
                    }
                } catch (IOException e) {
                    //iterators can't throw a checked exception, so wrap in a runtime
                    throw new RuntimeException(e);
                }
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
    }

    private void populateKV() {
        keys = new ArrayList();
        values = new ArrayList();
        for (Row row : getRows()) {
            keys.add(row.getKey());
            values.add(row.getValue());
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy