// Path: src/main/java/com/gocardless/errors/ApiErrorResponse.java
// public class ApiErrorResponse {
//     private static final Joiner JOINER = Joiner.on(", ");
//     private final String message;
//     private final ErrorType type;
//     private final String documentationUrl;
//     private final String requestId;
//     private final int code;
//     private final List<ApiError> errors;
// 
//     private ApiErrorResponse(String message, ErrorType type, String documentationUrl,
//             String requestId, int code, List<ApiError> errors) {
//         this.message = message;
//         this.type = type;
//         this.documentationUrl = documentationUrl;
//         this.requestId = requestId;
//         this.code = code;
//         this.errors = errors;
//     }
// 
//     String getMessage() {
//         return message;
//     }
// 
//     ErrorType getType() {
//         return type;
//     }
// 
//     String getDocumentationUrl() {
//         return documentationUrl;
//     }
// 
//     String getRequestId() {
//         return requestId;
//     }
// 
//     int getCode() {
//         return code;
//     }
// 
//     List<ApiError> getErrors() {
//         if (errors == null) {
//             return ImmutableList.of();
//         }
//         return ImmutableList.copyOf(errors);
//     }
// 
//     @Override
//     public String toString() {
//         if (errors == null || errors.isEmpty()) {
//             return message;
//         } else {
//             return JOINER.join(errors);
//         }
//     }
// }
// 
// Path: src/main/java/com/gocardless/errors/GoCardlessApiException.java
// public class GoCardlessApiException extends GoCardlessException {
//     private final ApiErrorResponse error;
// 
//     GoCardlessApiException(ApiErrorResponse error) {
//         super(error.toString());
//         this.error = error;
//     }
// 
//     /**
//      * Returns a human-readable description of the error.
//      */
//     public String getErrorMessage() {
//         return error.getMessage();
//     }
// 
//     /**
//      * Returns the type of the error.
//      */
//     public ErrorType getType() {
//         return error.getType();
//     }
// 
//     /**
//      * Returns the URL to the documentation describing the error.
//      */
//     public String getDocumentationUrl() {
//         return error.getDocumentationUrl();
//     }
// 
//     /**
//      * Returns the ID of the request. This can be used to help the support team find your error
//      * quickly.
//      */
//     public String getRequestId() {
//         return error.getRequestId();
//     }
// 
//     /**
//      * Returns the HTTP status code.
//      */
//     public int getCode() {
//         return error.getCode();
//     }
// 
//     /**
//      * Returns a list of errors.
//      */
//     public List<ApiError> getErrors() {
//         return error.getErrors();
//     }
// 
//     @Override
//     public String toString() {
//         return error.toString();
//     }
// }
// 
// Path: src/main/java/com/gocardless/errors/GoCardlessErrorMapper.java
// public class GoCardlessErrorMapper {
//     /**
//      * Maps an error response to an exception.
//      *
//      * @param error the error response to map
//      */
//     public static GoCardlessApiException toException(ApiErrorResponse error) {
//         switch (error.getCode()) {
//             case 401:
//                 return new AuthenticationException(error);
//             case 403:
//                 return new PermissionException(error);
//             case 429:
//                 return new RateLimitException(error);
//         }
//         switch (error.getType()) {
//             case GOCARDLESS:
//                 return new GoCardlessInternalException(error);
//             case INVALID_API_USAGE:
//                 return new InvalidApiUsageException(error);
//             case INVALID_STATE:
//                 return new InvalidStateException(error);
//             case VALIDATION_FAILED:
//                 return new ValidationFailedException(error);
//         }
//         throw new IllegalStateException("Unknown error type: " + error.getType());
//     }
// }
// 
// Path: src/main/java/com/gocardless/errors/MalformedResponseException.java
// public class MalformedResponseException extends GoCardlessException {
//     private final String responseBody;
// 
//     public MalformedResponseException(String responseBody) {
//         super("Malformed response received from server");
//         this.responseBody = responseBody;
//     }
// 
//     public String getResponseBody() {
//         return responseBody;
//     }
// }


import com.gocardless.errors.ApiErrorResponse;
import com.gocardless.errors.GoCardlessApiException;
import com.gocardless.errors.GoCardlessErrorMapper;
import com.gocardless.errors.MalformedResponseException;
import com.google.common.collect.ImmutableList;
import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import java.util.List;
package com.gocardless.http;


final class ResponseParser {
    private final Gson gson;

    ResponseParser(Gson gson) {
        this.gson = gson;
    }

    <T> T parseSingle(String responseBody, String envelope, Class<T> clazz) {
        JsonElement json;
        try {
            json = new JsonParser().parse(responseBody);
        } catch (JsonSyntaxException e) {
            throw new MalformedResponseException(responseBody);
        }
        JsonObject object = json.getAsJsonObject().getAsJsonObject(envelope);
        return gson.fromJson(object, clazz);
    }

    <T> ImmutableList<T> parseMultiple(String responseBody, String envelope,
            TypeToken<List<T>> clazz) {
        JsonObject json = new JsonParser().parse(responseBody).getAsJsonObject();
        JsonArray array = json.getAsJsonArray(envelope);
        List<T> items = gson.fromJson(array, clazz.getType());
        return ImmutableList.copyOf(items);
    }

