<commit_before>package org.ccci.gto.android.common.api;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import org.ccci.gto.android.common.api.AbstractApi.Request;
import org.ccci.gto.android.common.api.AbstractApi.Session;
import org.ccci.gto.android.common.util.IOUtils;
import org.ccci.gto.android.common.util.UriUtils;

import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public abstract class AbstractApi<R extends Request<S>, S extends Session> {
    private static final int DEFAULT_ATTEMPTS = 3;
    protected static final String PREF_SESSION_BASE_NAME = "session";

    protected final Object LOCK_SESSION = new Object();

    @NonNull
    private final Context mContext;

    @NonNull
    private final Uri mBaseUri;
    @NonNull
    private final String mPrefFile;

    protected AbstractApi(@NonNull final Context context, @NonNull final String baseUri) {
        this(context, baseUri, null);
    }

    protected AbstractApi(@NonNull final Context context, @NonNull final String baseUri,
                          @Nullable final String prefFile) {
        this(context, Uri.parse(baseUri.endsWith("/") ? baseUri : baseUri + "/"), prefFile);
    }

    protected AbstractApi(@NonNull final Context context, @NonNull final Uri baseUri) {
        this(context, baseUri, null);
    }

    protected AbstractApi(@NonNull final Context context, @NonNull final Uri baseUri, @Nullable final String prefFile) {
        this.mContext = context;
        this.mBaseUri = baseUri;
        this.mPrefFile = prefFile != null ? prefFile : getClass().getSimpleName();
    }

    private SharedPreferences getPrefs() {
        return mContext.getSharedPreferences(mPrefFile, Context.MODE_PRIVATE);
    }

    @NonNull
    protected final HttpURLConnection sendRequest(@NonNull final R request) throws ApiException {
        return this.sendRequest(request, DEFAULT_ATTEMPTS);
    }

    @NonNull
    protected final HttpURLConnection sendRequest(@NonNull final R request, final int attempts)
            throws ApiException {
        try {
            HttpURLConnection conn = null;
            boolean successful = false;
            try {
                // load/establish the session if we are using sessions
                if (request.useSession) {
                    // prepare for the session
                    this.onPrepareSession(request);

                    // get the session, establish a session if one doesn't exist or if we have a stale session
                    synchronized (LOCK_SESSION) {
                        request.session = loadSession(request);
                        if (request.session == null) {
                            request.session = this.establishSession(request);

                            // save the newly established session
                            if (request.session != null) {
                                this.saveSession(request.session);
                            }
                        }
                    }

                    // throw an exception if we don't have a valid session
                    if (request.session == null) {
                        throw new InvalidSessionApiException();
                    }
                }

                // build the request uri
                final Uri.Builder uri = mBaseUri.buildUpon();
                onPrepareUri(uri, request);
                final URL url;
                try {
                    url = new URL(uri.build().toString());
                } catch (final MalformedURLException e) {
                    throw new RuntimeException("invalid Request URL: " + uri.build().toString(), e);
                }

                // prepare the request
                conn = (HttpURLConnection) url.openConnection();
                onPrepareRequest(conn, request);

                // POST/PUT requests
                if (request.method == Request.Method.POST || request.method == Request.Method.PUT) {
                    conn.setDoOutput(true);
                    final byte[] data = request.content != null ? request.content : new byte[0];
                    conn.setFixedLengthStreamingMode(data.length);
                    conn.setUseCaches(false);
                    OutputStream out = null;
                    try {
                        out = conn.getOutputStream();
                        out.write(data);
                    } finally {
                        // XXX: don't use IOUtils.closeQuietly, we want exceptions thrown
                        if (out != null) {
                            out.close();
                        }
                    }
                }

                // no need to explicitly execute, accessing the response triggers the execute

                // check for an invalid session
                if (request.useSession && this.isSessionInvalid(conn, request)) {
                    // reset the session
                    synchronized (LOCK_SESSION) {
                        // only reset if this is still the same session
                        final Session current = this.loadSession(request);
                        if (current != null && current.equals(request.session)) {
                            this.deleteSession(request.session);
                        }
                    }

                    // throw an invalid session exception
                    throw new InvalidSessionApiException();
                }

                // process the response
                onProcessResponse(conn, request);

                // return the connection for method specific handling
                successful = true;
                return conn;
            } catch (final IOException e) {
                throw new ApiSocketException(e);
            } finally {
                // close a potentially open connection if we weren't successful
                if (!successful) {
                    IOUtils.closeQuietly(conn);
                }

                // clear out the session that was loaded & used
                request.session = null;

                // cleanup any request specific data
                onCleanupRequest(request);
            }
        } catch (final ApiException e) {
            // retry request on an API exception
            if (attempts > 0) {
                return this.sendRequest(request, attempts - 1);
            }

            // propagate the exception
            throw e;
        }
    }

    @NonNull
    protected final Request.Parameter param(@NonNull final String name, @NonNull final String value) {
        return new Request.Parameter(name, value);
    }

    @Nullable
    protected S loadSession(@NonNull final R request) {<commit_msg>add a convenience method for setting int parameters<commit_after>package org.ccci.gto.android.common.api;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import org.ccci.gto.android.common.api.AbstractApi.Request;
import org.ccci.gto.android.common.api.AbstractApi.Session;
import org.ccci.gto.android.common.util.IOUtils;
import org.ccci.gto.android.common.util.UriUtils;

import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public abstract class AbstractApi<R extends Request<S>, S extends Session> {
    private static final int DEFAULT_ATTEMPTS = 3;
    protected static final String PREF_SESSION_BASE_NAME = "session";

    protected final Object LOCK_SESSION = new Object();

    @NonNull
    private final Context mContext;

    @NonNull
    private final Uri mBaseUri;
    @NonNull
    private final String mPrefFile;

    protected AbstractApi(@NonNull final Context context, @NonNull final String baseUri) {
        this(context, baseUri, null);
    }

    protected AbstractApi(@NonNull final Context context, @NonNull final String baseUri,
                          @Nullable final String prefFile) {
        this(context, Uri.parse(baseUri.endsWith("/") ? baseUri : baseUri + "/"), prefFile);
    }

    protected AbstractApi(@NonNull final Context context, @NonNull final Uri baseUri) {
        this(context, baseUri, null);
    }

    protected AbstractApi(@NonNull final Context context, @NonNull final Uri baseUri, @Nullable final String prefFile) {
        this.mContext = context;
        this.mBaseUri = baseUri;
        this.mPrefFile = prefFile != null ? prefFile : getClass().getSimpleName();
    }

    private SharedPreferences getPrefs() {
        return mContext.getSharedPreferences(mPrefFile, Context.MODE_PRIVATE);
    }

    @NonNull
    protected final HttpURLConnection sendRequest(@NonNull final R request) throws ApiException {
        return this.sendRequest(request, DEFAULT_ATTEMPTS);
    }

    @NonNull
    protected final HttpURLConnection sendRequest(@NonNull final R request, final int attempts)
            throws ApiException {
        try {
            HttpURLConnection conn = null;
            boolean successful = false;
            try {
                // load/establish the session if we are using sessions
                if (request.useSession) {
                    // prepare for the session
                    this.onPrepareSession(request);

                    // get the session, establish a session if one doesn't exist or if we have a stale session
                    synchronized (LOCK_SESSION) {
                        request.session = loadSession(request);
                        if (request.session == null) {
                            request.session = this.establishSession(request);

                            // save the newly established session
                            if (request.session != null) {
                                this.saveSession(request.session);
                            }
                        }
                    }

                    // throw an exception if we don't have a valid session
                    if (request.session == null) {
                        throw new InvalidSessionApiException();
                    }
                }

                // build the request uri
                final Uri.Builder uri = mBaseUri.buildUpon();
                onPrepareUri(uri, request);
                final URL url;
                try {
                    url = new URL(uri.build().toString());
                } catch (final MalformedURLException e) {
                    throw new RuntimeException("invalid Request URL: " + uri.build().toString(), e);
                }

                // prepare the request
                conn = (HttpURLConnection) url.openConnection();
                onPrepareRequest(conn, request);

                // POST/PUT requests
                if (request.method == Request.Method.POST || request.method == Request.Method.PUT) {
                    conn.setDoOutput(true);
                    final byte[] data = request.content != null ? request.content : new byte[0];
                    conn.setFixedLengthStreamingMode(data.length);
                    conn.setUseCaches(false);
                    OutputStream out = null;
                    try {
                        out = conn.getOutputStream();
                        out.write(data);
                    } finally {
                        // XXX: don't use IOUtils.closeQuietly, we want exceptions thrown
                        if (out != null) {
                            out.close();
                        }
                    }
                }

                // no need to explicitly execute, accessing the response triggers the execute

                // check for an invalid session
                if (request.useSession && this.isSessionInvalid(conn, request)) {
                    // reset the session
                    synchronized (LOCK_SESSION) {
                        // only reset if this is still the same session
                        final Session current = this.loadSession(request);
                        if (current != null && current.equals(request.session)) {
                            this.deleteSession(request.session);
                        }
                    }

                    // throw an invalid session exception
                    throw new InvalidSessionApiException();
                }

                // process the response
                onProcessResponse(conn, request);

                // return the connection for method specific handling
                successful = true;
                return conn;
            } catch (final IOException e) {
                throw new ApiSocketException(e);
            } finally {
                // close a potentially open connection if we weren't successful
                if (!successful) {
                    IOUtils.closeQuietly(conn);
                }

                // clear out the session that was loaded & used
                request.session = null;

                // cleanup any request specific data
                onCleanupRequest(request);
            }
        } catch (final ApiException e) {
            // retry request on an API exception
            if (attempts > 0) {
                return this.sendRequest(request, attempts - 1);
            }

            // propagate the exception
            throw e;
        }
    }

    @NonNull
    protected final Request.Parameter param(@NonNull final String name, @NonNull final String value) {
        return new Request.Parameter(name, value);
    }

    protected final Request.Parameter param(@NonNull final String name, final int value) {
        return new Request.Parameter(name, Integer.toString(value));
    }

    @Nullable
    protected S loadSession(@NonNull final R request) {
{'fraction_non_alphanumeric': 0.07819954366314043, 'fraction_numerical': 0.0005531355873608519, 'mean_word_length': 1.6394160583941606, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '373547', 'n_tokens_mistral': 3403, 'n_tokens_neox': 3244, 'n_words': 973}