001package com.plivo.api.models.base;
002
003import com.fasterxml.jackson.databind.annotation.JsonSerialize;
004import com.fasterxml.jackson.databind.annotation.JsonSerialize.Typing;
005import com.plivo.api.PlivoClient;
006import com.plivo.api.exceptions.PlivoValidationException;
007import com.plivo.api.exceptions.IterableError;
008import com.plivo.api.exceptions.PlivoRestException;
009import com.plivo.api.util.Utils;
010
011import java.io.IOException;
012import java.util.Deque;
013import java.util.Iterator;
014import java.util.Map;
015import java.util.concurrent.ConcurrentLinkedDeque;
016
017import retrofit2.Call;
018import retrofit2.Response;
019
020/**
021 * Lists instances of a resource, possibly filtered.
022 *
023 * @param <T> The type of the resource.
024 */
025@JsonSerialize(typing = Typing.STATIC)
026public abstract class Lister<T extends BaseResource> extends BaseRequest<T> implements Iterable<T> {
027
028  protected Integer limit = null;
029  protected Integer offset = null;
030
031  @Override
032  public Lister<T> client(final PlivoClient plivoClient) {
033    this.plivoClient = plivoClient;
034    return this;
035  }
036
037
038  /**
039   * @return Used to display the number of results per page. The maximum number of results that can
040   * be fetched is 20.
041   */
042  public Integer limit() {
043    return this.limit;
044  }
045
046  /**
047   * @return Denotes the number of value items by which the results should be offset.
048   */
049  public Integer offset() {
050    return this.offset;
051  }
052
053  /**
054   * @param limit Used to display the number of results per page. The maximum number of results that
055   *              can be fetched is 20.
056   */
057  public Lister<T> limit(final Integer limit) {
058    this.limit = limit;
059    return this;
060  }
061
062  /**
063   * @param offset Denotes the number of value items by which the results should be offset.
064   */
065  public Lister<T> offset(final Integer offset) {
066    this.offset = offset;
067    return this;
068  }
069
070  protected abstract Call<ListResponse<T>> obtainCall() throws PlivoValidationException;
071
072  /**
073   * Actually list instances of the resource.
074   */
075  public ListResponse<T> list() throws IOException, PlivoRestException, PlivoValidationException {
076    validate();
077    Response<ListResponse<T>> response = obtainCall().execute();
078
079    handleResponse(response);
080
081    return response.body();
082  }
083
084  public Long get() throws IOException, PlivoRestException, PlivoValidationException {
085    validate();
086    Response<ListResponse<T>> response = obtainCall().execute();
087    handleResponse(response);
088    try {
089      return response.body().getMeta().getTotalCount();
090    } catch (Exception e) {
091      return 0L;
092    }
093  }
094
095
096  protected Map<String, Object> toMap() {
097    client();
098    return Utils.objectToMap(plivoClient.getObjectMapper(), this);
099  }
100
101  @Override
102  public Iterator<T> iterator() throws IterableError {
103    if (limit == null) {
104      limit = 20;
105    }
106
107    if (offset == null) {
108      offset = 0;
109    }
110
111    return new Iterator<T>() {
112      Deque<T> items = new ConcurrentLinkedDeque<>();
113
114      @Override
115      public boolean hasNext() {
116        if (!items.isEmpty()) {
117          return true;
118        }
119
120        try {
121          ListResponse<T> itemList = Lister.this.list();
122          if (itemList.getObjects().isEmpty()) {
123            return false;
124          }
125          this.items.addAll(itemList.getObjects());
126          offset += limit;
127        } catch (IOException | PlivoRestException | PlivoValidationException exception) {
128          throw new IterableError();
129        }
130        return true;
131      }
132
133      @Override
134      public T next() {
135        if (items.isEmpty()) {
136          try {
137            ListResponse<T> itemList = Lister.this.list();
138            this.items.addAll(itemList.getObjects());
139            offset += limit;
140          } catch (IOException | PlivoRestException | PlivoValidationException exception) {
141            throw new IterableError();
142          }
143        }
144
145        return items.removeFirst();
146      }
147    };
148  }
149}