    <T> ImmutableList<T> parseMultiple(JsonObject json, String envelope, TypeToken<List<T>> clazz) {
// Path: src/main/java/com/gocardless/errors/ApiErrorResponse.java
// public class ApiErrorResponse {
//     private static final Joiner JOINER = Joiner.on(", ");
//     private final String message;
//     private final ErrorType type;
//     private final String documentationUrl;
//     private final String requestId;
//     private final int code;
//     private final List<ApiError> errors;
// 
//     private ApiErrorResponse(String message, ErrorType type, String documentationUrl,
//             String requestId, int code, List<ApiError> errors) {
//         this.message = message;
//         this.type = type;
//         this.documentationUrl = documentationUrl;
//         this.requestId = requestId;
//         this.code = code;
//         this.errors = errors;
//     }
// 
//     String getMessage() {
//         return message;
//     }
// 
//     ErrorType getType() {
//         return type;
//     }
// 
//     String getDocumentationUrl() {
//         return documentationUrl;
//     }
// 
//     String getRequestId() {
//         return requestId;
//     }
// 
//     int getCode() {
//         return code;
//     }
// 
//     List<ApiError> getErrors() {
//         if (errors == null) {
//             return ImmutableList.of();
//         }
//         return ImmutableList.copyOf(errors);
//     }
// 
//     @Override
//     public String toString() {
//         if (errors == null || errors.isEmpty()) {
//             return message;
//         } else {
//             return JOINER.join(errors);
//         }
//     }
// }
// 
// Path: src/main/java/com/gocardless/errors/GoCardlessApiException.java
// public class GoCardlessApiException extends GoCardlessException {
//     private final ApiErrorResponse error;
// 
//     GoCardlessApiException(ApiErrorResponse error) {
//         super(error.toString());
//         this.error = error;
//     }
// 
//     /**
//      * Returns a human-readable description of the error.
//      */
//     public String getErrorMessage() {
//         return error.getMessage();
//     }
// 
//     /**
//      * Returns the type of the error.
//      */
//     public ErrorType getType() {
//         return error.getType();
//     }
// 
//     /**
//      * Returns the URL to the documentation describing the error.
//      */
//     public String getDocumentationUrl() {
//         return error.getDocumentationUrl();
//     }
// 
//     /**
//      * Returns the ID of the request. This can be used to help the support team find your error
//      * quickly.
//      */
//     public String getRequestId() {
//         return error.getRequestId();
//     }
// 
//     /**
//      * Returns the HTTP status code.
//      */
//     public int getCode() {
//         return error.getCode();
//     }
// 
//     /**
//      * Returns a list of errors.
//      */
//     public List<ApiError> getErrors() {
//         return error.getErrors();
//     }
// 
//     @Override
//     public String toString() {
//         return error.toString();
//     }
// }
// 
// Path: src/main/java/com/gocardless/errors/GoCardlessErrorMapper.java
// public class GoCardlessErrorMapper {
//     /**
//      * Maps an error response to an exception.
//      *
//      * @param error the error response to map
//      */
//     public static GoCardlessApiException toException(ApiErrorResponse error) {
//         switch (error.getCode()) {
//             case 401:
//                 return new AuthenticationException(error);
//             case 403:
//                 return new PermissionException(error);
//             case 429:
//                 return new RateLimitException(error);
//         }
//         switch (error.getType()) {
//             case GOCARDLESS:
//                 return new GoCardlessInternalException(error);
//             case INVALID_API_USAGE:
//                 return new InvalidApiUsageException(error);
//             case INVALID_STATE:
//                 return new InvalidStateException(error);
//             case VALIDATION_FAILED:
//                 return new ValidationFailedException(error);
//         }
//         throw new IllegalStateException("Unknown error type: " + error.getType());
//     }
// }
// 
// Path: src/main/java/com/gocardless/errors/MalformedResponseException.java
// public class MalformedResponseException extends GoCardlessException {
//     private final String responseBody;
// 
//     public MalformedResponseException(String responseBody) {
//         super("Malformed response received from server");
//         this.responseBody = responseBody;
//     }
// 
//     public String getResponseBody() {
//         return responseBody;
//     }
// }


// Path: src/main/java/com/gocardless/http/ResponseParser.java
import com.gocardless.errors.ApiErrorResponse;
import com.gocardless.errors.GoCardlessApiException;
import com.gocardless.errors.GoCardlessErrorMapper;
import com.gocardless.errors.MalformedResponseException;
import com.google.common.collect.ImmutableList;
import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import java.util.List;

package com.gocardless.http;


final class ResponseParser {
    private final Gson gson;

    ResponseParser(Gson gson) {
        this.gson = gson;
    }

    <T> T parseSingle(String responseBody, String envelope, Class<T> clazz) {
        JsonElement json;
        try {
            json = new JsonParser().parse(responseBody);
        } catch (JsonSyntaxException e) {
            throw new MalformedResponseException(responseBody);
        }
        JsonObject object = json.getAsJsonObject().getAsJsonObject(envelope);
        return gson.fromJson(object, clazz);
    }

    <T> ImmutableList<T> parseMultiple(String responseBody, String envelope,
            TypeToken<List<T>> clazz) {
        JsonObject json = new JsonParser().parse(responseBody).getAsJsonObject();
        JsonArray array = json.getAsJsonArray(envelope);
        List<T> items = gson.fromJson(array, clazz.getType());
        return ImmutableList.copyOf(items);
    }

    <T> ImmutableList<T> parseMultiple(JsonObject json, String envelope, TypeToken<List<T>> clazz) {
        JsonArray array = json.getAsJsonArray(envelope);