<commit_before>/**
 * EasyPostResource.java
 * This file is a part of EasyPost API SDK.
 * (c) 2022 EasyPost
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
package com.easypost.net;

import com.easypost.EasyPost;
import com.easypost.exception.EasyPostException;
import com.easypost.model.Event;
import com.easypost.model.EventDeserializer;
import com.easypost.model.Fee;
import com.easypost.model.Rate;
import com.easypost.model.RateDeserializer;
import com.easypost.model.Shipment;
import com.easypost.model.SmartrateCollection;
import com.easypost.model.SmartrateCollectionDeserializer;
import com.easypost.model.TrackingDetail;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.net.URLStreamHandler;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;

public abstract class EasyPostResource {
    public static final String EASYPOST_SUPPORT_EMAIL = "<EMAIL>";

    private static final int DEFAULT_CONNECT_TIMEOUT_MILLISECONDS = 30000;
    private static final int DEFAULT_READ_TIMEOUT_MILLISECONDS = 60000;
    private static final double APP_ENGINE_DEFAULT_TIMEOUT_SECONDS = 20.0;

    public static final Gson GSON =
            new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
                    .registerTypeAdapter(Event.class, new EventDeserializer())
                    .registerTypeAdapter(Rate.class, new RateDeserializer())
                    .registerTypeAdapter(SmartrateCollection.class, new SmartrateCollectionDeserializer()).create();

    public static final Gson PRETTY_PRINT_GSON = new GsonBuilder().setPrettyPrinting().serializeNulls()
            .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
            .registerTypeAdapter(Event.class, new EventDeserializer()).create();

    /**
     * Returns a string representation of the object.
     */
    @Override
    public String toString() {

        return (String) this.getIdString();
    }

    /**
     * Pretty print the JSON representation of the object.
     *
     * @return the JSON representation of the object.
     */
    public String prettyPrint() {
        return String.format("<%s@%s id=%s> JSON: %s", this.getClass().getName(), System.identityHashCode(this),
                this.getIdString(), PRETTY_PRINT_GSON.toJson(this));
    }

    private Object getIdString() {
        try {
            Field idField = this.getClass().getDeclaredField("id");
            return idField.get(this);
        } catch (SecurityException e) {
            return "";
        } catch (NoSuchFieldException e) {
            return "";
        } catch (IllegalArgumentException e) {
            return "";
        } catch (IllegalAccessException e) {
            return "";
        }
    }

    private static String className(final Class<?> clazz) {
        return clazz.getSimpleName().replaceAll("([a-z])([A-Z])", "$1_$2").toLowerCase().replace("$", "");

    }

    protected static String singleClassURL(final Class<?> clazz) {
        return String.format("%s/%s", EasyPost.API_BASE, className(clazz));
    }

    protected static String classURL(final Class<?> clazz) {
        String singleURL = singleClassURL(clazz);
        if (singleURL.charAt(singleURL.length() - 1) == 's' || singleURL.charAt(singleURL.length() - 1) == 'h') {
            return String.format("%ses", singleClassURL(clazz));
        } else {
            return String.format("%ss", singleClassURL(clazz));
        }
    }

    protected static String instanceURL(final Class<?> clazz, final String id) {
        return String.format("%s/%s", classURL(clazz), id);
    }

    /**
     * Merge two EasyPostResource objects.
     *
     * @param obj    the base object
     * @param update the object to merge
     */
    public void merge(final EasyPostResource obj, final EasyPostResource update) {
        if (!obj.getClass().isAssignableFrom(update.getClass())) {
            return;
        }

        Method[] methods = obj.getClass().getMethods();

        for (Method fromMethod : methods) {
            if ((fromMethod.getDeclaringClass().equals(obj.getClass()) && fromMethod.getName().startsWith("get")) ||
                    GLOBAL_FIELD_ACCESSORS.contains(fromMethod.getName())) {

                String fromName = fromMethod.getName();
                String toName = fromName.replace("get", "set");

                try {
                    Object value = fromMethod.invoke(update, (Object[]) null);
                    if (value != null) {
                        Method toMethod = obj.getClass().getMethod(toName, fromMethod.getReturnType());
                        toMethod.invoke(obj, value);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static final String CHARSET = "UTF-8";

    private static final String DNS_CACHE_TTL_PROPERTY_NAME = "networkaddress.cache.ttl";


    // Set this property to override your environment's default URLStreamHandler.
    private static final String CUSTOM_URL_STREAM_HANDLER_PROPERTY_NAME = "com.easypost.net.customURLStreamHandler";

    protected enum RequestMethod {
        GET, POST, DELETE, PUT
    }

    private static String urlEncodePair(final String key, final String value) throws UnsupportedEncodingException {
        return String.format("%s=%s", URLEncoder.encode(key, CHARSET), URLEncoder.encode(value, CHARSET));
    }

    static Map<String, String> getHeaders(String apiKey) {
        Map<String, String> headers = new HashMap<String, String>();
        headers.put("Accept-Charset", CHARSET);
        headers.put("User-Agent", String.format("EasyPost/v2 JavaClient/%s", EasyPost.VERSION));

        if (apiKey == null) {
            apiKey = EasyPost.apiKey;
        }

        headers.put("Authorization", String.format("Bearer %s", apiKey));

        // debug headers
        String[] propertyNames = { "os.name", "os.version", "os.arch", "java.version", "java.vendor", "java.vm.version",
                "java.vm.vendor" };
        Map<String, String> propertyMap = new HashMap<String, String>();
        for (String propertyName : propertyNames) {
            propertyMap.put(propertyName, System.getProperty(propertyName));
        }
        propertyMap.put("lang", "Java");
        propertyMap.put("publisher", "EasyPost");
        headers.put("X-Client-User-Agent", GSON.toJson(propertyMap));

        return headers;
    }

    private static javax.net.ssl.HttpsURLConnection createEasyPostConnection(final String url, final String apiKey)
            throws IOException {
        URL easypostURL = null;
        String customURLStreamHandlerClassName = System.getProperty(CUSTOM_URL_STREAM_HANDLER_PROPERTY_NAME, null);
        if (customURLStreamHandlerClassName != null) {<commit_msg>add Java version to the User-Agent in requests (#83)<commit_after>/**
 * EasyPostResource.java
 * This file is a part of EasyPost API SDK.
 * (c) 2022 EasyPost
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
package com.easypost.net;

import com.easypost.EasyPost;
import com.easypost.exception.EasyPostException;
import com.easypost.model.Event;
import com.easypost.model.EventDeserializer;
import com.easypost.model.Fee;
import com.easypost.model.Rate;
import com.easypost.model.RateDeserializer;
import com.easypost.model.Shipment;
import com.easypost.model.SmartrateCollection;
import com.easypost.model.SmartrateCollectionDeserializer;
import com.easypost.model.TrackingDetail;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.net.URLStreamHandler;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;

public abstract class EasyPostResource {
    public static final String EASYPOST_SUPPORT_EMAIL = "<EMAIL>";

    private static final int DEFAULT_CONNECT_TIMEOUT_MILLISECONDS = 30000;
    private static final int DEFAULT_READ_TIMEOUT_MILLISECONDS = 60000;
    private static final double APP_ENGINE_DEFAULT_TIMEOUT_SECONDS = 20.0;

    public static final Gson GSON =
            new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
                    .registerTypeAdapter(Event.class, new EventDeserializer())
                    .registerTypeAdapter(Rate.class, new RateDeserializer())
                    .registerTypeAdapter(SmartrateCollection.class, new SmartrateCollectionDeserializer()).create();

    public static final Gson PRETTY_PRINT_GSON = new GsonBuilder().setPrettyPrinting().serializeNulls()
            .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
            .registerTypeAdapter(Event.class, new EventDeserializer()).create();

    /**
     * Returns a string representation of the object.
     */
    @Override
    public String toString() {

        return (String) this.getIdString();
    }

    /**
     * Pretty print the JSON representation of the object.
     *
     * @return the JSON representation of the object.
     */
    public String prettyPrint() {
        return String.format("<%s@%s id=%s> JSON: %s", this.getClass().getName(), System.identityHashCode(this),
                this.getIdString(), PRETTY_PRINT_GSON.toJson(this));
    }

    private Object getIdString() {
        try {
            Field idField = this.getClass().getDeclaredField("id");
            return idField.get(this);
        } catch (SecurityException e) {
            return "";
        } catch (NoSuchFieldException e) {
            return "";
        } catch (IllegalArgumentException e) {
            return "";
        } catch (IllegalAccessException e) {
            return "";
        }
    }

    private static String className(final Class<?> clazz) {
        return clazz.getSimpleName().replaceAll("([a-z])([A-Z])", "$1_$2").toLowerCase().replace("$", "");

    }

    protected static String singleClassURL(final Class<?> clazz) {
        return String.format("%s/%s", EasyPost.API_BASE, className(clazz));
    }

    protected static String classURL(final Class<?> clazz) {
        String singleURL = singleClassURL(clazz);
        if (singleURL.charAt(singleURL.length() - 1) == 's' || singleURL.charAt(singleURL.length() - 1) == 'h') {
            return String.format("%ses", singleClassURL(clazz));
        } else {
            return String.format("%ss", singleClassURL(clazz));
        }
    }

    protected static String instanceURL(final Class<?> clazz, final String id) {
        return String.format("%s/%s", classURL(clazz), id);
    }

    /**
     * Merge two EasyPostResource objects.
     *
     * @param obj    the base object
     * @param update the object to merge
     */
    public void merge(final EasyPostResource obj, final EasyPostResource update) {
        if (!obj.getClass().isAssignableFrom(update.getClass())) {
            return;
        }

        Method[] methods = obj.getClass().getMethods();

        for (Method fromMethod : methods) {
            if ((fromMethod.getDeclaringClass().equals(obj.getClass()) && fromMethod.getName().startsWith("get")) ||
                    GLOBAL_FIELD_ACCESSORS.contains(fromMethod.getName())) {

                String fromName = fromMethod.getName();
                String toName = fromName.replace("get", "set");

                try {
                    Object value = fromMethod.invoke(update, (Object[]) null);
                    if (value != null) {
                        Method toMethod = obj.getClass().getMethod(toName, fromMethod.getReturnType());
                        toMethod.invoke(obj, value);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static final String CHARSET = "UTF-8";

    private static final String DNS_CACHE_TTL_PROPERTY_NAME = "networkaddress.cache.ttl";


    // Set this property to override your environment's default URLStreamHandler.
    private static final String CUSTOM_URL_STREAM_HANDLER_PROPERTY_NAME = "com.easypost.net.customURLStreamHandler";

    protected enum RequestMethod {
        GET, POST, DELETE, PUT
    }

    private static String urlEncodePair(final String key, final String value) throws UnsupportedEncodingException {
        return String.format("%s=%s", URLEncoder.encode(key, CHARSET), URLEncoder.encode(value, CHARSET));
    }

    static Map<String, String> getHeaders(String apiKey) {
        Map<String, String> headers = new HashMap<String, String>();
        headers.put("Accept-Charset", CHARSET);
        headers.put("User-Agent", String.format("EasyPost/v2 JavaClient/%s Java/%s"
                                , EasyPost.VERSION, System.getProperty("java.version")));

        if (apiKey == null) {
            apiKey = EasyPost.apiKey;
        }

        headers.put("Authorization", String.format("Bearer %s", apiKey));

        // debug headers
        String[] propertyNames = { "os.name", "os.version", "os.arch", "java.version", "java.vendor", "java.vm.version",
                "java.vm.vendor" };
        Map<String, String> propertyMap = new HashMap<String, String>();
        for (String propertyName : propertyNames) {
            propertyMap.put(propertyName, System.getProperty(propertyName));
        }
        propertyMap.put("lang", "Java");
        propertyMap.put("publisher", "EasyPost");
        headers.put("X-Client-User-Agent", GSON.toJson(propertyMap));

        return headers;
    }

    private static javax.net.ssl.HttpsURLConnection createEasyPostConnection(final String url, final String apiKey)
            throws IOException {
        URL easypostURL = null;
        String customURLStreamHandlerClassName = System.getProperty(CUSTOM_URL_STREAM_HANDLER_PROPERTY_NAME, null);
        if (customURLStreamHandlerClassName != null) {
{'fraction_non_alphanumeric': 0.11204167765761013, 'fraction_numerical': 0.0031653917172250064, 'mean_word_length': 2.94921875, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '974804', 'n_tokens_mistral': 4339, 'n_tokens_neox': 4202, 'n_words': 956}