/*
 * Copyright (C) 2015 Square, Inc.
 *
 * 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 retrofit;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.concurrent.Executor;

final class ExecutorCallAdapterFactory implements CallAdapter.Factory {
  private final Executor callbackExecutor;

  ExecutorCallAdapterFactory(Executor callbackExecutor) {
    this.callbackExecutor = callbackExecutor;
  }

  @Override
  public CallAdapter<Call<?>> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    if (Utils.getRawType(returnType) != Call.class) {
      return null;
    }
    final Type responseType = Utils.getCallResponseType(returnType);
    return new CallAdapter<Call<?>>() {
      @Override public Type responseType() {
        return responseType;
      }

      @Override public <R> Call<R> adapt(Call<R> call) {
        return new ExecutorCallbackCall<>(callbackExecutor, call);
      }
    };
  }

  static final class ExecutorCallbackCall<T> implements Call<T> {
    private final Executor callbackExecutor;
    private final Call<T> delegate;

    ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
      this.callbackExecutor = callbackExecutor;
      this.delegate = delegate;
    }

    @Override public void enqueue(Callback<T> callback) {
      delegate.enqueue(new ExecutorCallback<>(callbackExecutor, callback));
    }

    @Override public Response<T> execute() throws IOException {
      return delegate.execute();
    }

    @Override public void cancel() {
      delegate.cancel();
    }

    @SuppressWarnings("CloneDoesntCallSuperClone") // Performing deep clone.
    @Override public Call<T> clone() {
      return new ExecutorCallbackCall<>(callbackExecutor, delegate.clone());
    }
  }

  static final class ExecutorCallback<T> implements Callback<T> {
    private final Executor callbackExecutor;
    private final Callback<T> delegate;

    ExecutorCallback(Executor callbackExecutor, Callback<T> delegate) {
      this.callbackExecutor = callbackExecutor;
      this.delegate = delegate;
    }

    @Override public void onResponse(final Response<T> response) {
      callbackExecutor.execute(new Runnable() {
        @Override public void run() {
          delegate.onResponse(response);
        }
      });
    }

    @Override public void onFailure(final Throwable t) {
      callbackExecutor.execute(new Runnable() {
        @Override public void run() {
          delegate.onFailure(t);
        }
      });
    }
  }
}

@@ -1639,32 +1639,71 @@
 all%3CT%3E delegate;
+%0A    private volatile boolean canceled;
 %0A%0A    ExecutorCa
@@ -1881,16 +1881,22 @@
 enqueue(
+final 
 Callback
@@ -1942,54 +1942,744 @@
 new 
-ExecutorCallback%3C%3E(callbackExecutor, callback)
+Callback%3CT%3E() %7B%0A        @Override public void onResponse(final Response%3CT%3E response) %7B%0A          callbackExecutor.execute(new Runnable() %7B%0A            @Override public void run() %7B%0A              if (canceled) %7B%0A                // Emulate OkHttp's behavior of throwing/delivering an IOException on cancelation%0A                callback.onFailure(new IOException(%22Canceled%22));%0A              %7D else %7B%0A                callback.onResponse(response);%0A              %7D%0A            %7D%0A          %7D);%0A        %7D%0A%0A        @Override public void onFailure(final Throwable t) %7B%0A          callbackExecutor.execute(new Runnable() %7B%0A            @Override public void run() %7B%0A              callback.onFailure(t);%0A            %7D%0A          %7D);%0A        %7D%0A      %7D
 );%0A 
@@ -2821,24 +2821,47 @@
  cancel() %7B%0A
+      canceled = true;%0A
       delega
@@ -2992,24 +2992,24 @@
 %3E clone() %7B%0A
+
       return
@@ -3086,746 +3086,7 @@
 %0A  %7D
-%0A%0A  static final class ExecutorCallback%3CT%3E implements Callback%3CT%3E %7B%0A    private final Executor callbackExecutor;%0A    private final Callback%3CT%3E delegate;%0A%0A    ExecutorCallback(Executor callbackExecutor, Callback%3CT%3E delegate) %7B%0A      this.callbackExecutor = callbackExecutor;%0A      this.delegate = delegate;%0A    %7D%0A%0A    @Override public void onResponse(final Response%3CT%3E response) %7B%0A      callbackExecutor.execute(new Runnable() %7B%0A        @Override public void run() %7B%0A          delegate.onResponse(response);%0A        %7D%0A      %7D);%0A    %7D%0A%0A    @Override public void onFailure(final Throwable t) %7B%0A      callbackExecutor.execute(new Runnable() %7B%0A        @Override public void run() %7B%0A          delegate.onFailure(t);%0A        %7D%0A      %7D);%0A    %7D%0A  %7D
 %0A%7D%0A

Check for cancellation on callback executor run.