From 114655620c3a71b00d51e0590b448dbb36bcd7f5 Mon Sep 17 00:00:00 2001 From: ryber Date: Sun, 10 May 2026 06:52:13 -0500 Subject: [PATCH] update and add javadoc --- .../java/kong/unirest/core/BaseResponse.java | 102 +++++ .../java/kong/unirest/core/BasicResponse.java | 55 +++ .../src/main/java/kong/unirest/core/Body.java | 88 ++++ .../main/java/kong/unirest/core/BodyPart.java | 86 +++- .../java/kong/unirest/core/ByteArrayPart.java | 42 ++ .../java/kong/unirest/core/ByteResponse.java | 50 +++ .../main/java/kong/unirest/core/Cache.java | 183 +++++--- .../main/java/kong/unirest/core/Callback.java | 72 ++- .../main/java/kong/unirest/core/Client.java | 89 ++-- .../main/java/kong/unirest/core/Config.java | 187 ++++++-- .../java/kong/unirest/core/ContentType.java | 139 +++++- .../main/java/kong/unirest/core/Cookie.java | 137 ++++-- .../kong/unirest/core/FailedResponse.java | 129 ++++-- .../java/kong/unirest/core/GenericType.java | 60 ++- .../main/java/kong/unirest/core/Headers.java | 130 ++++-- .../java/kong/unirest/core/HttpMethod.java | 52 +++ .../java/kong/unirest/core/HttpRequest.java | 2 + .../java/kong/unirest/core/HttpResponse.java | 119 +++-- .../java/kong/unirest/core/HttpStatus.java | 140 ++++++ .../kong/unirest/core/json/JsonEngine.java | 425 ++++++++++++++++++ 20 files changed, 1993 insertions(+), 294 deletions(-) diff --git a/unirest/src/main/java/kong/unirest/core/BaseResponse.java b/unirest/src/main/java/kong/unirest/core/BaseResponse.java index 10f41a1a7..4e932e3f3 100644 --- a/unirest/src/main/java/kong/unirest/core/BaseResponse.java +++ b/unirest/src/main/java/kong/unirest/core/BaseResponse.java @@ -30,6 +30,19 @@ import java.util.function.Consumer; import java.util.function.Function; +/** + * Abstract base implementation of {@link HttpResponse} that provides common functionality + * for handling HTTP responses. + *

+ * This class handles the core response data including status code, status text, headers, + * and cookies. It also provides utility methods for success/failure handling, body mapping, + * and error parsing. + *

+ * Subclasses must implement {@link #getBody()} and {@link #getRawBody()} to provide + * the actual response body content. + * + * @param the type of the response body + */ abstract class BaseResponse implements HttpResponse { private final Headers headers; @@ -41,6 +54,15 @@ abstract class BaseResponse implements HttpResponse { private Cookies cookies; + /** + * Constructs a new BaseResponse from a raw HTTP response. + *

+ * This constructor extracts headers, status code, status text, and configuration + * from the raw response. It also removes the "Content-Encoding: gzip" header + * since Unirest automatically decompresses the content. + * + * @param response the raw HTTP response to extract data from + */ protected BaseResponse(RawResponse response) { this.headers = response.getHeaders(); // Unirest decompresses the content, so this should be removed as it is @@ -52,6 +74,13 @@ protected BaseResponse(RawResponse response) { this.reqSummary = response.getRequestSummary(); } + /** + * Copy constructor that creates a new BaseResponse from another BaseResponse. + *

+ * This is used internally when mapping responses to new types. + * + * @param other the BaseResponse to copy from + */ protected BaseResponse(BaseResponse other) { this.headers = other.headers; this.statusCode = other.statusCode; @@ -60,48 +89,84 @@ protected BaseResponse(BaseResponse other) { this.reqSummary = other.reqSummary; } + /** + * {@inheritDoc} + */ @Override public int getStatus() { return statusCode; } + /** + * {@inheritDoc} + */ @Override public String getStatusText() { return statusText; } + /** + * {@inheritDoc} + */ @Override public Headers getHeaders() { return headers; } + /** + * {@inheritDoc} + */ @Override public abstract T getBody(); + /** + * {@inheritDoc} + */ @Override public Optional getParsingError() { return parsingerror; } + /** + * {@inheritDoc} + */ @Override public V mapBody(Function func) { return func.apply(getBody()); } + /** + * {@inheritDoc} + */ @Override public HttpResponse map(Function func) { return new BasicResponse(this, mapBody(func)); } + /** + * Sets a parsing exception that occurred while processing the response body. + * + * @param originalBody the original body content that failed to parse + * @param e the exception that was thrown during parsing + */ protected void setParsingException(String originalBody, RuntimeException e) { parsingerror = Optional.of(new UnirestParsingException(originalBody, e)); } + /** + * {@inheritDoc} + *

+ * A response is considered successful if the status code is in the 2xx range + * (200-299) and no parsing error occurred. + */ @Override public boolean isSuccess() { return getStatus() >= 200 && getStatus() < 300 && !getParsingError().isPresent(); } + /** + * {@inheritDoc} + */ @Override public HttpResponse ifSuccess(Consumer> consumer) { if (isSuccess()) { @@ -110,6 +175,9 @@ public HttpResponse ifSuccess(Consumer> consumer) { return this; } + /** + * {@inheritDoc} + */ @Override public HttpResponse ifFailure(Consumer> consumer) { if (!isSuccess()) { @@ -118,6 +186,9 @@ public HttpResponse ifFailure(Consumer> consumer) { return this; } + /** + * {@inheritDoc} + */ @Override public E mapError(Class errorClass) { if (!isSuccess()) { @@ -134,6 +205,18 @@ public E mapError(Class errorClass) { return null; } + /** + * Retrieves the error body as a string for error mapping purposes. + *

+ * This method attempts to get the error body from multiple sources: + *

    + *
  1. From a parsing exception's original body, if present
  2. + *
  3. From the raw body string, if available
  4. + *
  5. By serializing the body object back to a string
  6. + *
+ * + * @return the error body as a string, or {@code null} if no body is available + */ private String getErrorBody() { if (getParsingError().isPresent()) { return getParsingError().get().getOriginalBody(); @@ -154,6 +237,9 @@ private String getErrorBody() { } } + /** + * {@inheritDoc} + */ @Override public HttpResponse ifFailure(Class errorClass, Consumer> consumer) { if (!isSuccess()) { @@ -165,6 +251,11 @@ public HttpResponse ifFailure(Class errorClass, Consumer + * Cookies are lazily parsed from the "set-cookie" headers on first access. + */ @Override public Cookies getCookies() { if (cookies == null) { @@ -173,10 +264,21 @@ public Cookies getCookies() { return cookies; } + /** + * {@inheritDoc} + */ @Override public HttpRequestSummary getRequestSummary() { return reqSummary; } + /** + * Returns the raw response body as a string. + *

+ * This method is used internally for error body retrieval when the parsed + * body is not available or appropriate. + * + * @return the raw response body as a string, or {@code null} if not available + */ protected abstract String getRawBody(); } diff --git a/unirest/src/main/java/kong/unirest/core/BasicResponse.java b/unirest/src/main/java/kong/unirest/core/BasicResponse.java index 2ad4f6a07..bb1c38663 100644 --- a/unirest/src/main/java/kong/unirest/core/BasicResponse.java +++ b/unirest/src/main/java/kong/unirest/core/BasicResponse.java @@ -25,34 +25,89 @@ package kong.unirest.core; +/** + * A basic implementation of {@link HttpResponse} that holds a pre-parsed response body. + *

+ * This class wraps an HTTP response along with its parsed body content. It is typically + * used internally by Unirest after transforming the raw response into the desired type. + * + * @param the type of the response body + * @see BaseResponse + * @see HttpResponse + */ public class BasicResponse extends BaseResponse { private final T body; + /** + * Creates a new BasicResponse by copying metadata from another response. + * + * @param response the source response to copy metadata from (status, headers, etc.) + * @param body the parsed response body + */ BasicResponse(BaseResponse response, T body) { super(response); this.body = body; } + /** + * Creates a new BasicResponse from a raw HTTP response with a parsed body. + * + * @param httpResponse the raw HTTP response containing status, headers, and other metadata + * @param body the parsed response body + */ public BasicResponse(RawResponse httpResponse, T body) { super(httpResponse); this.body = body; } + /** + * Creates a new BasicResponse from a raw HTTP response with no body. + *

+ * This constructor is useful for responses that have no content or when + * the body is not needed. + * + * @param httpResponse the raw HTTP response containing status, headers, and other metadata + */ public BasicResponse(RawResponse httpResponse) { super(httpResponse); this.body = null; } + /** + * Creates a new BasicResponse that captures a parsing exception. + *

+ * This constructor is used when the response body could not be parsed into the + * expected type. The original body content and exception are preserved for + * debugging and error handling. + * + * @param httpResponse the raw HTTP response + * @param ogBody the original response body as a string before parsing failed + * @param ex the exception that occurred during parsing + */ public BasicResponse(RawResponse httpResponse, String ogBody, RuntimeException ex) { this(httpResponse, null); setParsingException(ogBody, ex); } + /** + * Returns the parsed response body. + * + * @return the response body, or {@code null} if no body was set or parsing failed + */ @Override public T getBody() { return body; } + /** + * Returns the raw unparsed response body. + *

+ * This implementation always returns {@code null} as the raw body is not + * retained after parsing. To access the original body when a parsing error + * occurs, use {@link #getParsingError()}. + * + * @return always {@code null} + */ @Override protected String getRawBody() { return null; diff --git a/unirest/src/main/java/kong/unirest/core/Body.java b/unirest/src/main/java/kong/unirest/core/Body.java index 0e61c613a..8aec9a2e0 100644 --- a/unirest/src/main/java/kong/unirest/core/Body.java +++ b/unirest/src/main/java/kong/unirest/core/Body.java @@ -31,35 +31,123 @@ import java.util.Collection; import java.util.Collections; +/** + * Represents the body of an HTTP request. + *

+ * This interface provides access to request body content, which can be either: + *

    + *
  • A multipart body containing multiple {@link BodyPart} elements (e.g., file uploads with form fields)
  • + *
  • A single entity body (e.g., JSON, XML, or raw content)
  • + *
+ * Implementations handle the serialization and encoding of body content + * for transmission in HTTP requests. + * + * @see BodyPart + * @see MultipartBody + */ public interface Body { + + /** + * Indicates whether this body is a multipart body. + *

+ * Multipart bodies contain multiple parts, typically used for file uploads + * combined with form fields. + * + * @return {@code true} if this is a multipart body, {@code false} otherwise + */ boolean isMultiPart(); + /** + * Indicates whether this body is a single entity body. + *

+ * Entity bodies contain a single piece of content, such as JSON, XML, + * or raw binary data. + * + * @return {@code true} if this is an entity body, {@code false} otherwise + */ boolean isEntityBody(); + /** + * Returns the character set used for encoding the body content. + *

+ * The default implementation returns {@link StandardCharsets#UTF_8}. + * + * @return the {@link Charset} for body encoding + */ default Charset getCharset(){ return StandardCharsets.UTF_8; } + /** + * Returns the collection of body parts for a multipart body. + *

+ * The default implementation returns an empty collection. Multipart body + * implementations should override this to return their parts. + * + * @return a {@link Collection} of {@link BodyPart} elements, or an empty collection + * if this is not a multipart body + */ default Collection multiParts(){ return Collections.emptyList(); } + /** + * Returns the single body part for a non-multipart body. + *

+ * The default implementation returns {@code null}. Entity body + * implementations should override this to return their content. + * + * @return the single {@link BodyPart}, or {@code null} if not applicable + */ default BodyPart uniPart(){ return null; } + /** + * Returns the multipart mode for encoding. + *

+ * The mode determines how multipart content is formatted and encoded. + * The default implementation returns {@link MultipartMode#BROWSER_COMPATIBLE}. + * + * @return the {@link MultipartMode} for this body + */ default MultipartMode getMode(){ return MultipartMode.BROWSER_COMPATIBLE; } + /** + * Returns the progress monitor for tracking upload progress. + *

+ * The default implementation returns {@code null}, indicating no progress monitoring. + * + * @return the {@link ProgressMonitor}, or {@code null} if progress monitoring is not enabled + */ default ProgressMonitor getMonitor(){ return null; } + /** + * Returns the boundary string used to separate parts in a multipart body. + *

+ * The boundary is a unique string that delimits each part in the multipart + * message. The default implementation returns {@code null}, allowing the + * system to generate a boundary automatically. + * + * @return the boundary string, or {@code null} to use an auto-generated boundary + */ default String getBoundary() { return null; } + /** + * Finds and returns a body part by its field name. + *

+ * Searches through the multipart body parts and returns the first part + * whose name matches the specified field name. + * + * @param name the field name to search for + * @return the matching {@link BodyPart}, or {@code null} if no part with the given name exists + */ default BodyPart getField(String name){ return multiParts() .stream() diff --git a/unirest/src/main/java/kong/unirest/core/BodyPart.java b/unirest/src/main/java/kong/unirest/core/BodyPart.java index 787c5a238..5ff4ca094 100644 --- a/unirest/src/main/java/kong/unirest/core/BodyPart.java +++ b/unirest/src/main/java/kong/unirest/core/BodyPart.java @@ -27,6 +27,19 @@ import java.nio.charset.StandardCharsets; +/** + * Represents a single part of a multipart HTTP request body. + *

+ * Body parts are used when building multipart form requests, such as file uploads + * or forms with multiple fields. Each part has a name, value, content type, and + * optional headers. + *

+ * This is an abstract class; concrete implementations handle specific value types + * such as strings, files, or input streams. + * + * @param the type of the body part value (e.g., String, File, InputStream) + * @see MultipartBody + */ public abstract class BodyPart implements Comparable { ; private final String name; private final T value; @@ -34,10 +47,25 @@ public abstract class BodyPart implements Comparable { ; private final Class partType; private final Headers headers; + /** + * Creates a new body part with the specified value, name, and content type. + * + * @param value the value of this body part + * @param name the name (field name) of this body part + * @param contentType the MIME content type, or {@code null} to use defaults + */ protected BodyPart(T value, String name, String contentType) { this(value, name, contentType, null); } + /** + * Creates a new body part with the specified value, name, content type, and headers. + * + * @param value the value of this body part + * @param name the name (field name) of this body part + * @param contentType the MIME content type, or {@code null} to use defaults + * @param headers additional headers for this part, or {@code null} for none + */ protected BodyPart(T value, String name, String contentType, Headers headers) { this.name = name; this.value = value; @@ -46,14 +74,35 @@ protected BodyPart(T value, String name, String contentType, Headers headers) { this.headers = headers; } + /** + * Returns the value of this body part. + * + * @return the body part value + */ public T getValue() { return value; } + /** + * Returns the runtime class of the body part value. + * + * @return the {@link Class} object representing the value's type + */ public Class getPartType(){ return partType; } + /** + * Returns the content type for this body part. + *

+ * If no content type was explicitly set: + *

    + *
  • File parts default to {@code application/octet-stream}
  • + *
  • Non-file parts default to {@code application/x-www-form-urlencoded; charset=UTF-8}
  • + *
+ * + * @return the MIME content type string + */ public String getContentType() { if(contentType == null){ if(isFile()){ @@ -64,14 +113,37 @@ public String getContentType() { return contentType; } + /** + * Returns the name (field name) of this body part. + * + * @return the name, or an empty string if no name was set + */ public String getName() { return name == null ? "" : name; } + /** + * Returns the file name for this body part. + *

+ * By default, this returns the same value as {@link #getName()}. + * Subclasses may override this to provide a different file name. + * + * @return the file name + */ public String getFileName(){ return name; } + /** + * Compares this body part to another based on their names. + *

+ * This comparison is used for ordering body parts alphabetically by name. + * + * @param o the object to compare to + * @return a negative integer, zero, or a positive integer if this part's name + * is less than, equal to, or greater than the other part's name; + * returns 0 if the object is not a {@link BodyPart} + */ @Override public int compareTo(Object o) { if(o instanceof BodyPart){ @@ -80,9 +152,21 @@ public int compareTo(Object o) { return 0; } - + /** + * Indicates whether this body part represents a file. + *

+ * File parts are handled differently during multipart encoding, + * including the use of different default content types. + * + * @return {@code true} if this part represents a file, {@code false} otherwise + */ abstract public boolean isFile(); + /** + * Returns the additional headers for this body part. + * + * @return the {@link Headers} for this part, or {@code null} if no additional headers were set + */ public Headers getHeaders() { return headers; } diff --git a/unirest/src/main/java/kong/unirest/core/ByteArrayPart.java b/unirest/src/main/java/kong/unirest/core/ByteArrayPart.java index f02cb2620..e61555d11 100644 --- a/unirest/src/main/java/kong/unirest/core/ByteArrayPart.java +++ b/unirest/src/main/java/kong/unirest/core/ByteArrayPart.java @@ -26,28 +26,70 @@ package kong.unirest.core; +/** + * A multipart form body part that wraps a byte array as file content. + *

+ * This class is used to upload in-memory binary data as a file in multipart + * form requests. The byte array is treated as file content with an associated + * filename and content type. + * + * @see BodyPart + * @see MultipartBody#field(String, byte[], ContentType, String) + */ public class ByteArrayPart extends BodyPart { private final String fileName; + /** + * Constructs a new ByteArrayPart with the specified parameters. + * + * @param name the form field name for this part + * @param bytes the byte array content to upload + * @param contentType the MIME content type of the data + * @param fileName the filename to associate with this part + */ ByteArrayPart(String name, byte[] bytes, ContentType contentType, String fileName) { this(name, bytes, contentType, fileName, null); } + /** + * Constructs a new ByteArrayPart with the specified parameters and custom headers. + * + * @param name the form field name for this part + * @param bytes the byte array content to upload + * @param contentType the MIME content type of the data + * @param fileName the filename to associate with this part + * @param headers additional headers to include with this part, or {@code null} for none + */ ByteArrayPart(String name, byte[] bytes, ContentType contentType, String fileName, Headers headers) { super(bytes, name, contentType.toString(), headers); this.fileName = fileName; } + /** + * Returns the filename associated with this byte array part. + * + * @return the filename for this part + */ @Override public String getFileName() { return fileName; } + /** + * Indicates that this part represents file content. + * + * @return {@code true} always, as byte array parts are treated as file uploads + */ @Override public boolean isFile() { return true; } + /** + * Returns a string representation of this part in the format "name=fileName". + * + * @return a string representation of this part + */ @Override public String toString() { return String.format("%s=%s", getName(), fileName); diff --git a/unirest/src/main/java/kong/unirest/core/ByteResponse.java b/unirest/src/main/java/kong/unirest/core/ByteResponse.java index 575d01b48..7750e5ca6 100644 --- a/unirest/src/main/java/kong/unirest/core/ByteResponse.java +++ b/unirest/src/main/java/kong/unirest/core/ByteResponse.java @@ -30,9 +30,30 @@ import java.io.IOException; import java.io.InputStream; +/** + * An HTTP response implementation that provides the response body as a byte array. + *

+ * This class is used when the response content needs to be consumed as raw bytes, + * such as when downloading binary files. It supports optional progress monitoring + * for tracking download progress. + * + * @see BaseResponse + * @see HttpResponse + */ public class ByteResponse extends BaseResponse { private final byte[] body; + /** + * Creates a new ByteResponse from a raw HTTP response. + *

+ * If a progress monitor is provided, the download progress will be tracked + * and reported through the monitor. Otherwise, the content is read directly. + * + * @param r the raw HTTP response to read the body from + * @param downloadMonitor the progress monitor for tracking download progress, + * or {@code null} for no monitoring + * @throws UnirestException if an I/O error occurs while reading the response body + */ public ByteResponse(RawResponse r, ProgressMonitor downloadMonitor) { super(r); if(downloadMonitor == null) { @@ -47,6 +68,16 @@ public ByteResponse(RawResponse r, ProgressMonitor downloadMonitor) { } } + /** + * Reads all bytes from an input stream into a byte array. + *

+ * This utility method handles both {@link ByteArrayInputStream} (optimized path) + * and general input streams. The input stream is closed after reading. + * + * @param is the input stream to read from + * @return a byte array containing all bytes read from the stream + * @throws IOException if an I/O error occurs while reading the stream + */ public static byte[] getBytes(InputStream is) throws IOException { try { int len; @@ -71,15 +102,34 @@ public static byte[] getBytes(InputStream is) throws IOException { } } + /** + * Checks if a content encoding value indicates gzip compression. + * + * @param value the content encoding header value to check + * @return {@code true} if the value indicates gzip encoding, {@code false} otherwise + */ public static boolean isGzipped(String value) { return "gzip".equalsIgnoreCase(value.toLowerCase().trim()); } + /** + * Returns the response body as a byte array. + * + * @return the response body bytes + */ @Override public byte[] getBody() { return body; } + /** + * Returns the raw body as a string. + *

+ * This implementation always returns {@code null} since the byte response + * does not retain a string representation of the body. + * + * @return {@code null} + */ @Override protected String getRawBody() { return null; diff --git a/unirest/src/main/java/kong/unirest/core/Cache.java b/unirest/src/main/java/kong/unirest/core/Cache.java index 1c572d183..a17e85a9f 100644 --- a/unirest/src/main/java/kong/unirest/core/Cache.java +++ b/unirest/src/main/java/kong/unirest/core/Cache.java @@ -31,44 +31,79 @@ import java.util.function.Supplier; /** - * Cache interface for response caching + * Cache interface for HTTP response caching. + *

+ * Implement this interface to provide custom caching strategies for HTTP responses. + * The cache stores responses keyed by request characteristics and can significantly + * improve performance for repeated requests to the same endpoints. + *

+ *

+ * A default in-memory cache implementation is available via {@link #builder()}. + * Custom implementations can integrate with external caching systems such as + * Redis, Memcached, or other distributed caches. + *

+ * + * @see Config#cacheResponses(Cache.Builder) + * @see Builder */ public interface Cache { /** - * Returns the cached HttpResponse for a key or uses the Supplier to fetch the response - * @param key the cache key - * @param fetcher a function to execute the request and return the response. This response should be - * cached by the implementation - * @param the type of response - * @return the Http Response + * Returns the cached HTTP response for a key, or uses the supplier to fetch and cache the response. + *

+ * If a cached response exists for the given key, it is returned immediately. + * Otherwise, the fetcher is invoked to execute the request, and the resulting + * response should be stored in the cache before being returned. + * + * @param the type of the response body + * @param key the cache key identifying the request + * @param fetcher a supplier that executes the request and returns the response; + * the implementation should cache this response + * @return the cached or newly fetched {@link HttpResponse} */ HttpResponse get(Key key, Supplier> fetcher); /** - * Returns the cached HttpResponse for a key or uses the Supplier to fetch the response - * @param key the cache key - * @param fetcher a function to execute the request and return the response. This response should be - * cached by the implementation - * @param the type of response - * @return the CompletableFuture for the response + * Returns the cached HTTP response for a key asynchronously, or uses the supplier to fetch and cache the response. + *

+ * If a cached response exists for the given key, it is returned immediately via the future. + * Otherwise, the fetcher is invoked to execute the request asynchronously, and the resulting + * response should be stored in the cache before completing the future. + * + * @param the type of the response body + * @param key the cache key identifying the request + * @param fetcher a supplier that executes the request asynchronously and returns a future + * containing the response; the implementation should cache this response + * @return a {@link CompletableFuture} that will be completed with the cached or newly fetched response */ CompletableFuture getAsync(Key key, Supplier>> fetcher); /** - * a builder for cache options - * @return a new Builder. + * Creates a new builder for configuring cache options. + * + * @return a new {@link Builder} instance */ static Builder builder(){ return new Builder(); } + /** + * A builder for configuring and creating cache instances. + *

+ * Use this builder to configure cache properties such as maximum depth, + * time-to-live, custom backing caches, and key generators. + */ class Builder { private int depth = 100; private long ttl = 0; private Cache backing; private KeyGenerator keyGen; + /** + * Builds and returns a configured {@link CacheManager} instance. + * + * @return a new CacheManager configured with this builder's settings + */ CacheManager build() { if(backing != null){ return new CacheManager(backing, keyGen); @@ -77,12 +112,15 @@ CacheManager build() { } /** - * defines the max depth of the cache in number of values. - * defaults to 100. - * Elements exceeding the depth are purged on read. - * Custom Cache implementations may not honor this setting - * @param value the max depth - * @return the current builder. + * Defines the maximum depth of the cache in number of entries. + *

+ * When the cache exceeds this depth, the oldest elements are purged on read. + * Defaults to 100 entries. + *

+ * Note: Custom {@link Cache} implementations may not honor this setting. + * + * @param value the maximum number of entries to store in the cache + * @return this builder for method chaining */ public Builder depth(int value) { this.depth = value; @@ -90,13 +128,17 @@ public Builder depth(int value) { } /** - * Sets a Time-To-Live for response objects. - * There is no TTL by default and objects will be kept indefinitely - * Elements exceeding the TTL are purged on read. - * Custom Cache implementations may not honor this setting - * @param number a number - * @param units the TimeUnits of the number - * @return this builder. + * Sets the time-to-live (TTL) for cached response entries. + *

+ * Entries older than the TTL are considered stale and purged on read. + * By default, there is no TTL and entries are kept indefinitely + * (subject to depth limits). + *

+ * Note: Custom {@link Cache} implementations may not honor this setting. + * + * @param number the duration value + * @param units the time unit for the duration + * @return this builder for method chaining */ public Builder maxAge(long number, TimeUnit units) { this.ttl = units.toMillis(number); @@ -104,10 +146,14 @@ public Builder maxAge(long number, TimeUnit units) { } /** - * Sets a custom backing cache. This cache must implement it's own purging rules - * There is no TTL by default and objects will be kept indefinitely - * @param cache the backing cache implementation - * @return this builder. + * Sets a custom backing cache implementation. + *

+ * Use this to integrate with external caching systems. When a custom + * backing cache is provided, the {@link #depth(int)} and {@link #maxAge(long, TimeUnit)} + * settings are ignored; the custom cache must implement its own purging rules. + * + * @param cache the custom cache implementation + * @return this builder for method chaining */ public Builder backingCache(Cache cache) { this.backing = cache; @@ -115,10 +161,13 @@ public Builder backingCache(Cache cache) { } /** - * Provide a custom key generator. - * The default key is a hash of the request, the request execution type and the response type. + * Provides a custom key generator for cache lookups. + *

+ * The default key is generated from a hash of the request details, + * the execution type (sync/async), and the response type. + * * @param keyGenerator a custom cache key generator - * @return this builder + * @return this builder for method chaining */ public Builder withKeyGen(KeyGenerator keyGenerator) { this.keyGen = keyGenerator; @@ -127,53 +176,69 @@ public Builder withKeyGen(KeyGenerator keyGenerator) { } /** - * A functional interface to generate a cache key + * A functional interface for generating cache keys from HTTP requests. + *

+ * Implement this interface to customize how cache keys are generated. + * The generated key determines cache hit/miss behavior. + *

*/ @FunctionalInterface interface KeyGenerator { /** - * A function to generate a cache key - * @param request the current http request - * @param isAsync indicates if this request is being executed async - * @param responseType the response type (String, JsonNode, etc) - * @return a key which can be used as a hash for the cache + * Generates a cache key for the given request. + * + * @param request the HTTP request to generate a key for + * @param isAsync {@code true} if the request is being executed asynchronously, + * {@code false} for synchronous execution + * @param responseType the expected response body type (e.g., String.class, JsonNode.class) + * @return a {@link Key} that uniquely identifies this request for caching purposes */ Key apply(HttpRequest request, Boolean isAsync, Class responseType); } /** - * Interface for the cache key which can be implemented by consumers - * The key should implement equals and hashCode - * It must must return the time the key was created. + * Interface for cache keys used to identify cached responses. + *

+ * Implementations must properly implement {@link #equals(Object)} and {@link #hashCode()} + * to ensure correct cache lookup behavior. The key must also track its creation time + * to support time-based expiration. + *

*/ interface Key { /** - * @param obj the reference object with which to compare. - * @return {@code true} if this object is the same as the obj - * argument; {@code false} otherwise. - * @see #hashCode() - * @see java.util.HashMap + * Compares this key with another object for equality. + *

+ * Two keys are equal if they represent the same cached request. Implementations + * should compare all relevant request characteristics used to generate the key. + *

+ * + * @param obj the object to compare with + * @return {@code true} if this key equals the specified object, {@code false} otherwise */ @Override boolean equals(Object obj); /** - * As much as is reasonably practical, the hashCode method defined - * by class {@code Object} does return distinct integers for - * distinct objects. (The hashCode may or may not be implemented - * as some function of an object's memory address at some point - * in time.) + * Returns a hash code for this key. + *

+ * The hash code must be consistent with {@link #equals(Object)} such that + * equal keys produce the same hash code. This is essential for proper + * cache lookup using hash-based data structures. + *

* - * @return a hash code value for this object. - * @see java.lang.Object#equals(java.lang.Object) - * @see java.lang.System#identityHashCode + * @return a hash code value for this key */ @Override int hashCode(); /** - * The time the key was created to be used by purging functions - * @return the time as an instant + * Returns the time when this key was created. + *

+ * This timestamp is used by cache purging functions to determine + * if a cached entry has exceeded its time-to-live. + *

+ * + * @return the creation time as an {@link Instant} */ Instant getTime(); } diff --git a/unirest/src/main/java/kong/unirest/core/Callback.java b/unirest/src/main/java/kong/unirest/core/Callback.java index 5c99a85c0..ff55d8c55 100644 --- a/unirest/src/main/java/kong/unirest/core/Callback.java +++ b/unirest/src/main/java/kong/unirest/core/Callback.java @@ -25,10 +25,78 @@ package kong.unirest.core; +/** + * A callback interface for handling asynchronous HTTP responses. + *

+ * Implement this interface to receive notifications when an asynchronous + * HTTP request completes, fails, or is cancelled. This is used with + * asynchronous request methods to process responses without blocking + * the calling thread. + *

+ * + *

Example usage:

+ *
{@code
+ * Unirest.get("http://example.com/api")
+ *     .asStringAsync(new Callback() {
+ *         @Override
+ *         public void completed(HttpResponse response) {
+ *             System.out.println("Response: " + response.getBody());
+ *         }
+ *
+ *         @Override
+ *         public void failed(UnirestException e) {
+ *             System.err.println("Request failed: " + e.getMessage());
+ *         }
+ *
+ *         @Override
+ *         public void cancelled() {
+ *             System.out.println("Request was cancelled");
+ *         }
+ *     });
+ * }
+ * + * @param the type of the response body + */ public interface Callback { + /** + * Called when the asynchronous HTTP request completes successfully. + *

+ * This method is invoked when the HTTP request finishes and a response + * is received, regardless of the HTTP status code. Check + * {@link HttpResponse#getStatus()} to determine if the response + * indicates success or an error. + *

+ * + * @param response the HTTP response containing the status, headers, and body + */ void completed(HttpResponse response); - default void failed(UnirestException e){} + /** + * Called when the asynchronous HTTP request fails due to an exception. + *

+ * This method is invoked when the request cannot be completed due to + * network errors, connection timeouts, or other exceptional conditions + * that prevent receiving a response. + *

+ *

+ * The default implementation does nothing. Override this method to + * handle failure scenarios. + *

+ * + * @param e the exception that caused the request to fail + */ + default void failed(UnirestException e) {} - default void cancelled(){} + /** + * Called when the asynchronous HTTP request is cancelled. + *

+ * This method is invoked when the request is cancelled before completion, + * typically by calling {@code cancel()} on the returned {@link java.util.concurrent.Future}. + *

+ *

+ * The default implementation does nothing. Override this method to + * handle cancellation scenarios. + *

+ */ + default void cancelled() {} } diff --git a/unirest/src/main/java/kong/unirest/core/Client.java b/unirest/src/main/java/kong/unirest/core/Client.java index b1cede72a..b9f107a59 100644 --- a/unirest/src/main/java/kong/unirest/core/Client.java +++ b/unirest/src/main/java/kong/unirest/core/Client.java @@ -33,33 +33,56 @@ import java.util.stream.Stream; /** - * The client that does the work. + * The HTTP client abstraction responsible for executing HTTP requests. + *

+ * This interface defines the contract for HTTP client implementations that + * handle synchronous and asynchronous requests, WebSocket connections, and + * Server-Sent Events (SSE). The default implementation uses Java's built-in + * {@link java.net.http.HttpClient}. + *

+ *

+ * Custom implementations can be provided to wrap other HTTP libraries or + * to add custom behavior such as logging, metrics, or request manipulation. + *

+ * + * @see Config#httpClient(Client) */ public interface Client { /** - * @param the underlying client - * @return the underlying client if this instance is wrapping another library. + * Returns the underlying HTTP client implementation. + *

+ * This method provides access to the wrapped HTTP client library, + * allowing direct access to implementation-specific features when needed. + *

+ * + * @param the type of the underlying client + * @return the underlying client instance */ T getClient(); /** - * Make a request - * @param The type of the body - * @param request the prepared request object - * @param transformer the function to transform the response - * @param resultType the final body result type. This is a hint to downstream systems to make up for type erasure. - * @return a HttpResponse with a transformed body + * Executes a synchronous HTTP request. + * + * @param the type of the response body + * @param request the prepared request object containing the HTTP method, URL, headers, and body + * @param transformer the function to transform the raw response into the desired response type + * @param resultType the expected body result type; used as a hint to downstream systems + * to work around type erasure + * @return an {@link HttpResponse} containing the status, headers, and transformed body */ HttpResponse request(HttpRequest request, Function> transformer, Class resultType); /** - * Make a Async request - * @param The type of the body - * @param request the prepared request object - * @param transformer the function to transform the response - * @param callback the CompletableFuture that will handle the eventual response - * @param resultType the final body result type. This is a hint to downstream systems to make up for type erasure. - * @return a CompletableFuture of a response + * Executes an asynchronous HTTP request. + * + * @param the type of the response body + * @param request the prepared request object containing the HTTP method, URL, headers, and body + * @param transformer the function to transform the raw response into the desired response type + * @param callback the {@link CompletableFuture} that will be completed with the response + * when the request finishes + * @param resultType the expected body result type; used as a hint to downstream systems + * to work around type erasure + * @return a {@link CompletableFuture} that will be completed with the HTTP response */ CompletableFuture> request(HttpRequest request, Function> transformer, @@ -67,22 +90,38 @@ CompletableFuture> request(HttpRequest request, Class resultType); /** - * Create a websocket connection - * @param request the connection - * @param listener (in the voice of Cicero) the listener - * @return a WebSocketResponse + * Creates a WebSocket connection. + * + * @param request the WebSocket connection request containing the URL and headers + * @param listener the WebSocket listener to handle incoming messages and connection events + * @return a {@link WebSocketResponse} representing the established WebSocket connection */ WebSocketResponse websocket(WebSocketRequest request, WebSocket.Listener listener); /** - * execute a SSE Event connection. - * Because these events are a stream they are processed async and take a handler you can use to consume the events - * @param request the request details - * @param handler the SseHandler - * @return a CompletableFuture + * Executes a Server-Sent Events (SSE) connection with a handler. + *

+ * Because SSE events are streamed, they are processed asynchronously. + * The provided handler is invoked for each event received from the server. + *

+ * + * @param request the SSE request containing the URL and headers + * @param handler the {@link SseHandler} to process incoming events + * @return a {@link CompletableFuture} that completes when the SSE connection closes */ CompletableFuture sse(SseRequest request, SseHandler handler); + /** + * Executes a Server-Sent Events (SSE) connection and returns a stream of events. + *

+ * This method provides a reactive-style API for consuming SSE events as a + * {@link Stream}. The stream will continue to provide events until the + * connection is closed or an error occurs. + *

+ * + * @param request the SSE request containing the URL and headers + * @return a {@link Stream} of {@link Event} objects representing the SSE events + */ Stream sse(SseRequest request); } diff --git a/unirest/src/main/java/kong/unirest/core/Config.java b/unirest/src/main/java/kong/unirest/core/Config.java index 60bbf0e8b..4313a1b4c 100644 --- a/unirest/src/main/java/kong/unirest/core/Config.java +++ b/unirest/src/main/java/kong/unirest/core/Config.java @@ -44,9 +44,50 @@ import java.util.function.Function; import java.util.function.Supplier; +/** + * Configuration class for Unirest HTTP client settings. + *

+ * This class provides a fluent API for configuring all aspects of HTTP client behavior, + * including timeouts, proxies, SSL/TLS settings, headers, cookies, authentication, and more. + *

+ * Configuration can be accessed and modified through {@link Unirest#config()} or by creating + * a custom {@link UnirestInstance} with its own configuration. + *

+ * Example usage: + *

{@code
+ * Unirest.config()
+ *     .connectTimeout(5000)
+ *     .defaultBaseUrl("https://api.example.com")
+ *     .setDefaultHeader("Accept", "application/json")
+ *     .proxy("proxy.example.com", 8080);
+ * }
+ *

+ * Most configuration changes require the client to not be running. If the client has already + * been built, call {@link #reset()} before making changes that require a client rebuild. + * + * @see Unirest + * @see UnirestInstance + */ public class Config { + /** + * Default connection timeout in milliseconds (10 seconds). + */ public static final int DEFAULT_CONNECT_TIMEOUT = 10000; + + /** + * System property name for configuring the HTTP keep-alive timeout in seconds. + * This applies to both HTTP/1.1 and HTTP/2 connections. + * + * @see Java HTTP Client Module + */ public static final String JDK_HTTPCLIENT_KEEPALIVE_TIMEOUT = "jdk.httpclient.keepalive.timeout"; + + /** + * System property name for disabling hostname verification in SSL/TLS connections. + *

+ * Warning: Disabling hostname verification affects the entire JVM + * and should only be used in development/testing environments. + */ public static final String JDK_HTTPCLIENT_DISABLE_HOST_NAME_VERIFICATION = "jdk.internal.httpclient.disableHostnameVerification"; private Optional client = Optional.empty(); @@ -79,10 +120,28 @@ public class Config { private Authenticator authenticator; private ProxySelector proxySelector; + /** + * Creates a new Config instance with default settings. + *

+ * Default values: + *

    + *
  • Connection timeout: 10 seconds
  • + *
  • Request timeout: none (infinite)
  • + *
  • Follow redirects: true
  • + *
  • Cookie management: true
  • + *
  • Request compression: true
  • + *
  • SSL verification: true
  • + *
  • HTTP version: HTTP/2
  • + *
  • Default encoding: UTF-8
  • + *
+ */ public Config() { setDefaults(); } + /** + * Resets all configuration options to their default values. + */ private void setDefaults() { proxy = null; cache = null; @@ -730,54 +789,66 @@ private synchronized void buildClient() { } /** - * @return if cookie management should be enabled. - * default: true + * Returns whether cookie management is enabled. + * + * @return {@code true} if cookie management is enabled, {@code false} otherwise. + * Default: {@code true} */ public boolean getEnabledCookieManagement() { return cookieManagement; } /** - * @return if the clients should follow redirects - * default: true + * Returns whether the client follows redirects. + * + * @return {@code true} if the client follows redirects, {@code false} otherwise. + * Default: {@code true} */ public boolean getFollowRedirects() { return followRedirects; } /** - * @return the connection timeout in milliseconds - * default: 10000 + * Returns the connection timeout in milliseconds. + * + * @return the connection timeout in milliseconds. Default: 10000 */ public int getConnectionTimeout() { return connectionTimeout; } /** - * @return the connection timeout in milliseconds - * default: null (infinite) + * Returns the request timeout in milliseconds. + * + * @return the request timeout in milliseconds, or {@code null} for infinite. Default: {@code null} */ public Integer getRequestTimeout() { return requestTimeout; } /** - * @return a security keystore if one has been provided + * Returns the configured keystore for SSL client certificates. + * + * @return the keystore, or {@code null} if none has been configured */ public KeyStore getKeystore() { return this.keystore; } /** - * @return The password for the keystore if provided + * Returns the password for the configured keystore. + * + * @return the keystore password, or {@code null} if none has been configured */ public String getKeyStorePassword() { return this.keystorePassword.get(); } /** - * @return a configured object mapper - * @throws UnirestException if none has been configured. + * Returns the configured ObjectMapper for JSON serialization. + * + * @return the configured ObjectMapper + * @throws UnirestConfigException if no ObjectMapper has been configured */ public ObjectMapper getObjectMapper() { ObjectMapper om = this.objectMapper.get(); @@ -797,121 +868,154 @@ private void validateClientsNotRunning() { } /** - * @return the configured proxy configuration + * Returns the configured proxy settings. + * + * @return the proxy configuration, or {@code null} if none is configured */ public Proxy getProxy() { return proxy; } /** - * @return if the system will pick up system properties (default is false) + * Returns whether the client uses system properties for configuration. + * + * @return {@code true} if system properties are used, {@code false} otherwise. Default: {@code false} */ public boolean useSystemProperties() { return this.useSystemProperties; } /** - * @return the default encoding (UTF-8 is the default default) + * Returns the default response encoding. + * + * @return the default encoding. Default: UTF-8 */ public String getDefaultResponseEncoding() { return defaultResponseEncoding; } /** - * @return if request compression is on (default is true) + * Returns whether request compression is enabled. + * + * @return {@code true} if request compression is enabled, {@code false} otherwise. Default: {@code true} */ public boolean isRequestCompressionOn() { return requestCompressionOn; } /** - * Will unirest verify the SSL? - * You should only do this in non-prod environments. - * Default is true - * @return if unirest will verify the SSL + * Returns whether SSL verification is enabled. + *

+ * You should only disable this in non-production environments. + * + * @return {@code true} if SSL is verified, {@code false} otherwise. Default: {@code true} */ public boolean isVerifySsl() { return verifySsl; } /** - * @return the configured Cookie Spec + * Returns the configured cookie specification policy. + * + * @return the cookie spec policy, or {@code null} if using default */ public String getCookieSpec() { return cookieSpec; } /** - * @return the currently configured UniMetric object + * Returns the configured metrics instrumentation object. + * + * @return the UniMetric object */ public UniMetric getMetric() { return metrics; } /** - * @return the currently configured Interceptor + * Returns the configured request interceptor. + * + * @return the Interceptor */ public Interceptor getUniInterceptor() { return interceptor; } /** - * @return the SSL connection configuration + * Returns the configured SSL context. + * + * @return the SSLContext, or {@code null} if none is configured */ public SSLContext getSslContext() { return sslContext; } /** - * @return the ciphers for the SSL connection configuration + * Returns the configured SSL ciphers. + * + * @return the array of cipher names, or {@code null} if using defaults */ public String[] getCiphers() { return ciphers; } /** - * @return the protocols for the SSL connection configuration + * Returns the configured SSL protocols. + * + * @return the array of protocol names, or {@code null} if using defaults */ public String[] getProtocols() { return protocols; } /** - * @return the default base URL + * Returns the default base URL for all requests. + * + * @return the default base URL, or {@code null} if none is configured */ public String getDefaultBaseUrl() { return this.defaultBaseUrl; } /** - * @return the custom executor + * Returns the custom executor for async requests. + * + * @return the custom Executor, or {@code null} if using the default */ public Executor getCustomExecutor(){ return customExecutor; } /** - * @return the preferred http version + * Returns the preferred HTTP protocol version. + * + * @return the HTTP version. Default: HTTP/2 */ public HttpClient.Version getVersion() { return version; } /** - * @return if unirest will retry requests on 429/529 + * Returns whether automatic retry on 429/529 responses is enabled. + * + * @return {@code true} if automatic retry is enabled, {@code false} otherwise */ public boolean isAutomaticRetryAfter(){ return retry != null; } /** - * @return the max number of times to attempt to do a 429/529 retry-after + * Returns the maximum number of retry attempts for 429/529 responses. + * + * @return the maximum number of retry attempts */ public int maxRetries() { return retry.getMaxAttempts(); } /** - * @return the maximum life span of persistent connections regardless of their expiration setting. + * Returns the time-to-live for persistent connections. + * + * @return the TTL in seconds, or -1 if not configured */ public long getTTL() { try { @@ -922,7 +1026,9 @@ public long getTTL() { } /** - * @return the RetryStrategy configured + * Returns the configured retry strategy. + * + * @return the RetryStrategy, or {@code null} if none is configured */ public RetryStrategy getRetryStrategy() { return retry; @@ -943,9 +1049,10 @@ public Config disableHostNameVerification(boolean enabled) { } /** - * Sets a authenticator object for the client - * @param auth - * @return this config + * Sets an authenticator for the HTTP client. + * + * @param auth the Authenticator to use for authentication challenges + * @return this config object */ public Config authenticator(Authenticator auth) { this.authenticator = auth; @@ -953,14 +1060,18 @@ public Config authenticator(Authenticator auth) { } /** - * @return the authenticator registered with the config + * Returns the configured authenticator. + * + * @return the Authenticator, or {@code null} if none is configured */ public Authenticator getAuthenticator(){ return authenticator; } /** - * @return the ProxySelector + * Returns the configured proxy selector. + * + * @return the ProxySelector, or {@code null} if none is configured */ public ProxySelector getProxySelector(){ return proxySelector; diff --git a/unirest/src/main/java/kong/unirest/core/ContentType.java b/unirest/src/main/java/kong/unirest/core/ContentType.java index 7959c9d5a..92b17d666 100644 --- a/unirest/src/main/java/kong/unirest/core/ContentType.java +++ b/unirest/src/main/java/kong/unirest/core/ContentType.java @@ -32,87 +32,220 @@ import static java.nio.charset.StandardCharsets.ISO_8859_1; +/** + * Represents an HTTP Content-Type header value, consisting of a MIME type, + * an optional character encoding, and a binary indicator. + *

+ * This class provides predefined constants for common content types and factory + * methods for creating custom content types. It is used throughout Unirest for + * specifying the content type of request bodies and multipart form fields. + *

+ * + *

Example usage:

+ *
{@code
+ * // Using a predefined constant
+ * Unirest.post("/api/data")
+ *     .contentType(ContentType.APPLICATION_JSON)
+ *     .body("{\"key\": \"value\"}")
+ *     .asString();
+ *
+ * // Creating a custom content type
+ * ContentType custom = ContentType.create("application/vnd.api+json", StandardCharsets.UTF_8);
+ * }
+ */ public class ContentType { private static final Set BINARY_TYPES = new HashSet<>(); + /** Content type for Atom XML feeds: {@code application/atom+xml} with ISO-8859-1 charset. */ public static final ContentType APPLICATION_ATOM_XML = create("application/atom+xml", ISO_8859_1); + + /** Content type for URL-encoded form data: {@code application/x-www-form-urlencoded} with ISO-8859-1 charset. */ public static final ContentType APPLICATION_FORM_URLENCODED = create("application/x-www-form-urlencoded", ISO_8859_1); + + /** Content type for JSON data: {@code application/json} with UTF-8 charset. */ public static final ContentType APPLICATION_JSON = create("application/json", StandardCharsets.UTF_8); + + /** Content type for JSON Patch documents: {@code application/json-patch+json}. */ public static final ContentType APPLICATION_JSON_PATCH = create("application/json-patch+json"); + + /** Content type for arbitrary binary data: {@code application/octet-stream}. Marked as binary. */ public static final ContentType APPLICATION_OCTET_STREAM = create("application/octet-stream", true); + + /** Content type for binary octet stream: {@code binary/octet-stream}. Marked as binary. */ public static final ContentType BINARY_OCTET_STREAM = create("binary/octet-stream", true); + + /** Content type for SVG XML images: {@code application/svg+xml} with ISO-8859-1 charset. */ public static final ContentType APPLICATION_SVG_XML = create("application/svg+xml", ISO_8859_1); + + /** Content type for XHTML documents: {@code application/xhtml+xml} with ISO-8859-1 charset. */ public static final ContentType APPLICATION_XHTML_XML = create("application/xhtml+xml", ISO_8859_1); + + /** Content type for XML data: {@code application/xml} with ISO-8859-1 charset. */ public static final ContentType APPLICATION_XML = create("application/xml", ISO_8859_1); + + /** Content type for PDF documents: {@code application/pdf}. Marked as binary. */ public static final ContentType APPLICATION_PDF = create("application/pdf", true); + + /** Content type for BMP images: {@code image/bmp}. Marked as binary. */ public static final ContentType IMAGE_BMP = create("image/bmp", true); + + /** Content type for GIF images: {@code image/gif}. Marked as binary. */ public static final ContentType IMAGE_GIF = create("image/gif", true); + + /** Content type for JPEG images: {@code image/jpeg}. Marked as binary. */ public static final ContentType IMAGE_JPEG = create("image/jpeg", true); + + /** Content type for PNG images: {@code image/png}. Marked as binary. */ public static final ContentType IMAGE_PNG = create("image/png", true); + + /** Content type for SVG images: {@code image/svg+xml}. */ public static final ContentType IMAGE_SVG = create("image/svg+xml"); + + /** Content type for TIFF images: {@code image/tiff}. Marked as binary. */ public static final ContentType IMAGE_TIFF = create("image/tiff", true); + + /** Content type for WebP images: {@code image/webp}. Marked as binary. */ public static final ContentType IMAGE_WEBP = create("image/webp", true); + + /** Content type for multipart form data: {@code multipart/form-data} with ISO-8859-1 charset. */ public static final ContentType MULTIPART_FORM_DATA = create("multipart/form-data", ISO_8859_1); + + /** Content type for HTML documents: {@code text/html} with ISO-8859-1 charset. */ public static final ContentType TEXT_HTML = create("text/html", ISO_8859_1); + + /** Content type for plain text: {@code text/plain} with ISO-8859-1 charset. */ public static final ContentType TEXT_PLAIN = create("text/plain", ISO_8859_1); + + /** Content type for XML text: {@code text/xml} with ISO-8859-1 charset. */ public static final ContentType TEXT_XML = create("text/xml", ISO_8859_1); + + /** Content type for Server-Sent Events: {@code text/event-stream} with UTF-8 charset. */ public static final ContentType EVENT_STREAMS = create("text/event-stream", StandardCharsets.UTF_8); + + /** Wildcard content type that matches any MIME type: {@code *}{@code /*}. */ public static final ContentType WILDCARD = create("*/*"); private final String mimeType; private final Charset encoding; private final boolean isBinary; + /** + * Creates a new ContentType with the specified MIME type and no charset. + * + * @param mimeType the MIME type string (e.g., "application/json") + * @return a new ContentType instance + */ public static ContentType create(String mimeType) { return new ContentType(mimeType, null, false); } + /** + * Creates a new ContentType with the specified MIME type and charset. + * + * @param mimeType the MIME type string (e.g., "application/json") + * @param charset the character encoding for this content type + * @return a new ContentType instance + */ public static ContentType create(String mimeType, Charset charset) { return new ContentType(mimeType, charset, false); } + /** + * Creates a new ContentType with the specified MIME type and binary flag. + * + * @param mimeType the MIME type string (e.g., "image/png") + * @param isBinary true if this content type represents binary data + * @return a new ContentType instance + */ public static ContentType create(String mimeType, boolean isBinary) { return new ContentType(mimeType, null, isBinary); } + /** + * Constructs a new ContentType with all parameters. + * + * @param mimeType the MIME type string + * @param encoding the character encoding, or null if not specified + * @param isBinary true if this content type represents binary data + */ ContentType(String mimeType, Charset encoding, boolean isBinary) { this.mimeType = mimeType; this.encoding = encoding; this.isBinary = isBinary; - if(isBinary && !BINARY_TYPES.contains(mimeType)){ + if (isBinary && !BINARY_TYPES.contains(mimeType)) { BINARY_TYPES.add(mimeType); } } + /** + * Determines if the given MIME type represents binary content. + *

+ * A MIME type is considered binary if it contains "binary" in the string + * or if it has been registered as a binary type (e.g., image types, PDF). + *

+ * + * @param mimeType the MIME type string to check + * @return true if the MIME type represents binary content, false otherwise + */ public static boolean isBinary(String mimeType) { - if(mimeType == null){ + if (mimeType == null) { return false; } String lc = mimeType.toLowerCase(); return lc.contains("binary") || BINARY_TYPES.contains(lc); } + /** + * Returns the string representation of this content type. + *

+ * If a charset is specified, it will be included in the format: + * {@code mimeType; charset=encoding} + *

+ * + * @return the content type string suitable for use in HTTP headers + */ @Override public String toString() { StringBuilder sb = new StringBuilder(mimeType); - if(encoding != null){ + if (encoding != null) { sb.append("; charset=").append(encoding); } return sb.toString(); } + /** + * Returns the MIME type portion of this content type. + * + * @return the MIME type string (e.g., "application/json") + */ public String getMimeType() { return mimeType; } + /** + * Creates a new ContentType with the same MIME type but a different charset. + * + * @param charset the new character encoding + * @return a new ContentType instance with the specified charset + */ public ContentType withCharset(Charset charset) { return new ContentType(mimeType, charset, isBinary); } + /** + * Returns whether this content type represents binary data. + * + * @return true if this is a binary content type, false otherwise + */ public boolean isBinary() { return isBinary; } + /** + * Returns the character encoding for this content type. + * + * @return the charset, or null if not specified + */ public Charset getCharset() { return encoding; } diff --git a/unirest/src/main/java/kong/unirest/core/Cookie.java b/unirest/src/main/java/kong/unirest/core/Cookie.java index 799a54a27..fd64b5149 100644 --- a/unirest/src/main/java/kong/unirest/core/Cookie.java +++ b/unirest/src/main/java/kong/unirest/core/Cookie.java @@ -32,11 +32,12 @@ import java.util.stream.Collectors; /** - * Represents a cookie parsed from the set-cookie header - * per https://tools.ietf.org/html/rfc6265 + * Represents an HTTP cookie parsed from the Set-Cookie header. + *

+ * This class implements cookie parsing and formatting according to + * RFC 6265. * - * note that the RFC is awful. - * The wikipedia article is far easier to understand https://en.wikipedia.org/wiki/HTTP_cookie + * @see HTTP cookie (Wikipedia) */ public class Cookie { private String name; @@ -50,14 +51,21 @@ public class Cookie { private SameSite sameSite; private boolean partitioned; + /** + * Creates a new cookie with the specified name and value. + * + * @param name the cookie name + * @param value the cookie value + */ public Cookie(String name, String value){ this.name = name; this.value = value; } /** - * Construct a cookie from a set-cookie value - * @param v cookie string value + * Constructs a cookie by parsing a Set-Cookie header value. + * + * @param v the Set-Cookie header value to parse */ public Cookie(String v) { this(v.split(";")); @@ -164,26 +172,56 @@ public String toString() { return pairs.stream().map(Pair::toString).collect(Collectors.joining(";")); } + /** + * Sets the domain attribute of the cookie. + * + * @param domain the domain value + */ public void setDomain(String domain) { this.domain = domain; } + /** + * Sets the path attribute of the cookie. + * + * @param path the path value + */ public void setPath(String path) { this.path = path; } + /** + * Sets the HttpOnly attribute of the cookie. + * + * @param httpOnly {@code true} to mark the cookie as HttpOnly + */ public void setHttpOnly(boolean httpOnly) { this.httpOnly = httpOnly; } + /** + * Returns whether the cookie has the Partitioned attribute set. + * + * @return {@code true} if the cookie is partitioned, {@code false} otherwise + */ public boolean isPartitioned() { return this.partitioned; } + /** + * Sets the Partitioned attribute of the cookie. + * + * @param partitionedFlag {@code true} to mark the cookie as partitioned + */ public void setPartitioned(boolean partitionedFlag) { this.partitioned = partitionedFlag; } + /** + * Sets the Secure attribute of the cookie. + * + * @param secureFlag {@code true} to mark the cookie as secure + */ public void setSecured(boolean secureFlag) { this.secure = secureFlag; } @@ -207,93 +245,132 @@ public String toString() { } /** - * @return the cookie-name + * Returns the cookie name. + * + * @return the cookie name */ public String getName() { return name; } /** - * @return the cookie-value + * Returns the cookie value. + * + * @return the cookie value */ public String getValue() { return value; } /** - * @return the cookie-value, url-decoded + * Returns the cookie value, URL-decoded. + * + * @return the URL-decoded cookie value */ public String getUrlDecodedValue() { return getDecode(value); } /** - * @return the domain value of the cookie + * Returns the domain attribute of the cookie. + * + * @return the domain value, or {@code null} if not set */ public String getDomain() { return domain; } /** - * @return the path value of the cookie + * Returns the path attribute of the cookie. + * + * @return the path value, or {@code null} if not set */ public String getPath() { return path; } /** - * Per Wikipedia: - * The HttpOnly attribute directs browsers not to expose cookies through channels other than HTTP (and HTTPS) requests. - * This means that the cookie cannot be accessed via client-side scripting languages (notably JavaScript), - * and therefore cannot be stolen easily via cross-site scripting (a pervasive attack technique) - * @return a boolean if the cookie is httpOnly + * Returns whether the cookie has the HttpOnly attribute set. + *

+ * The HttpOnly attribute directs browsers not to expose cookies through + * channels other than HTTP (and HTTPS) requests. This means that the cookie + * cannot be accessed via client-side scripting languages (notably JavaScript), + * and therefore cannot be stolen easily via cross-site scripting. + * + * @return {@code true} if the cookie is HttpOnly, {@code false} otherwise */ public boolean isHttpOnly() { return httpOnly; } /** - * Per Wikipedia: - * The Secure attribute is meant to keep cookie communication limited to encrypted transmission, - * directing browsers to use cookies only via secure/encrypted connections. - * @return a boolean of if the cookie is secure + * Returns whether the cookie has the Secure attribute set. + *

+ * The Secure attribute directs browsers to use cookies only via + * secure/encrypted connections (HTTPS). + * + * @return {@code true} if the cookie is secure, {@code false} otherwise */ public boolean isSecure() { return secure; } /** - * Per Wikipedia: - * the Max-Age attribute can be used to set the cookie's expiration as an interval of seconds in the future, - * relative to the time the browser received the cookie. - * @return Max-Age attribute + * Returns the Max-Age attribute of the cookie. + *

+ * The Max-Age attribute specifies the cookie's expiration as an interval + * of seconds in the future, relative to the time the browser received the cookie. + * + * @return the Max-Age value in seconds, or {@code null} if not set */ public int getMaxAge() { return maxAge; } /** - * Per Wikipedia: - * The Expires attribute defines a specific date and time for when the browser should delete the cookie. - * @return a ZonedDateTime of the expiration + * Returns the Expires attribute of the cookie. + *

+ * The Expires attribute defines a specific date and time for when + * the browser should delete the cookie. + * + * @return the expiration date and time, or {@code null} if not set */ public ZonedDateTime getExpiration() { return expires; } /** - * returns the SameSite attribute - * @return the SameSite attribute if set. or null + * Returns the SameSite attribute of the cookie. + * + * @return the SameSite attribute, or {@code null} if not set */ public SameSite getSameSite() { return sameSite; } + /** + * Represents the SameSite cookie attribute values. + *

+ * The SameSite attribute controls whether the cookie is sent with + * cross-site requests, providing protection against cross-site + * request forgery attacks. + */ public enum SameSite { - None, Strict, Lax; + /** Cookie is sent with all requests, including cross-site. */ + None, + /** Cookie is only sent with same-site requests or top-level navigations. */ + Strict, + /** Cookie is sent with same-site requests and cross-site top-level navigations. */ + Lax; private static EnumSet all = EnumSet.allOf(SameSite.class); + /** + * Parses a string value into a SameSite enum constant. + * + * @param value the string value to parse (case-insensitive) + * @return the matching SameSite value, or {@code null} if no match + */ public static SameSite parse(String value) { return all.stream() .filter(e -> e.name().equalsIgnoreCase(value)) diff --git a/unirest/src/main/java/kong/unirest/core/FailedResponse.java b/unirest/src/main/java/kong/unirest/core/FailedResponse.java index c0c30af91..972a52b22 100644 --- a/unirest/src/main/java/kong/unirest/core/FailedResponse.java +++ b/unirest/src/main/java/kong/unirest/core/FailedResponse.java @@ -30,28 +30,37 @@ import java.util.function.Function; /** - * A failed response you COULD return if you want to live in a house of lies. - * This can be returned by a interceptor rather than throwing an exception. - * It's possible if not handled correctly this could be more confusing than the exception + * Represents a failed HTTP response when an exception occurs before receiving a server response. + *

+ * This class can be returned by an interceptor instead of throwing an exception, + * allowing failed requests to be handled through the normal response processing flow. + * Since no actual HTTP response was received, this class provides synthetic values + * for status code, headers, and body. + *

+ * + * @param the type of the response body (always {@code null} for failed responses) + * @see HttpResponse */ public class FailedResponse implements HttpResponse { private final Exception failureReason; /** - * Build a elaborate lie from a failure. - * Just like what you're going to do at thanksgiving dinner. - * @param e where it all went wrong. + * Creates a new FailedResponse wrapping the given exception. + * + * @param e the exception that caused the request to fail */ public FailedResponse(Exception e) { this.failureReason = e; } /** - * Returns a 542, which is nothing and a lie. - * The remove server in this case returned nothing all all. - * As far as we know you aren't even on the internet. - * So we made up this code, because a 500+ status is better than 0 - * @return 542 + * Returns a synthetic status code of 542. + *

+ * Since no actual response was received from the server, this method returns + * a non-standard 5xx status code to indicate a client-side failure. + *

+ * + * @return 542 (a synthetic error status code) */ @Override public int getStatus() { @@ -59,8 +68,9 @@ public int getStatus() { } /** - * a error message of the exception - * @return a 'status' message + * Returns the exception message as the status text. + * + * @return the message from the underlying exception */ @Override public String getStatusText() { @@ -68,7 +78,12 @@ public String getStatusText() { } /** - * @return a empty headers object because none was returned because there was no return + * Returns an empty headers collection. + *

+ * Since no response was received, there are no headers to return. + *

+ * + * @return an empty {@link Headers} object */ @Override public Headers getHeaders() { @@ -76,7 +91,9 @@ public Headers getHeaders() { } /** - * @return null, because there was no response + * Returns {@code null} since no response body was received. + * + * @return {@code null} */ @Override public T getBody() { @@ -84,7 +101,9 @@ public T getBody() { } /** - * @return a parsing exception with the exception. + * Returns a parsing exception containing the original failure reason. + * + * @return an {@link Optional} containing a {@link UnirestParsingException} wrapping the failure */ @Override public Optional getParsingError() { @@ -92,9 +111,15 @@ public Optional getParsingError() { } /** - * @param func a function to transform a body type to something else. - * @param always null - * @return another object + * Applies the mapping function to the body. + *

+ * Since the body is always {@code null} for failed responses, this passes + * {@code null} to the function. + *

+ * + * @param func a function to transform the body + * @param the type to transform the body into + * @return the result of applying the function to {@code null} */ @Override public V mapBody(Function func) { @@ -102,9 +127,15 @@ public V mapBody(Function func) { } /** - * @param func a function to transform a body type to something else. - * @param always null - * @return another response + * Maps this response to a new response type. + *

+ * Since the body is always {@code null} for failed responses, this passes + * {@code null} to the function. + *

+ * + * @param func a function to transform the body + * @param the type to transform the body into + * @return a new HttpResponse with the transformed body type */ @Override public HttpResponse map(Function func) { @@ -112,9 +143,10 @@ public HttpResponse map(Function func) { } /** - * @param consumer a function to consume a successful HttpResponse. - * This is never called in this case. - * @return this HttpResponse. + * Does nothing, as this response is never successful. + * + * @param consumer a function to consume a successful HttpResponse (never invoked) + * @return this HttpResponse */ @Override public HttpResponse ifSuccess(Consumer> consumer) { @@ -122,8 +154,12 @@ public HttpResponse ifSuccess(Consumer> consumer) { } /** - * @param consumer a function to consume a failed HttpResponse - * always called in this case + * Invokes the consumer with this response. + *

+ * Since this is always a failed response, the consumer is always invoked. + *

+ * + * @param consumer a function to consume the failed HttpResponse * @return this HttpResponse */ @Override @@ -133,10 +169,15 @@ public HttpResponse ifFailure(Consumer> consumer) { } /** - * @param errorClass the class to transform the body to. However as the body is null - * in this case it will also be null - * @param consumer a function to consume a failed HttpResponse - * always called in this case + * Invokes the consumer for a failed response. + *

+ * Note: The consumer receives {@code null} since the body cannot be mapped + * to the error class when no response was received. + *

+ * + * @param the error type + * @param errorClass the class to transform the body to (unused since body is null) + * @param consumer a function to consume the failed HttpResponse * @return this HttpResponse */ @Override @@ -146,8 +187,9 @@ public HttpResponse ifFailure(Class errorClass, Consumer the error type - * @return null + * @param errorClass the class for the error (unused) + * @return {@code null} */ @Override public E mapError(Class errorClass) { return null; } + /** + * Returns an empty cookies collection. + *

+ * Since no response was received, there are no cookies to return. + *

+ * + * @return an empty {@link Cookies} collection + */ @Override public Cookies getCookies() { return new Cookies(); } + /** + * Returns {@code null} since there is no request summary available. + * + * @return {@code null} + */ @Override public HttpRequestSummary getRequestSummary() { return null; diff --git a/unirest/src/main/java/kong/unirest/core/GenericType.java b/unirest/src/main/java/kong/unirest/core/GenericType.java index 2a73b39b2..575f2be25 100644 --- a/unirest/src/main/java/kong/unirest/core/GenericType.java +++ b/unirest/src/main/java/kong/unirest/core/GenericType.java @@ -29,30 +29,34 @@ import java.lang.reflect.Type; /** - * Parts of this file were taken from Jackson/core TypeReference under the Apache License: + * A type reference holder that preserves generic type information at runtime. + *

+ * This class works around Java's type erasure by capturing generic type parameters + * through subclassing. Create an anonymous subclass to preserve the type information: + *

{@code
+ * GenericType> ref = new GenericType>() { };
+ * }
+ *

+ * This technique is commonly used in libraries like Jackson, GSON, and Spring + * for object mapping with generic types. + *

+ * Parts of this implementation were derived from Jackson's TypeReference + * under the Apache License 2.0. * - * Apache (Software) License, version 2.0 ("the License"). - * See the License for details about distribution rights, and the - * specific rights regarding derivate works. - * - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * - * A class to hold onto generic type params for object mapping by creating a anonymous subtype. - * This is a common "trick" commonly used in Java to avoid issues with type erasure. - * - * Other examples can be found in popular libraries like Jackson, GSON, and Spring - * - *

- *  GenericType ref = new GenericType<List<Integer>>() { };
- * 
- * @param the generic type you wish to represent. + * @param the generic type to represent + * @see Apache License 2.0 */ public abstract class GenericType implements Comparable> { protected final Type type; + /** + * Creates a new GenericType instance, capturing the generic type information. + *

+ * This constructor must be called from a subclass (typically an anonymous class) + * that provides the actual type argument. + * + * @throws IllegalArgumentException if instantiated without actual type information + */ protected GenericType() { Type superClass = this.getClass().getGenericSuperclass(); if (superClass instanceof Class) { @@ -63,16 +67,32 @@ protected GenericType() { } /** - * @return the Type which includes generic type information + * Returns the captured generic type. + * + * @return the {@link Type} which includes generic type information */ public Type getType() { return this.type; } + /** + * Compares this GenericType with another for ordering. + *

+ * This implementation always returns 0, indicating all GenericType instances + * are considered equal for ordering purposes. + * + * @param o the GenericType to compare with + * @return always returns 0 + */ public int compareTo(GenericType o) { return 0; } + /** + * Returns the runtime class of the type. + * + * @return the {@link Class} object representing the type's runtime class + */ public Class getTypeClass(){ return this.type.getClass(); } diff --git a/unirest/src/main/java/kong/unirest/core/Headers.java b/unirest/src/main/java/kong/unirest/core/Headers.java index e0e398d27..0806210fe 100644 --- a/unirest/src/main/java/kong/unirest/core/Headers.java +++ b/unirest/src/main/java/kong/unirest/core/Headers.java @@ -32,22 +32,37 @@ import static java.util.stream.Collectors.toSet; /** - * Represents a collection of headers + * Represents a collection of HTTP headers. + *

+ * This class provides methods for adding, removing, and querying HTTP headers. + * Headers are stored as name-value pairs, and multiple values can be associated + * with the same header name. Header names are case-insensitive for lookups. + * + * @see Header */ public class Headers { private static final long serialVersionUID = 71310341388734766L; private List

headers = new ArrayList<>(); + /** + * Creates an empty Headers collection. + */ public Headers() { } + /** + * Creates a Headers collection initialized with the specified entries. + * + * @param entries the collection of header entries to add + */ public Headers(Collection entries) { entries.forEach(e -> add(e.name, e.value)); } /** - * Add a header element + * Adds a header element. + * * @param name the name of the header * @param value the value for the header */ @@ -56,9 +71,10 @@ public void add(String name, String value) { } /** - * Add a header element with a supplier which will be evaluated on request + * Adds a header element with a supplier which will be evaluated on request. + * * @param name the name of the header - * @param value the value for the header + * @param value a supplier providing the value for the header */ public void add(String name, Supplier value) { if (Objects.nonNull(name)) { @@ -67,7 +83,11 @@ public void add(String name, Supplier value) { } /** - * Replace a header value. If there are multiple instances it will overwrite all of them + * Replaces a header value. + *

+ * If there are multiple instances of the header, all of them will be removed + * and replaced with the new value. + * * @param name the name of the header * @param value the value for the header */ @@ -77,25 +97,28 @@ public void replace(String name, String value) { } /** - * Remove a header by name - * @param name the name of the header + * Removes all headers with the specified name. + * + * @param name the name of the header to remove */ public void remove(String name) { headers.removeIf(h -> isName(h, name)); } /** - * Get the number of header keys. - * @return the size of the header keys + * Returns the number of unique header names. + * + * @return the count of distinct header names */ public int size() { return headers.stream().map(Header::getName).collect(toSet()).size(); } /** - * Get all the values for a header name - * @param name name of the header element - * @return a list of values + * Returns all values for a header name. + * + * @param name the name of the header + * @return a list of values for the specified header name */ public List get(String name) { return headers.stream() @@ -105,8 +128,9 @@ public List get(String name) { } /** - * Add a bunch of headers at once - * @param other a header + * Adds all headers from another Headers collection. + * + * @param other the headers to add */ public void putAll(Headers other) { if(other != null) { @@ -115,25 +139,27 @@ public void putAll(Headers other) { } /** - * Check if a header is present - * @param name a header - * @return if the headers contain this name. + * Checks if a header with the specified name exists. + * + * @param name the header name to check + * @return {@code true} if a header with the name exists, {@code false} otherwise */ public boolean containsKey(String name) { return this.headers.stream().anyMatch(h -> isName(h, name)); } /** - * Clear the headers! + * Removes all headers from this collection. */ public void clear() { this.headers.clear(); } /** - * Get the first header value for a name + * Returns the first value for a header name. + * * @param key the name of the header - * @return the first value + * @return the first value, or an empty string if the header is not present */ public String getFirst(String key) { return headers @@ -145,24 +171,27 @@ public String getFirst(String key) { } /** - * Get all of the headers - * @return all the headers, in order + * Returns all headers in this collection. + * + * @return a new list containing all headers in order */ public List

all() { return new ArrayList<>(this.headers); } /** - * Add a Cookie header - * @param cookie the cookie + * Adds a Cookie header. + * + * @param cookie the cookie to add */ public void cookie(Cookie cookie) { headers.add(new Entry("cookie", cookie.toString())); } /** - * sets a collection of cookies - * @param cookies some cookies + * Adds multiple Cookie headers. + * + * @param cookies the cookies to add */ public void cookie(Collection cookies) { cookies.forEach(this::cookie); @@ -180,9 +209,9 @@ void remove(String key, String value) { } /** - * @return list all headers like this:
Content-Length: 42
-     * Cache-Control: no-cache
-     * ...
+ * Returns a string representation of all headers. + * + * @return headers formatted as {@code Name: Value}, one per line */ @Override public String toString() { @@ -205,10 +234,13 @@ public int hashCode() { } /** - * creates a basic auth header from a username and password. - * The creds will be the Base64 encoded value of {username}:{password} along with the 'Basic' schema - * For example given a creds of "username" and "password" you would get - * eg: Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ= + * Sets a Basic Authentication header. + *

+ * Creates an Authorization header with the Base64 encoded value of + * {@code username:password} prefixed with "Basic". + * For example, given "username" and "password", produces: + * {@code Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=} + * * @param username the username * @param password the password */ @@ -217,16 +249,18 @@ public void setBasicAuth(String username, String password) { } /** - * sets the Accept header - * @param value the value for accept + * Sets the Accept header. + * + * @param value the accept header value (e.g., "application/json") */ public void accepts(String value) { add(HeaderNames.ACCEPT, value); } /** - * Sets headers based on a map of key-value pairs - * @param headerMap I'm the MAP + * Adds headers from a map of key-value pairs. + * + * @param headerMap a map of header names to values */ public void add(Map headerMap) { if (headerMap != null) { @@ -237,8 +271,11 @@ public void add(Map headerMap) { } /** - * Replace all headers from a given map. - * @param headerMap the map of headers + * Replaces headers from a map of key-value pairs. + *

+ * Each header in the map will replace any existing header with the same name. + * + * @param headerMap a map of header names to values */ public void replace(Map headerMap) { if (headerMap != null) { @@ -246,16 +283,31 @@ public void replace(Map headerMap) { } } + /** + * A header entry implementation that supports supplier-based values. + */ static class Entry implements Header { private final String name; private final Supplier value; + /** + * Creates a header entry with a fixed value. + * + * @param name the header name + * @param value the header value + */ public Entry(String name, String value) { this.name = name; this.value = () -> value; } + /** + * Creates a header entry with a supplier-based value. + * + * @param name the header name + * @param value a supplier that provides the header value + */ public Entry(String name, Supplier value) { this.name = name; this.value = value == null ? () -> null : value; diff --git a/unirest/src/main/java/kong/unirest/core/HttpMethod.java b/unirest/src/main/java/kong/unirest/core/HttpMethod.java index 4e789c3fc..2df78a144 100644 --- a/unirest/src/main/java/kong/unirest/core/HttpMethod.java +++ b/unirest/src/main/java/kong/unirest/core/HttpMethod.java @@ -30,17 +30,44 @@ import java.util.Map; import java.util.Set; +/** + * Represents an HTTP request method. + *

+ * This class provides constants for standard HTTP methods (GET, POST, PUT, DELETE, etc.) + * and supports custom method names through the {@link #valueOf(String)} factory method. + * Method instances are cached and reused, ensuring that the same method name always + * returns the same instance. + * + * @see HTTP request methods (MDN) + */ public class HttpMethod { private static final Map REGISTRY = new HashMap<>(); + /** The HTTP GET method requests a representation of the specified resource. */ public static final HttpMethod GET = valueOf("GET"); + + /** The HTTP POST method sends data to the server to create a resource. */ public static final HttpMethod POST = valueOf("POST"); + + /** The HTTP PUT method replaces all current representations of the target resource. */ public static final HttpMethod PUT = valueOf("PUT"); + + /** The HTTP DELETE method deletes the specified resource. */ public static final HttpMethod DELETE = valueOf("DELETE"); + + /** The HTTP PATCH method applies partial modifications to a resource. */ public static final HttpMethod PATCH = valueOf("PATCH"); + + /** The HTTP HEAD method requests headers identical to GET, but without the response body. */ public static final HttpMethod HEAD = valueOf("HEAD"); + + /** The HTTP OPTIONS method describes the communication options for the target resource. */ public static final HttpMethod OPTIONS = valueOf("OPTIONS"); + + /** The HTTP TRACE method performs a message loop-back test along the path to the target resource. */ public static final HttpMethod TRACE = valueOf("TRACE"); + + /** Represents a WebSocket connection upgrade request. */ public static final HttpMethod WEBSOCKET = valueOf("WEBSOCKET"); private final String name; @@ -49,18 +76,43 @@ private HttpMethod(String name){ this.name = name; } + /** + * Returns an HttpMethod instance for the specified method name. + *

+ * Method names are case-insensitive and will be converted to uppercase. + * Instances are cached, so calling this method multiple times with the + * same name returns the same instance. + * + * @param verb the HTTP method name (e.g., "GET", "POST", "CUSTOM") + * @return the HttpMethod instance for the specified name + */ public static HttpMethod valueOf(String verb){ return REGISTRY.computeIfAbsent(String.valueOf(verb).toUpperCase(), HttpMethod::new); } + /** + * Returns a set of all registered HTTP methods. + * + * @return a new {@link Set} containing all HttpMethod instances that have been created + */ public Set all(){ return new HashSet<>(REGISTRY.values()); } + /** + * Returns the name of this HTTP method. + * + * @return the method name in uppercase (e.g., "GET", "POST") + */ public String name() { return name; } + /** + * Returns the string representation of this HTTP method. + * + * @return the method name + */ @Override public String toString() { return name; diff --git a/unirest/src/main/java/kong/unirest/core/HttpRequest.java b/unirest/src/main/java/kong/unirest/core/HttpRequest.java index 4f8914252..0492aca2b 100644 --- a/unirest/src/main/java/kong/unirest/core/HttpRequest.java +++ b/unirest/src/main/java/kong/unirest/core/HttpRequest.java @@ -40,6 +40,8 @@ /** The primary request builder used to create a request. This will be completed after calling one of the "as**" methods like asString() + + @param a interface that extends this interface which supports the builder pattern */ public interface HttpRequest { /** diff --git a/unirest/src/main/java/kong/unirest/core/HttpResponse.java b/unirest/src/main/java/kong/unirest/core/HttpResponse.java index 806ff29f7..2aca61ace 100644 --- a/unirest/src/main/java/kong/unirest/core/HttpResponse.java +++ b/unirest/src/main/java/kong/unirest/core/HttpResponse.java @@ -30,102 +30,141 @@ import java.util.function.Function; /** - * @param a Http Response holding a specific type of body. + * Represents an HTTP response with a typed body. + *

+ * This interface provides access to the response status, headers, body, and cookies. + * It also supports functional-style operations for mapping and conditional processing. + * + * @param the type of the response body */ public interface HttpResponse { /** - * @return the HTTP status code. + * Returns the HTTP status code of the response. + * + * @return the HTTP status code (e.g., 200, 404, 500) */ int getStatus(); /** - * @return status text + * Returns the HTTP status text of the response. + * + * @return the HTTP status text (e.g., "OK", "Not Found") */ String getStatusText(); /** - * @return Response Headers (map) with same case as server response. - * For instance use getHeaders().getFirst("Location") and not getHeaders().getFirst("location") to get first header "Location" + * Returns the response headers. + *

+ * Headers are returned with the same case as the server response. + * For instance, use {@code getHeaders().getFirst("Location")} and not + * {@code getHeaders().getFirst("location")} to get the first "Location" header. + * + * @return the response {@link Headers} */ Headers getHeaders(); /** - * @return the body + * Returns the response body. + * + * @return the body of type {@code T} */ T getBody(); /** - * If the transformation to the body failed by an exception it will be kept here - * @return a possible RuntimeException. Checked exceptions are wrapped in a UnirestException + * Returns any exception that occurred during body transformation. + *

+ * If the transformation to the body failed, the exception is captured here. + * Checked exceptions are wrapped in a {@link UnirestParsingException}. + * + * @return an {@link Optional} containing the parsing exception, or empty if parsing succeeded */ Optional getParsingError(); /** - * Map the body into another type - * @param func a function to transform a body type to something else. - * @param The return type of the function - * @return the return type + * Maps the body into another type. + * + * @param func a function to transform the body to another type + * @param the type to transform the body into + * @return the result of applying the function to the body */ V mapBody(Function func); /** - * Map the Response into another response with a different body - * @param func a function to transform a body type to something else. - * @param The return type of the function - * @return the return type + * Maps this response into another response with a different body type. + * + * @param func a function to transform the body to another type + * @param the type to transform the body into + * @return a new {@link HttpResponse} with the transformed body */ HttpResponse map(Function func); /** - * If the response was a 200-series response. Invoke this consumer - * can be chained with ifFailure - * @param consumer a function to consume a HttpResponse - * @return the same response + * Invokes the consumer if the response was successful (2xx status code). + *

+ * Can be chained with {@link #ifFailure(Consumer)}. + * + * @param consumer a consumer to process the successful response + * @return this response for method chaining */ HttpResponse ifSuccess(Consumer> consumer); /** - * If the response was NOT a 200-series response or a mapping exception happened. Invoke this consumer - * can be chained with ifSuccess - * @param consumer a function to consume a HttpResponse - * @return the same response + * Invokes the consumer if the response was not successful or a mapping exception occurred. + *

+ * Can be chained with {@link #ifSuccess(Consumer)}. + * + * @param consumer a consumer to process the failed response + * @return this response for method chaining */ HttpResponse ifFailure(Consumer> consumer); /** - * If the response was NOT a 200-series response or a mapping exception happened. map the original body into a error type and invoke this consumer - * can be chained with ifSuccess - * @param the type of error class to map the body - * @param errorClass the class of the error type to map to - * @param consumer a function to consume a HttpResponse - * @return the same response + * Invokes the consumer if the response was not successful or a mapping exception occurred, + * mapping the body to the specified error type. + *

+ * Can be chained with {@link #ifSuccess(Consumer)}. + * + * @param the error type to map the body to + * @param errorClass the class of the error type + * @param consumer a consumer to process the failed response with the mapped error body + * @return this response for method chaining */ HttpResponse ifFailure(Class errorClass, Consumer> consumer); - /** - * @return true if the response was a 200-series response and no mapping exception happened, else false + /** + * Indicates whether this response represents a successful request. + * + * @return {@code true} if the response has a 2xx status code and no mapping exception occurred, + * {@code false} otherwise */ boolean isSuccess(); /** - * Map the body into a error class if the response was NOT a 200-series response or a mapping exception happened. - * Uses the system Object Mapper - * @param the response type - * @param errorClass the class for the error - * @return the error object + * Maps the body into an error object if the response was not successful. + *

+ * Uses the configured {@link ObjectMapper} for deserialization. + * + * @param the error type + * @param errorClass the class to map the error body to + * @return the mapped error object, or {@code null} if the response was successful */ E mapError(Class errorClass); /** - * return a cookie collection parse from the set-cookie header - * @return a Cookies collection + * Returns the cookies from the response. + *

+ * Cookies are parsed from the {@code Set-Cookie} header. + * + * @return a {@link Cookies} collection containing the response cookies */ Cookies getCookies(); /** - * @return a Summary of the HttpRequest that created this response + * Returns a summary of the request that created this response. + * + * @return the {@link HttpRequestSummary} for the originating request */ HttpRequestSummary getRequestSummary(); } diff --git a/unirest/src/main/java/kong/unirest/core/HttpStatus.java b/unirest/src/main/java/kong/unirest/core/HttpStatus.java index b829be03b..29d74023f 100644 --- a/unirest/src/main/java/kong/unirest/core/HttpStatus.java +++ b/unirest/src/main/java/kong/unirest/core/HttpStatus.java @@ -25,64 +25,204 @@ package kong.unirest.core; +/** + * Constants for standard HTTP status codes as defined in RFC 7231, RFC 6585, and other relevant specifications. + *

+ * HTTP status codes are divided into five classes: + *

    + *
  • 2xx Success - The request was successfully received, understood, and accepted
  • + *
  • 3xx Redirection - Further action needs to be taken to complete the request
  • + *
  • 4xx Client Error - The request contains bad syntax or cannot be fulfilled
  • + *
  • 5xx Server Error - The server failed to fulfill a valid request
  • + *
+ * + * @see RFC 7231 - HTTP/1.1 Semantics and Content + * @see RFC 6585 - Additional HTTP Status Codes + */ public final class HttpStatus { + + // 2xx Success + + /** {@code 200 OK} - The request has succeeded. */ public static final int OK = 200; + + /** {@code 201 Created} - The request has been fulfilled and resulted in a new resource being created. */ public static final int CREATED = 201; + + /** {@code 202 Accepted} - The request has been accepted for processing, but processing has not been completed. */ public static final int ACCEPTED = 202; + + /** {@code 203 Non-Authoritative Information} - The returned metadata is not exactly the same as available from the origin server. */ public static final int NON_AUTHORITATIVE_INFORMATION = 203; + + /** {@code 204 No Content} - The server has fulfilled the request but does not need to return a response body. */ public static final int NO_CONTENT = 204; + + /** {@code 205 Reset Content} - The server has fulfilled the request and the user agent should reset the document view. */ public static final int RESET_CONTENT = 205; + + /** {@code 206 Partial Content} - The server has fulfilled the partial GET request for the resource. */ public static final int PARTIAL_CONTENT = 206; + + /** {@code 207 Multi-Status} - Provides status for multiple independent operations (WebDAV). */ public static final int MULTI_STATUS = 207; + + /** {@code 208 Already Reported} - Used inside a DAV:propstat response element to avoid enumerating members repeatedly (WebDAV). */ public static final int ALREADY_REPORTED = 208; + + /** {@code 226 IM Used} - The server has fulfilled a GET request for the resource with instance-manipulations applied. */ public static final int IM_USED = 226; + + // 3xx Redirection + + /** {@code 300 Multiple Choices} - The requested resource corresponds to multiple representations. */ public static final int MULTIPLE_CHOICE = 300; + + /** {@code 301 Moved Permanently} - The requested resource has been assigned a new permanent URI. */ public static final int MOVED_PERMANENTLY = 301; + + /** {@code 302 Found} - The requested resource resides temporarily under a different URI. */ public static final int FOUND = 302; + + /** {@code 303 See Other} - The response to the request can be found under a different URI using GET. */ public static final int SEE_OTHER = 303; + + /** {@code 304 Not Modified} - The resource has not been modified since the version specified in the request headers. */ public static final int NOT_MODIFIED = 304; + + /** {@code 305 Use Proxy} - The requested resource must be accessed through the proxy given by the Location field. */ public static final int USE_PROXY = 305; + + /** {@code 306 Unused} - This status code is no longer used but is reserved. */ public static final int UNUSED = 306; + + /** {@code 307 Temporary Redirect} - The requested resource resides temporarily under a different URI. */ public static final int TEMPORARY_REDIRECT = 307; + + /** {@code 308 Permanent Redirect} - The requested resource has been assigned a new permanent URI. */ public static final int PERMANENT_REDIRECT = 308; + + // 4xx Client Error + + /** {@code 400 Bad Request} - The request could not be understood by the server due to malformed syntax. */ public static final int BAD_REQUEST = 400; + + /** {@code 401 Unauthorized} - The request requires user authentication. */ public static final int UNAUTHORIZED = 401; + + /** {@code 402 Payment Required} - Reserved for future use. */ public static final int PAYMENT_REQUIRED = 402; + + /** {@code 403 Forbidden} - The server understood the request but refuses to authorize it. */ public static final int FORBIDDEN = 403; + + /** {@code 404 Not Found} - The server has not found anything matching the Request-URI. */ public static final int NOT_FOUND = 404; + + /** {@code 405 Method Not Allowed} - The method specified in the request is not allowed for the resource. */ public static final int METHOD_NOT_ALLOWED = 405; + + /** {@code 406 Not Acceptable} - The resource is not available in a format acceptable according to the Accept headers. */ public static final int NOT_ACCEPTABLE = 406; + + /** {@code 407 Proxy Authentication Required} - The client must first authenticate itself with the proxy. */ public static final int PROXY_AUTHENTICATION_REQUIRED = 407; + + /** {@code 408 Request Timeout} - The client did not produce a request within the time the server was prepared to wait. */ public static final int REQUEST_TIMEOUT = 408; + + /** {@code 409 Conflict} - The request could not be completed due to a conflict with the current state of the resource. */ public static final int CONFLICT = 409; + + /** {@code 410 Gone} - The requested resource is no longer available and no forwarding address is known. */ public static final int GONE = 410; + + /** {@code 411 Length Required} - The server refuses to accept the request without a defined Content-Length. */ public static final int LENGTH_REQUIRED = 411; + + /** {@code 412 Precondition Failed} - A precondition given in the request header evaluated to false. */ public static final int PRECONDITION_FAILED = 412; + + /** {@code 413 Payload Too Large} - The server refuses to process a request because the payload is too large. */ public static final int PAYLOAD_TOO_LARGE = 413; + + /** {@code 414 URI Too Long} - The server refuses to service the request because the Request-URI is too long. */ public static final int URI_TOO_LONG = 414; + + /** {@code 415 Unsupported Media Type} - The server refuses to service the request because the payload format is unsupported. */ public static final int UNSUPPORTED_MEDIA_TYPE = 415; + + /** {@code 416 Range Not Satisfiable} - None of the ranges in the request's Range header field overlap the resource. */ public static final int RANGE_NOT_SATISFIABLE = 416; + + /** {@code 417 Expectation Failed} - The expectation given in the Expect header could not be met by the server. */ public static final int EXPECTATION_FAILED = 417; + + /** {@code 418 I'm a teapot} - The server refuses to brew coffee because it is, permanently, a teapot (RFC 2324). */ public static final int IM_A_TEAPOT = 418; + + /** {@code 421 Misdirected Request} - The request was directed at a server that is not able to produce a response. */ public static final int MISDIRECTED_REQUEST = 421; + + /** {@code 422 Unprocessable Entity} - The server understands the content type but was unable to process the instructions (WebDAV). */ public static final int UNPROCESSABLE_ENTITY = 422; + + /** {@code 423 Locked} - The resource that is being accessed is locked (WebDAV). */ public static final int LOCKED = 423; + + /** {@code 424 Failed Dependency} - The request failed due to failure of a previous request (WebDAV). */ public static final int FAILED_DEPENDENCY = 424; + + /** {@code 425 Too Early} - The server is unwilling to risk processing a request that might be replayed. */ public static final int TOO_EARLY = 425; + + /** {@code 426 Upgrade Required} - The server refuses to perform the request using the current protocol. */ public static final int UPGRADE_REQUIRED = 426; + + /** {@code 428 Precondition Required} - The origin server requires the request to be conditional. */ public static final int PRECONDITION_REQUIRED = 428; + + /** {@code 429 Too Many Requests} - The user has sent too many requests in a given amount of time (rate limiting). */ public static final int TOO_MANY_REQUESTS = 429; + + /** {@code 431 Request Header Fields Too Large} - The server refuses to process the request because the header fields are too large. */ public static final int REQUEST_HEADER_FIELDS_TOO_LARGE = 431; + + /** {@code 451 Unavailable For Legal Reasons} - The resource is unavailable due to legal demands. */ public static final int UNAVAILABLE_FOR_LEGAL_REASONS = 451; + + // 5xx Server Error + + /** {@code 500 Internal Server Error} - The server encountered an unexpected condition that prevented it from fulfilling the request. */ public static final int INTERNAL_SERVER_ERROR = 500; + + /** {@code 501 Not Implemented} - The server does not support the functionality required to fulfill the request. */ public static final int NOT_IMPLEMENTED = 501; + + /** {@code 502 Bad Gateway} - The server, while acting as a gateway, received an invalid response from the upstream server. */ public static final int BAD_GATEWAY = 502; + + /** {@code 503 Service Unavailable} - The server is currently unable to handle the request due to temporary overloading or maintenance. */ public static final int SERVICE_UNAVAILABLE = 503; + + /** {@code 504 Gateway Timeout} - The server, while acting as a gateway, did not receive a timely response from the upstream server. */ public static final int GATEWAY_TIMEOUT = 504; + + /** {@code 505 HTTP Version Not Supported} - The server does not support the HTTP protocol version used in the request. */ public static final int VERSION_NOT_SUPPORTED = 505; + + /** {@code 506 Variant Also Negotiates} - The server has an internal configuration error in transparent content negotiation. */ public static final int VARIANT_ALSO_NEGOTIATES = 506; + + /** {@code 507 Insufficient Storage} - The server is unable to store the representation needed to complete the request (WebDAV). */ public static final int INSUFFICIENT_STORAGE = 507; + + /** {@code 508 Loop Detected} - The server detected an infinite loop while processing the request (WebDAV). */ public static final int LOOP_DETECTED = 508; + + /** {@code 510 Not Extended} - Further extensions to the request are required for the server to fulfill it. */ public static final int NOT_EXTENDED = 510; + + /** {@code 511 Network Authentication Required} - The client needs to authenticate to gain network access. */ public static final int NETWORK_AUTHENTICATION_REQUIRED = 511; } diff --git a/unirest/src/main/java/kong/unirest/core/json/JsonEngine.java b/unirest/src/main/java/kong/unirest/core/json/JsonEngine.java index 3b87f2cd0..b4fec5d3a 100644 --- a/unirest/src/main/java/kong/unirest/core/json/JsonEngine.java +++ b/unirest/src/main/java/kong/unirest/core/json/JsonEngine.java @@ -33,76 +33,501 @@ import java.util.Collection; import java.util.Set; +/** + * Service provider interface for JSON processing engines. + *

+ * This interface defines the contract that JSON libraries (such as GSON or Jackson) + * must implement to be used as Unirest's JSON processing engine. It provides methods + * for serialization, deserialization, and manipulation of JSON structures. + *

+ * Implementations are discovered via Java's ServiceLoader mechanism or can be + * configured explicitly. + * + * @see kong.unirest.core.json.CoreFactory + */ public interface JsonEngine { + + /** + * Converts a JSON element to a pretty-printed JSON string. + * + * @param obj the JSON element to convert + * @return a formatted JSON string with indentation + */ String toPrettyJson(Element obj); + + /** + * Converts a JSON element to a compact JSON string. + * + * @param obj the JSON element to convert + * @return a compact JSON string without extra whitespace + */ String toJson(Element obj); + + /** + * Writes a JSON element to a Writer as a compact JSON string. + * + * @param obj the JSON element to write + * @param sw the writer to write to + */ void toJson(Element obj, Writer sw); + + /** + * Writes a JSON element to a Writer as a pretty-printed JSON string. + * + * @param engineElement the JSON element to write + * @param sw the writer to write to + */ void toPrettyJson(Element engineElement, Writer sw); + + /** + * Converts a Java object to a JSON tree structure. + * + * @param obj the Java object to convert + * @return the JSON element representation of the object + */ Element toJsonTree(java.lang.Object obj); + + /** + * Creates a new empty JSON object. + * + * @return a new empty JSON object + */ Object newEngineObject(); + + /** + * Parses a JSON string into a JSON object. + * + * @param string the JSON string to parse + * @return the parsed JSON object + * @throws JSONException if the string is not valid JSON + */ Object newEngineObject(String string) throws JSONException; + + /** + * Parses a JSON string into a JSON array. + * + * @param jsonString the JSON string to parse + * @return the parsed JSON array + * @throws JSONException if the string is not a valid JSON array + */ Array newJsonArray(String jsonString) throws JSONException; + + /** + * Creates a new JSON array from a Java collection. + * + * @param collection the collection to convert + * @return a JSON array containing the collection elements + */ Array newJsonArray(Collection collection); + + /** + * Creates a new empty JSON array. + * + * @return a new empty JSON array + */ Array newEngineArray(); + + /** + * Deserializes a JSON element to a Java object of the specified type. + * + * @param the target type + * @param obj the JSON element to deserialize + * @param mapClass the class of the target type + * @return the deserialized Java object + */ T fromJson(Element obj, Class mapClass); + + /** + * Creates a new JSON primitive from an enum value. + * + * @param the enum type + * @param enumValue the enum value + * @return a JSON primitive representing the enum + */ Primitive newJsonPrimitive(T enumValue); + + /** + * Creates a new JSON primitive from a string value. + * + * @param string the string value + * @return a JSON primitive representing the string + */ Primitive newJsonPrimitive(String string); + + /** + * Creates a new JSON primitive from a numeric value. + * + * @param number the numeric value + * @return a JSON primitive representing the number + */ Primitive newJsonPrimitive(Number number); + + /** + * Creates a new JSON primitive from a boolean value. + * + * @param bool the boolean value + * @return a JSON primitive representing the boolean + */ Primitive newJsonPrimitive(Boolean bool); + + /** + * Returns the ObjectMapper associated with this JSON engine. + * + * @return the ObjectMapper for object serialization/deserialization + */ ObjectMapper getObjectMapper(); + + /** + * Quotes and escapes a value for use in a JSON string. + * + * @param s the value to quote + * @return the quoted and escaped string + */ String quote(java.lang.Object s); + /** + * Base interface for all JSON element types. + *

+ * Provides common methods for type checking and value extraction + * from JSON elements. + */ interface Element { + + /** + * Returns this element as a JSON object. + * + * @return the underlying JSON object + */ Object getAsJsonObject(); + + /** + * Checks if this element represents a JSON null value. + * + * @return {@code true} if this is a JSON null + */ boolean isJsonNull(); + + /** + * Returns this element as a JSON primitive. + * + * @return the JSON primitive + */ Primitive getAsJsonPrimitive(); + + /** + * Returns this element as a JSON array. + * + * @return the JSON array + */ Array getAsJsonArray(); + + /** + * Returns this element's value as a float. + * + * @return the float value + */ float getAsFloat(); + + /** + * Returns this element's value as a double. + * + * @return the double value + */ double getAsDouble(); + + /** + * Returns this element's value as a string. + * + * @return the string value + */ String getAsString(); + + /** + * Returns this element's value as a long. + * + * @return the long value + */ long getAsLong(); + + /** + * Returns this element's value as an int. + * + * @return the int value + */ int getAsInt(); + + /** + * Returns this element's value as a boolean. + * + * @return the boolean value + */ boolean getAsBoolean(); + + /** + * Returns this element's value as a BigInteger. + * + * @return the BigInteger value + */ BigInteger getAsBigInteger(); + + /** + * Returns this element's value as a BigDecimal. + * + * @return the BigDecimal value + */ BigDecimal getAsBigDecimal(); + + /** + * Returns this element as a JSON primitive. + * + * @return the JSON primitive + */ Primitive getAsPrimitive(); + + /** + * Checks if this element is a JSON array. + * + * @return {@code true} if this is a JSON array + */ boolean isJsonArray(); + + /** + * Checks if this element is a JSON primitive. + * + * @return {@code true} if this is a JSON primitive + */ boolean isJsonPrimitive(); + + /** + * Checks if this element is a JSON object. + * + * @return {@code true} if this is a JSON object + */ boolean isJsonObject(); + + /** + * Returns the underlying engine-specific element. + * + * @param the type of the engine element + * @return the underlying engine element + */ T getEngineElement(); } + /** + * Represents a JSON array. + *

+ * Provides methods for accessing, modifying, and iterating over + * array elements. + */ interface Array extends Element { + + /** + * Returns the number of elements in this array. + * + * @return the size of the array + */ int size(); + + /** + * Returns the element at the specified index. + * + * @param index the index of the element + * @return the element at the index + */ Element get(int index); + + /** + * Removes and returns the element at the specified index. + * + * @param index the index of the element to remove + * @return the removed element + */ Element remove(int index); + + /** + * Sets a numeric value at the specified index. + * + * @param index the index + * @param number the numeric value + * @return this array for chaining + */ Element put(int index, Number number); + + /** + * Sets a string value at the specified index. + * + * @param index the index + * @param number the string value + * @return this array for chaining + */ Element put(int index, String number); + + /** + * Sets a boolean value at the specified index. + * + * @param index the index + * @param number the boolean value + * @return this array for chaining + */ Element put(int index, Boolean number); + + /** + * Adds an element to the end of this array. + * + * @param obj the element to add + */ void add(Element obj); + + /** + * Sets an element at the specified index. + * + * @param index the index + * @param o the element to set + */ void set(int index, Element o); + + /** + * Adds a numeric value to the end of this array. + * + * @param num the numeric value to add + */ void add(Number num); + + /** + * Adds a string value to the end of this array. + * + * @param str the string value to add + */ void add(String str); + + /** + * Adds a boolean value to the end of this array. + * + * @param bool the boolean value to add + */ void add(Boolean bool); + + /** + * Joins array elements into a string with the specified separator. + * + * @param token the separator token + * @return the joined string + */ String join(String token); } + /** + * Represents a JSON object (key-value map). + *

+ * Provides methods for accessing and modifying object properties. + */ interface Object extends Element { + + /** + * Returns the number of properties in this object. + * + * @return the number of properties + */ int size(); + + /** + * Checks if this object has a property with the specified key. + * + * @param key the property key + * @return {@code true} if the property exists + */ boolean has(String key); + + /** + * Returns the value associated with the specified key. + * + * @param key the property key + * @return the element value, or {@code null} if not found + */ Element get(String key); + + /** + * Adds a JSON element as a property. + * + * @param key the property key + * @param value the element value + */ void add(String key, Element value); + + /** + * Adds a boolean property. + * + * @param key the property key + * @param value the boolean value + */ void addProperty(String key, Boolean value); + + /** + * Adds a string property. + * + * @param key the property key + * @param value the string value + */ void addProperty(String key, String value); + + /** + * Adds a numeric property. + * + * @param key the property key + * @param value the numeric value + */ void addProperty(String key, Number value); + + /** + * Adds an element property. + * + * @param key the property key + * @param value the element value + */ void addProperty(String key, Element value); + + /** + * Removes the property with the specified key. + * + * @param key the property key to remove + */ void remove(String key); + + /** + * Adds an enum value as a property. + * + * @param the enum type + * @param key the property key + * @param enumValue the enum value + */ void add(String key, E enumValue); + + /** + * Returns the set of all property keys in this object. + * + * @return the set of keys + */ Set keySet(); } + /** + * Represents a JSON primitive value (string, number, or boolean). + */ interface Primitive extends Element { + + /** + * Checks if this primitive is a boolean value. + * + * @return {@code true} if this is a boolean + */ boolean isBoolean(); + + /** + * Checks if this primitive is a numeric value. + * + * @return {@code true} if this is a number + */ boolean isNumber(); } }