/*
* Copyright 2014 serso aka se.solovyev
*
* 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.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Contact details
*
* Email: se.solovyev@gmail.com
* Site: http://se.solovyev.org
*/
package org.solovyev.android.checkout;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
/**
* List of the requests to be executed when connection to the billing service is established.
*/
final class PendingRequests implements Runnable {
@GuardedBy("mList")
@Nonnull
private final List<RequestRunnable> mList = new ArrayList<>();
/**
* Adds <var>runnable</var> to the end of waiting list.
*
* @param runnable runnable to be executed when connection is established
*/
void add(@Nonnull RequestRunnable runnable) {
synchronized (mList) {
Billing.debug("Adding pending request: " + runnable);
mList.add(runnable);
}
}
/**
* Method cancels all pending requests
*/
void cancelAll() {
synchronized (mList) {
Billing.debug("Cancelling all pending requests");
final Iterator<RequestRunnable> iterator = mList.iterator();
while (iterator.hasNext()) {
final RequestRunnable request = iterator.next();
request.cancel();
iterator.remove();
}
}
}
/**
* Method cancels all pending requests with specified <var>tag</var>
*
* @param tag request tag
*/
void cancelAll(@Nullable Object tag) {
synchronized (mList) {
Billing.debug("Cancelling all pending requests with tag=" + tag);
final Iterator<RequestRunnable> iterator = mList.iterator();
while (iterator.hasNext()) {
final RequestRunnable request = iterator.next();
final Object requestTag = request.getTag();
if (requestTag == tag) {
request.cancel();
iterator.remove();
continue;
}
if (requestTag != null && tag == null) {
continue;
}
if (requestTag != null && requestTag.equals(tag)) {
request.cancel();
iterator.remove();
}
}
}
}
/**
* Method cancels pending request with specified <var>requestId</var>
*
* @param requestId id of request to be cancelled
*/
void cancel(int requestId) {
synchronized (mList) {
Billing.debug("Cancelling pending request with id=" + requestId);
final Iterator<RequestRunnable> iterator = mList.iterator();
while (iterator.hasNext()) {
final RequestRunnable request = iterator.next();
if (request.getId() == requestId) {
request.cancel();
iterator.remove();
break;
}
}
}
}
/**
* Method removes first element from the waiting list
*
* @return first list element or null if waiting list is empty
*/
@Nullable
RequestRunnable pop() {
synchronized (mList) {
final RequestRunnable runnable = !mList.isEmpty() ? mList.remove(0) : null;
if (runnable != null) {
Billing.debug("Removing pending request: " + runnable);
}
return runnable;
}
}
/**
* Method gets first element from the waiting list
*
* @return first list element or null if waiting list is empty
*/
@Nullable
RequestRunnable peek() {
synchronized (mList) {
return !mList.isEmpty() ? mList.get(0) : null;
}
}
/**
* Executes all pending runnable.
* Note: this method must be called only on one thread.
*/
@Override
public void run() {
RequestRunnable runnable = peek();
while (runnable != null) {
Billing.debug("Running pending request: " + runnable);
if (runnable.run()) {
remove(runnable);
runnable = peek();
} else {
// request can't be run because service is not connected => no need to run other requests (they will be
// executed when service is connected)
break;
}
}
}
/**
* Method removes instance of <var>runnable</var> from the waiting list
*
* @param runnable runnable to be removed from the waiting list
*/
private void remove(@Nonnull RequestRunnable runnable) {
synchronized (mList) {
final Iterator<RequestRunnable> iterator = mList.iterator();
while (iterator.hasNext()) {
if (iterator.next() == runnable) {
Billing.debug("Removing pending request: " + runnable);
iterator.remove();
break;
}
}
}
}
/**
* Cancels all pending requests with {@link ResponseCodes#SERVICE_NOT_CONNECTED} error code.
*/
void onConnectionFailed() {
Check.isMainThread();
RequestRunnable requestRunnable = pop();
while (requestRunnable != null) {
final Request request = requestRunnable.getRequest();
if (request != null) {
request.onError(ResponseCodes.SERVICE_NOT_CONNECTED);
requestRunnable.cancel();
}
requestRunnable = pop();
}
}
}